From b0f45112f1c1d270d8b92e39ddc7ff5b88c5cdfd Mon Sep 17 00:00:00 2001 From: "Eric Sibly [chullybun]" Date: Mon, 21 Sep 2020 16:06:40 -0700 Subject: [PATCH] New code gen foundations (#72) * Initial refactor and introduction of handlebars. * Model generation complete. * Entity/model gen converted. * Ported IEntityManager, EntityManager, IEntityDataSvc and IEntityData. * Ported DataSvc code-gen. * Ported Data layer. * Code tidy-up/consistency. * Ported EntityController. * A couple of minor fixes for "all" code-gen. * Ported EntityAgent. * Ported Business-layer ServiceCollectionExtensions. * Ported ReferenceData code-gen (except data layer). * Ported RefData Data code-gen. * Ported partial gRPC code-gen. * Completed gRPC code-gen port. * Re codegen for CDR. * Start laying foundation for database port to handlebars. * Create new My.Hr sample to document and test end-to-end usage. * Re-gen all other samples and re-test all. * Upgrade the template solution and ensure functioning as expected. Refine My.Hr docs. Correct any further code-gen defects found. * Implement My.Hr PerformanceReview example. Introduced new ValidationTester for mocked unit testing. Updated example tests and solution template accordingly. * Tools updated to return correct status code. Tweaked database tool to support new args, * Handle a NotFoundException for an HTTP DELETE and return NoContent. All Data updates "have" to pre-query to enable proper mapping. ProperyMapping null source value fixes. * Doco update and end-to-end test. * Grpc Delete NotFound to be no content. Further tweak doco. Finally done; release it! --- Beef.sln | 67 +- README.md | 8 +- .../Generated/AccountController.cs | 30 +- .../Generated/ReferenceDataController.cs | 34 +- .../Generated/TransactionController.cs | 16 +- .../Data/Generated/AccountData.cs | 33 +- .../Data/Generated/AccountDetailData.cs | 12 +- .../Data/Generated/IAccountData.cs | 10 +- .../Data/Generated/IReferenceDataData.cs | 26 +- .../Data/Generated/ITransactionData.cs | 6 +- .../Data/Generated/ReferenceDataData.cs | 66 +- .../Data/Generated/TransactionData.cs | 23 +- .../Data/Model/Generated/Account.cs | 14 +- .../Data/Model/Generated/AccountUType.cs | 35 + .../Data/Model/Generated/Balance.cs | 8 +- .../Data/Model/Generated/BalancePurse.cs | 8 +- .../Data/Model/Generated/CreditCardAccount.cs | 6 +- .../Model/Generated/MaturityInstructions.cs | 35 + .../Data/Model/Generated/OpenStatus.cs | 35 + .../Data/Model/Generated/ProductCategory.cs | 35 + .../Model/Generated/TermDepositAccount.cs | 6 +- .../Data/Model/Generated/Transaction.cs | 8 +- .../Data/Model/Generated/TransactionStatus.cs | 35 + .../Data/Model/Generated/TransactionType.cs | 35 + .../DataSvc/Generated/AccountDataSvc.cs | 29 +- .../DataSvc/Generated/IAccountDataSvc.cs | 10 +- .../Generated/IReferenceDataDataSvc.cs | 5 +- .../DataSvc/Generated/ITransactionDataSvc.cs | 6 +- .../DataSvc/Generated/ReferenceDataDataSvc.cs | 13 +- .../DataSvc/Generated/TransactionDataSvc.cs | 13 +- .../Generated/AccountManager.cs | 35 +- .../Generated/IAccountManager.cs | 10 +- .../Generated/ITransactionManager.cs | 6 +- .../Generated/ReferenceDataProvider.cs | 10 +- .../Generated/TransactionManager.cs | 16 +- .../Cdr.Banking.CodeGen/Cdr.RefData.xml | 12 +- .../Agents/Generated/AccountAgent.cs | 32 +- .../Agents/Generated/ReferenceDataAgent.cs | 26 +- .../Generated/ReferenceDataAgentProvider.cs | 19 +- .../Agents/Generated/TransactionAgent.cs | 16 +- .../Entities/Generated/Account.cs | 124 +- .../Entities/Generated/AccountArgs.cs | 48 +- .../Entities/Generated/AccountDetail.cs | 68 +- .../Entities/Generated/AccountUType.cs | 38 +- .../Entities/Generated/Balance.cs | 70 +- .../Entities/Generated/BalancePurse.cs | 66 +- .../Entities/Generated/CreditCardAccount.cs | 55 +- .../Entities/Generated/IReferenceData.cs | 2 +- .../Generated/MaturityInstructions.cs | 38 +- .../Entities/Generated/OpenStatus.cs | 38 +- .../Entities/Generated/ProductCategory.cs | 38 +- .../Entities/Generated/ReferenceData.cs | 2 +- .../Entities/Generated/TermDepositAccount.cs | 58 +- .../Entities/Generated/Transaction.cs | 153 +- .../Entities/Generated/TransactionArgs.cs | 60 +- .../Entities/Generated/TransactionStatus.cs | 38 +- .../Entities/Generated/TransactionType.cs | 38 +- .../Controllers/Generated/ConfigController.cs | 11 +- .../Generated/ContactController.cs | 52 +- .../Controllers/Generated/GenderController.cs | 38 +- .../Controllers/Generated/PersonController.cs | 214 +-- .../Generated/ProductController.cs | 26 +- .../Generated/ReferenceDataController.cs | 34 +- .../Controllers/Generated/RobotController.cs | 66 +- .../Generated/TripPersonController.cs | 44 +- .../Grpc/Generated/RobotService.cs | 56 +- samples/Demo/Beef.Demo.Business/Data/EfDb.cs | 2 +- .../Data/Generated/AddressData.cs | 10 +- .../Data/Generated/ContactData.cs | 49 +- .../Data/Generated/GenderData.cs | 43 +- .../Data/Generated/IContactData.cs | 24 +- .../Data/Generated/IGenderData.cs | 18 +- .../Data/Generated/IPersonData.cs | 80 +- .../Data/Generated/IProductData.cs | 12 +- .../Data/Generated/IReferenceDataData.cs | 26 +- .../Data/Generated/IRobotData.cs | 26 +- .../Data/Generated/ITripPersonData.cs | 20 +- .../Data/Generated/PersonData.cs | 181 +- .../Data/Generated/ProductData.cs | 34 +- .../Data/Generated/ReferenceDataData.cs | 56 +- .../Data/Generated/RobotData.cs | 55 +- .../Data/Generated/TripPersonData.cs | 48 +- .../Data/Generated/WorkHistoryData.cs | 10 +- .../Data/Model/Generated/Person.cs | 6 +- .../Data/Model/Generated/PowerSource.cs | 40 + .../Data/Model/Generated/Product.cs | 6 +- .../Data/Model/Generated/Robot.cs | 10 +- .../DataSvc/Generated/ContactDataSvc.cs | 43 +- .../DataSvc/Generated/GenderDataSvc.cs | 33 +- .../DataSvc/Generated/IContactDataSvc.cs | 24 +- .../DataSvc/Generated/IGenderDataSvc.cs | 18 +- .../DataSvc/Generated/IPersonDataSvc.cs | 80 +- .../DataSvc/Generated/IProductDataSvc.cs | 12 +- .../Generated/IReferenceDataDataSvc.cs | 5 +- .../DataSvc/Generated/IRobotDataSvc.cs | 26 +- .../DataSvc/Generated/ITripPersonDataSvc.cs | 20 +- .../DataSvc/Generated/PersonDataSvc.cs | 160 +- .../DataSvc/Generated/ProductDataSvc.cs | 25 +- .../DataSvc/Generated/ReferenceDataDataSvc.cs | 13 +- .../DataSvc/Generated/RobotDataSvc.cs | 45 +- .../DataSvc/Generated/TripPersonDataSvc.cs | 37 +- .../Generated/ConfigManager.cs | 10 +- .../Generated/ContactManager.cs | 50 +- .../Generated/GenderManager.cs | 39 +- .../Generated/IConfigManager.cs | 2 +- .../Generated/IContactManager.cs | 24 +- .../Generated/IGenderManager.cs | 18 +- .../Generated/IPersonManager.cs | 86 +- .../Generated/IProductManager.cs | 12 +- .../Generated/IRobotManager.cs | 26 +- .../Generated/ITripPersonManager.cs | 20 +- .../Generated/PersonManager.cs | 147 +- .../Generated/ProductManager.cs | 32 +- .../Generated/ReferenceDataProvider.cs | 10 +- .../Generated/RobotManager.cs | 61 +- .../Generated/TripPersonManager.cs | 46 +- .../Demo/Beef.Demo.Business/PersonManager.cs | 10 +- .../Validation/PersonValidator.cs | 2 +- samples/Demo/Beef.Demo.CodeGen/Beef.Demo.xml | 8 +- .../Demo/Beef.Demo.CodeGen/Beef.RefData.xml | 4 +- .../Properties/launchSettings.json | 2 +- .../Agents/Generated/ConfigAgent.cs | 12 +- .../Agents/Generated/ContactAgent.cs | 72 +- .../Agents/Generated/GenderAgent.cs | 52 +- .../Agents/Generated/PersonAgent.cs | 316 ++-- .../Agents/Generated/ProductAgent.cs | 32 +- .../Agents/Generated/ReferenceDataAgent.cs | 26 +- .../Generated/ReferenceDataAgentProvider.cs | 19 +- .../Agents/Generated/RobotAgent.cs | 99 +- .../Agents/Generated/TripPersonAgent.cs | 62 +- .../Entities/Generated/Address.cs | 46 +- .../Entities/Generated/Company.cs | 42 +- .../Entities/Generated/Contact.cs | 99 +- .../Entities/Generated/Country.cs | 38 +- .../Entities/Generated/EyeColor.cs | 38 +- .../Entities/Generated/Gender.cs | 58 +- .../Entities/Generated/IReferenceData.cs | 2 +- .../Entities/Generated/MapArgs.cs | 69 +- .../Entities/Generated/MapCoordinates.cs | 46 +- .../Entities/Generated/Person.cs | 136 +- .../Entities/Generated/PersonArgs.cs | 56 +- .../Entities/Generated/PersonDetail.cs | 82 +- .../Entities/Generated/PersonOther.cs | 10 +- .../Entities/Generated/PowerSource.cs | 40 +- .../Entities/Generated/Product.cs | 102 +- .../Entities/Generated/ProductArgs.cs | 46 +- .../Entities/Generated/ReferenceData.cs | 2 +- .../Entities/Generated/Robot.cs | 119 +- .../Entities/Generated/RobotArgs.cs | 50 +- .../Entities/Generated/TripPerson.cs | 101 +- .../Entities/Generated/USState.cs | 38 +- .../Entities/Generated/WorkHistory.cs | 96 +- .../Grpc/Generated/RobotAgent.cs | 38 +- .../Grpc/Generated/Transformers.cs | 18 +- .../Grpc/Generated/beef.demo.proto | 2 +- samples/Demo/Beef.Demo.Functions/Startup.cs | 3 +- samples/Demo/Beef.Demo.Test/FixtureSetup.cs | 7 +- samples/Demo/Beef.Demo.Test/PersonTest.cs | 2 +- .../Generated/EmployeeController.cs | 152 ++ .../Generated/PerformanceReviewController.cs | 132 ++ .../Generated/ReferenceDataController.cs | 144 ++ samples/My.Hr/My.Hr.Api/My.Hr.Api.csproj | 22 + samples/My.Hr/My.Hr.Api/Program.cs | 24 + .../My.Hr.Api/Properties/launchSettings.json | 30 + samples/My.Hr/My.Hr.Api/Startup.cs | 128 ++ .../My.Hr.Api/appsettings.Development.json | 9 + samples/My.Hr/My.Hr.Api/appsettings.json | 3 + samples/My.Hr/My.Hr.Api/webapisettings.json | 38 + .../Data/EfModel/Generated/Employee.cs | 106 ++ .../Data/EfModel/Generated/Gender.cs | 106 ++ .../Generated/ModelBuilderExtensions.cs | 37 + .../EfModel/Generated/PerformanceOutcome.cs | 106 ++ .../EfModel/Generated/PerformanceReview.cs | 112 ++ .../EfModel/Generated/RelationshipType.cs | 106 ++ .../EfModel/Generated/TerminationReason.cs | 106 ++ .../Data/EfModel/Generated/USState.cs | 106 ++ .../My.Hr.Business/Data/EmployeeBaseData.cs | 14 + .../My.Hr/My.Hr.Business/Data/EmployeeData.cs | 100 ++ .../Data/Generated/EmergencyContactData.cs | 57 + .../Data/Generated/EmergencyContactDataTvp.cs | 41 + .../Data/Generated/EmployeeBaseData.cs | 88 + .../Data/Generated/EmployeeData.cs | 138 ++ .../Data/Generated/IEmployeeData.cs | 70 + .../Data/Generated/IPerformanceReviewData.cs | 62 + .../Data/Generated/IReferenceDataData.cs | 55 + .../Data/Generated/PerformanceReviewData.cs | 148 ++ .../Data/Generated/ReferenceDataData.cs | 130 ++ ...eferenceDataServiceCollectionExtensions.cs | 30 + .../Generated/ServiceCollectionExtensions.cs | 31 + .../Data/Generated/TerminationDetailData.cs | 75 + samples/My.Hr/My.Hr.Business/Data/HrDb.cs | 24 + samples/My.Hr/My.Hr.Business/Data/HrEfDb.cs | 16 + .../My.Hr.Business/Data/HrEfDbContext.cs | 46 + .../Data/PerformanceReviewData.cs | 12 + .../DataSvc/Generated/EmployeeDataSvc.cs | 143 ++ .../DataSvc/Generated/IEmployeeDataSvc.cs | 70 + .../Generated/IPerformanceReviewDataSvc.cs | 62 + .../Generated/IReferenceDataDataSvc.cs | 25 +- .../Generated/PerformanceReviewDataSvc.cs | 126 ++ .../DataSvc/Generated/ReferenceDataDataSvc.cs | 69 + ...eferenceDataServiceCollectionExtensions.cs | 30 + .../Generated/ServiceCollectionExtensions.cs | 31 + .../Generated/EmployeeManager.cs | 147 ++ .../Generated/IEmployeeManager.cs | 71 + .../Generated/IPerformanceReviewManager.cs | 64 + .../Generated/PerformanceReviewManager.cs | 129 ++ .../Generated/ReferenceDataProvider.cs | 104 ++ ...eferenceDataServiceCollectionExtensions.cs | 31 + .../Generated/ServiceCollectionExtensions.cs | 31 + .../My.Hr.Business/My.Hr.Business.csproj | 14 + .../Validation/CommonValidators.cs | 33 + .../Validation/EmployeeArgsValidator.cs | 22 + .../Validation/EmployeeValidator.cs | 87 + .../Validation/PerformanceReviewValidator.cs | 66 + .../Validation/TerminationDetailValidator.cs | 20 + .../My.Hr/My.Hr.CodeGen/My.Hr.CodeGen.csproj | 10 + samples/My.Hr/My.Hr.CodeGen/My.Hr.xml | 151 ++ samples/My.Hr/My.Hr.CodeGen/My.RefData.xml | 8 + samples/My.Hr/My.Hr.CodeGen/Program.cs | 21 + .../Properties/launchSettings.json | 8 + .../Agents/Generated/EmployeeAgent.cs | 177 ++ .../Generated/PerformanceReviewAgent.cs | 159 ++ .../Agents/Generated/ReferenceDataAgent.cs | 150 ++ .../Generated/ReferenceDataAgentProvider.cs | 172 ++ .../Entities/Generated/Address.cs | 271 +++ .../Entities/Generated/EmergencyContact.cs | 334 ++++ .../Entities/Generated/Employee.cs | 228 +++ .../Entities/Generated/EmployeeArgs.cs | 281 ++++ .../Entities/Generated/EmployeeBase.cs | 509 ++++++ .../My.Hr.Common/Entities/Generated/Gender.cs | 154 ++ .../Entities/Generated/IReferenceData.cs | 51 + .../Entities/Generated/PerformanceOutcome.cs | 154 ++ .../Entities/Generated/PerformanceReview.cs | 456 +++++ .../Entities/Generated/ReferenceData.cs | 81 +- .../Entities/Generated/RelationshipType.cs | 154 ++ .../Entities/Generated/TerminationDetail.cs | 220 +++ .../Entities/Generated/TerminationReason.cs | 154 ++ .../Entities/Generated/USState.cs | 154 ++ .../My.Hr/My.Hr.Common/My.Hr.Common.csproj | 13 + .../My.Hr/My.Hr.Database/Data/RefData.yaml | 72 + .../20190101-000000-create-Ref-schema.sql | 2 + .../20190101-000001-create-Hr-schema.sql | 2 + .../20200909-162702-create-Hr-Employee.sql | 24 + ...0909-163321-create-Hr-EmergencyContact.sql | 14 + .../20200909-164735-create-Ref-Gender.sql | 18 + ...09-164828-create-Ref-TerminationReason.sql | 18 + ...909-165308-create-Ref-RelationshipType.sql | 18 + .../20200909-165752-create-Ref-USState.sql | 18 + ...915-160812-create-Hr-PerformanceReview.sql | 19 + ...5-161927-create-Ref-PerformanceOutcome.sql | 18 + .../My.Hr.Database/My.Hr.Database.csproj | 29 + .../My.Hr/My.Hr.Database/My.Hr.Database.xml | 49 + samples/My.Hr/My.Hr.Database/Program.cs | 21 + .../spEmergencyContactGetByEmployeeId.sql | 21 + .../Generated/spEmergencyContactMerge.sql | 74 + .../Generated/spEmployeeCreate.sql | 88 + .../Generated/spEmployeeDelete.sql | 31 + .../Generated/spEmployeeGet.sql | 33 + .../Generated/spEmployeeUpdate.sql | 83 + .../Generated/udtEmergencyContactList.sql | 11 + samples/My.Hr/My.Hr.Test/Data/Data.yaml | 10 + samples/My.Hr/My.Hr.Test/Data/PerfReview.yaml | 5 + samples/My.Hr/My.Hr.Test/EmployeeTest.cs | 529 ++++++ samples/My.Hr/My.Hr.Test/FixtureSetup.cs | 31 + samples/My.Hr/My.Hr.Test/My.Hr.Test.csproj | 38 + .../My.Hr/My.Hr.Test/PerformanceReviewTest.cs | 226 +++ .../Validators/EmployeeValidatorTest.cs | 231 +++ .../PerformanceReviewValidatorTest.cs | 192 +++ samples/My.Hr/README.md | 103 ++ samples/My.Hr/docs/Employee-Api.md | 377 +++++ samples/My.Hr/docs/Employee-DB.md | 231 +++ samples/My.Hr/docs/Employee-Search.md | 162 ++ samples/My.Hr/docs/Employee-Terminate.md | 123 ++ samples/My.Hr/docs/Employee-Test.md | 68 + samples/My.Hr/docs/Performance-Review.md | 307 ++++ .../Beef.AspNetCore.WebApi.csproj | 2 +- src/Beef.AspNetCore.WebApi/ChangeLog.md | 4 +- .../WebApiActionBase.cs | 22 +- src/Beef.Core/Beef.Core.csproj | 2 +- src/Beef.Core/CHANGELOG.md | 5 + src/Beef.Core/CodeGen/CodeGenerator.cs | 405 ----- .../Diagnostics/ColoredConsoleLogger.cs | 12 +- src/Beef.Core/Mapper/PropertyMapper.cs | 5 +- .../Mapper/PropertyMapperCustomBase.cs | 2 +- src/Beef.Core/Mapper/PropertySrceMapper.cs | 9 +- src/Beef.Core/RefData/ReferenceDataBase.cs | 23 +- .../RefData/ReferenceDataCollectionBase.cs | 2 +- .../Reflection/PropertyExpression.cs | 2 +- src/Beef.Core/StringConversion.cs | 173 ++ src/Beef.Core/Validation/PropertyContext.cs | 14 + src/Beef.Core/Validation/PropertyRule.cs | 4 +- src/Beef.Core/Validation/ValidationContext.cs | 21 +- .../Validation/ValidationExtensions.cs | 2 +- .../Validation/ValueValidatorResult.cs | 12 +- src/Beef.Data.Cosmos/Beef.Data.Cosmos.csproj | 2 +- src/Beef.Data.Cosmos/CHANGELOG.md | 3 + src/Beef.Data.Cosmos/CosmosDbContainer.cs | 4 +- src/Beef.Data.Cosmos/CosmosDbQuery.cs | 4 +- .../CosmosDbValueContainer.cs | 4 +- src/Beef.Data.Cosmos/CosmosDbValueQuery.cs | 4 +- .../Beef.Data.Database.csproj | 2 +- src/Beef.Data.Database/CHANGELOG.md | 4 + src/Beef.Data.Database/DatabaseBase.cs | 6 +- .../DatabasePropertyMapper.cs | 9 +- .../Beef.Data.EntityFrameworkCore.csproj | 2 +- .../CHANGELOG.md | 3 + src/Beef.Data.EntityFrameworkCore/EfDbBase.cs | 40 +- .../EfDbQuery.cs | 12 +- src/Beef.Data.OData/Beef.Data.OData.csproj | 2 +- src/Beef.Data.OData/CHANGELOG.md | 3 + src/Beef.Data.OData/ODataBase.cs | 40 +- src/Beef.Data.OData/ODataQuery.cs | 10 +- src/Beef.Events/Beef.Events.csproj | 2 +- src/Beef.Events/CHANGELOG.md | 3 + src/Beef.Events/EventExtensions.cs | 25 +- .../Subscribe/EventDataSubscriberHost.cs | 52 +- .../Subscribe/EventHubSubscriberHost.cs | 74 +- src/Beef.Events/Subscribe/EventSubscriber.cs | 6 +- .../Subscribe/EventSubscriberHost.cs | 57 +- .../Subscribe/EventSubscriberHostArgs.cs | 164 +- src/Beef.Events/Subscribe/IEventSubscriber.cs | 6 +- src/Beef.Grpc/Beef.Grpc.csproj | 2 +- src/Beef.Grpc/CHANGELOG.md | 3 + src/Beef.Grpc/GrpcService.cs | 11 +- .../Beef.Template.Solution.csproj | 1 + templates/Beef.Template.Solution/CHANGELOG.md | 3 + .../content/.template.config/template.json | 8 + .../Company.AppName.Api.csproj | 4 +- .../Company.AppName.Business.csproj | 6 +- .../Data/AppNameEfDb.cs | 2 +- .../Data/PersonData.cs | 4 +- .../Validation/PersonValidator.cs | 2 +- .../Company.AppName.CodeGen.csproj | 2 +- .../Company.AppName.xml | 8 +- .../Company.RefData.xml | 10 +- .../Company.AppName.Common.csproj | 2 +- .../Company.AppName.Database.csproj | 4 +- .../Company.AppName.Database.xml | 2 +- .../Company.AppName.Test.csproj | 2 +- .../Data/{Person.yaml => Data.yaml} | 0 .../Company.AppName.Test/FixtureSetup.cs | 7 +- .../Company.AppName.Test/PersonTest.cs | 155 +- .../Validators/PersonValidatorTest.cs | 71 + .../Mapper/EntityMapperTest.cs | 28 + .../CosmosDbContainerTest.cs | 12 +- .../Subscribe/EventDataSubscriberHostTest.cs | 10 +- .../Subscribe/EventSubscriberHostArgsTest.cs | 13 +- .../Beef.CodeGen.Core.csproj | 90 +- tools/Beef.CodeGen.Core/CHANGELOG.md | 5 + .../Beef.CodeGen.Core}/CodeGenConfig.cs | 12 +- tools/Beef.CodeGen.Core/CodeGenConsole.cs | 4 +- .../Beef.CodeGen.Core}/CodeGenException.cs | 0 tools/Beef.CodeGen.Core/CodeGenExecutor.cs | 287 +++- .../Beef.CodeGen.Core}/CodeGenTemplate.cs | 50 +- tools/Beef.CodeGen.Core/CodeGenerator.cs | 230 +++ tools/Beef.CodeGen.Core/Config/ConfigBase.cs | 55 +- .../Config/Database/CodeGenConfig.cs | 188 +++ .../Config/Database/ParameterConfig.cs | 29 + .../Config/Database/StoredProcedureConfig.cs | 90 + .../Config/Database/TableConfig.cs | 206 +++ .../Config/Entity/CodeGenConfig.cs | 73 +- .../Config/Entity/ConstConfig.cs | 16 +- .../Config/Entity/EntityConfig.cs | 429 ++++- .../Config/Entity/OperationConfig.cs | 408 ++++- .../Config/Entity/ParameterConfig.cs | 116 +- .../Config/Entity/PropertyConfig.cs | 202 ++- .../Config/Entity/RefDataConfig.cs | 65 + .../Beef.CodeGen.Core/Config/XmlJsonRename.cs | 35 +- .../Converters/JsonOrYamlToXmlConverter.cs | 135 ++ .../Converters/XmlToYamlConverter.cs | 192 +++ tools/Beef.CodeGen.Core/Entities/Table.cs | 2 +- .../Generators/CodeGeneratorBase.cs | 125 ++ .../Generators/EntityCodeGenerator.cs | 22 + .../Generators/EntityDataCodeGenerator.cs | 22 + .../Generators/EntityDataSvcCodeGenerator.cs | 22 + .../EntityGrpcAgentCodeGenerator.cs | 22 + .../EntityGrpcProtoCodeGenerator.cs | 20 + .../EntityGrpcServiceCodeGenerator.cs | 22 + .../Generators/EntityManagerCodeGenerator.cs | 22 + .../Generators/EntityModelCodeGenerator.cs | 22 + .../Generators/EntityOmitBaseCodeGenerator.cs | 22 + .../EntityWebApiAgentCodeGenerator.cs | 22 + .../EntityWebApiControllerCodeGenerator.cs | 22 + .../Generators/HandlebarsHelpers.cs | 212 +++ .../Generators/IEntityDataCodeGenerator.cs | 22 + .../Generators/IEntityDataSvcCodeGenerator.cs | 22 + .../Generators/IEntityManagerCodeGenerator.cs | 22 + .../Generators/JsonSchemaGenerator.cs | 6 +- .../Generators/ModelCodeGenerator.cs | 22 + .../Generators/RootEntityCodeGenerator.cs | 9 + .../Generators/XmlSchemaGenerator.cs | 4 +- .../ICodeGenConfigLoader.cs | 0 .../Loaders/EntityConfigLoader.cs | 6 +- .../Loaders/OperationConfigLoader.cs | 6 +- .../Loaders/ParameterConfigLoader.cs | 10 +- .../Loaders/PropertyConfigLoader.cs | 12 +- .../Loaders/TableConfigLoader.cs | 4 +- tools/Beef.CodeGen.Core/ResourceManager.cs | 49 +- .../Schema/codegen.table.xsd | 2 +- .../Scripts/DataModelOnly.xml | 4 +- tools/Beef.CodeGen.Core/Scripts/Database.xml | 2 +- .../Scripts/EntityBusiness.xml | 27 +- .../Beef.CodeGen.Core/Scripts/EntityOnly.xml | 2 +- .../Scripts/EntityWebApiCoreAgent.xml | 42 +- .../Beef.CodeGen.Core/Scripts/RefDataCore.xml | 37 +- .../Scripts/RefDataCoreCrud.xml | 53 +- .../Templates/EntityDataSvc_cs.hbs | 137 ++ .../Templates/EntityDataSvc_cs.xml | 671 -------- .../Templates/EntityData_cs.hbs | 342 ++++ .../Templates/EntityData_cs.xml | 1466 ----------------- .../Templates/EntityGrpcAgent_cs.hbs | 82 + .../Templates/EntityGrpcAgent_cs.xml | 522 ------ .../Templates/EntityGrpcService_cs.hbs | 70 + .../Templates/EntityGrpcService_cs.xml | 279 ---- .../Templates/EntityManager_cs.hbs | 158 ++ .../Templates/EntityManager_cs.xml | 770 --------- .../Templates/EntityWebApiAgent_cs.hbs | 101 ++ .../Templates/EntityWebApiAgent_cs.xml | 568 ------- .../Templates/EntityWebApiController_cs.hbs | 120 ++ .../EntityWebApiCoreController_cs.xml | 805 --------- .../Beef.CodeGen.Core/Templates/Entity_cs.hbs | 586 +++++++ .../Beef.CodeGen.Core/Templates/Entity_cs.xml | 1335 --------------- ...formers_cs.xml => GrpcTransformers_cs.hbs} | 122 +- .../Templates/Grpc_proto.hbs | 95 ++ .../Templates/Grpc_proto.xml | 270 --- .../Templates/IEntityDataSvc_cs.hbs | 54 + .../Templates/IEntityDataSvc_cs.xml | 235 --- .../Templates/IEntityData_cs.hbs | 54 + .../Templates/IEntityData_cs.xml | 235 --- .../Templates/IEntityManager_cs.hbs | 54 + .../Templates/IEntityManager_cs.xml | 235 --- .../Templates/IReferenceDataDataSvc_cs.hbs | 34 + .../Templates/IReferenceDataData_cs.hbs | 41 + .../Templates/IReferenceDataData_cs.xml | 59 - .../Templates/IReferenceData_cs.hbs | 38 + .../Templates/IReferenceData_cs.xml | 55 - .../Beef.CodeGen.Core/Templates/Model_cs.hbs | 89 + .../Templates/ReferenceDataDataSvc_cs.hbs | 72 + .../Templates/ReferenceDataDataSvc_cs.xml | 102 -- .../Templates/ReferenceDataData_cs.hbs | 144 ++ .../Templates/ReferenceDataData_cs.xml | 461 ------ .../Templates/ReferenceDataProvider_cs.hbs | 89 + .../Templates/ReferenceDataProvider_cs.xml | 107 -- ...> ReferenceDataWebApiAgentProvider_cs.hbs} | 84 +- ...cs.xml => ReferenceDataWebApiAgent_cs.hbs} | 66 +- .../ReferenceDataWebApiController_cs.hbs | 91 + .../ReferenceDataWebApiCoreController_cs.xml | 143 -- .../Templates/ReferenceData_cs.hbs | 76 + .../ServiceCollectionExtensionsDataSvc_cs.hbs | 35 + .../ServiceCollectionExtensionsDataSvc_cs.xml | 67 - .../ServiceCollectionExtensionsData_cs.hbs | 35 + .../ServiceCollectionExtensionsData_cs.xml | 68 - .../ServiceCollectionExtensionsManager_cs.hbs | 43 + .../ServiceCollectionExtensionsManager_cs.xml | 79 - .../Beef.Database.Core.csproj | 2 +- tools/Beef.Database.Core/CHANGELOG.md | 4 + tools/Beef.Database.Core/DatabaseConsole.cs | 5 +- .../DatabaseConsoleWrapper.cs | 33 +- tools/Beef.Database.Core/DatabaseExecutor.cs | 167 +- tools/Beef.Database.Core/README.md | 14 +- .../Resources/ScriptNew_sql.xml | 38 +- tools/Beef.Database.Core/Sql/SqlDataTable.cs | 7 +- .../Beef.Database.Core/Sql/SqlDataUpdater.cs | 4 +- tools/Beef.Test.NUnit/Beef.Test.NUnit.csproj | 2 +- tools/Beef.Test.NUnit/CHANGELOG.md | 5 + tools/Beef.Test.NUnit/ExtensionMethods.cs | 67 +- .../Tests/EventSubscriberTest.cs | 3 +- .../Tests/ExpectValidationExceptionTest.cs | 4 +- tools/Beef.Test.NUnit/ValidationTester.cs | 270 +++ 469 files changed, 21684 insertions(+), 13531 deletions(-) create mode 100644 samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/AccountUType.cs create mode 100644 samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/MaturityInstructions.cs create mode 100644 samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/OpenStatus.cs create mode 100644 samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/ProductCategory.cs create mode 100644 samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/TransactionStatus.cs create mode 100644 samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/TransactionType.cs create mode 100644 samples/Demo/Beef.Demo.Business/Data/Model/Generated/PowerSource.cs create mode 100644 samples/My.Hr/My.Hr.Api/Controllers/Generated/EmployeeController.cs create mode 100644 samples/My.Hr/My.Hr.Api/Controllers/Generated/PerformanceReviewController.cs create mode 100644 samples/My.Hr/My.Hr.Api/Controllers/Generated/ReferenceDataController.cs create mode 100644 samples/My.Hr/My.Hr.Api/My.Hr.Api.csproj create mode 100644 samples/My.Hr/My.Hr.Api/Program.cs create mode 100644 samples/My.Hr/My.Hr.Api/Properties/launchSettings.json create mode 100644 samples/My.Hr/My.Hr.Api/Startup.cs create mode 100644 samples/My.Hr/My.Hr.Api/appsettings.Development.json create mode 100644 samples/My.Hr/My.Hr.Api/appsettings.json create mode 100644 samples/My.Hr/My.Hr.Api/webapisettings.json create mode 100644 samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/Employee.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/Gender.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/ModelBuilderExtensions.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/PerformanceOutcome.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/PerformanceReview.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/RelationshipType.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/TerminationReason.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/USState.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/EmployeeBaseData.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/EmployeeData.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/Generated/EmergencyContactData.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/Generated/EmergencyContactDataTvp.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/Generated/EmployeeBaseData.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/Generated/EmployeeData.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/Generated/IEmployeeData.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/Generated/IPerformanceReviewData.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/Generated/IReferenceDataData.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/Generated/PerformanceReviewData.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/Generated/ReferenceDataData.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/Generated/ReferenceDataServiceCollectionExtensions.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/Generated/ServiceCollectionExtensions.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/Generated/TerminationDetailData.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/HrDb.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/HrEfDb.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/HrEfDbContext.cs create mode 100644 samples/My.Hr/My.Hr.Business/Data/PerformanceReviewData.cs create mode 100644 samples/My.Hr/My.Hr.Business/DataSvc/Generated/EmployeeDataSvc.cs create mode 100644 samples/My.Hr/My.Hr.Business/DataSvc/Generated/IEmployeeDataSvc.cs create mode 100644 samples/My.Hr/My.Hr.Business/DataSvc/Generated/IPerformanceReviewDataSvc.cs rename tools/Beef.CodeGen.Core/Templates/IReferenceDataDataSvc_cs.xml => samples/My.Hr/My.Hr.Business/DataSvc/Generated/IReferenceDataDataSvc.cs (54%) create mode 100644 samples/My.Hr/My.Hr.Business/DataSvc/Generated/PerformanceReviewDataSvc.cs create mode 100644 samples/My.Hr/My.Hr.Business/DataSvc/Generated/ReferenceDataDataSvc.cs create mode 100644 samples/My.Hr/My.Hr.Business/DataSvc/Generated/ReferenceDataServiceCollectionExtensions.cs create mode 100644 samples/My.Hr/My.Hr.Business/DataSvc/Generated/ServiceCollectionExtensions.cs create mode 100644 samples/My.Hr/My.Hr.Business/Generated/EmployeeManager.cs create mode 100644 samples/My.Hr/My.Hr.Business/Generated/IEmployeeManager.cs create mode 100644 samples/My.Hr/My.Hr.Business/Generated/IPerformanceReviewManager.cs create mode 100644 samples/My.Hr/My.Hr.Business/Generated/PerformanceReviewManager.cs create mode 100644 samples/My.Hr/My.Hr.Business/Generated/ReferenceDataProvider.cs create mode 100644 samples/My.Hr/My.Hr.Business/Generated/ReferenceDataServiceCollectionExtensions.cs create mode 100644 samples/My.Hr/My.Hr.Business/Generated/ServiceCollectionExtensions.cs create mode 100644 samples/My.Hr/My.Hr.Business/My.Hr.Business.csproj create mode 100644 samples/My.Hr/My.Hr.Business/Validation/CommonValidators.cs create mode 100644 samples/My.Hr/My.Hr.Business/Validation/EmployeeArgsValidator.cs create mode 100644 samples/My.Hr/My.Hr.Business/Validation/EmployeeValidator.cs create mode 100644 samples/My.Hr/My.Hr.Business/Validation/PerformanceReviewValidator.cs create mode 100644 samples/My.Hr/My.Hr.Business/Validation/TerminationDetailValidator.cs create mode 100644 samples/My.Hr/My.Hr.CodeGen/My.Hr.CodeGen.csproj create mode 100644 samples/My.Hr/My.Hr.CodeGen/My.Hr.xml create mode 100644 samples/My.Hr/My.Hr.CodeGen/My.RefData.xml create mode 100644 samples/My.Hr/My.Hr.CodeGen/Program.cs create mode 100644 samples/My.Hr/My.Hr.CodeGen/Properties/launchSettings.json create mode 100644 samples/My.Hr/My.Hr.Common/Agents/Generated/EmployeeAgent.cs create mode 100644 samples/My.Hr/My.Hr.Common/Agents/Generated/PerformanceReviewAgent.cs create mode 100644 samples/My.Hr/My.Hr.Common/Agents/Generated/ReferenceDataAgent.cs create mode 100644 samples/My.Hr/My.Hr.Common/Agents/Generated/ReferenceDataAgentProvider.cs create mode 100644 samples/My.Hr/My.Hr.Common/Entities/Generated/Address.cs create mode 100644 samples/My.Hr/My.Hr.Common/Entities/Generated/EmergencyContact.cs create mode 100644 samples/My.Hr/My.Hr.Common/Entities/Generated/Employee.cs create mode 100644 samples/My.Hr/My.Hr.Common/Entities/Generated/EmployeeArgs.cs create mode 100644 samples/My.Hr/My.Hr.Common/Entities/Generated/EmployeeBase.cs create mode 100644 samples/My.Hr/My.Hr.Common/Entities/Generated/Gender.cs create mode 100644 samples/My.Hr/My.Hr.Common/Entities/Generated/IReferenceData.cs create mode 100644 samples/My.Hr/My.Hr.Common/Entities/Generated/PerformanceOutcome.cs create mode 100644 samples/My.Hr/My.Hr.Common/Entities/Generated/PerformanceReview.cs rename tools/Beef.CodeGen.Core/Templates/ReferenceData_cs.xml => samples/My.Hr/My.Hr.Common/Entities/Generated/ReferenceData.cs (58%) create mode 100644 samples/My.Hr/My.Hr.Common/Entities/Generated/RelationshipType.cs create mode 100644 samples/My.Hr/My.Hr.Common/Entities/Generated/TerminationDetail.cs create mode 100644 samples/My.Hr/My.Hr.Common/Entities/Generated/TerminationReason.cs create mode 100644 samples/My.Hr/My.Hr.Common/Entities/Generated/USState.cs create mode 100644 samples/My.Hr/My.Hr.Common/My.Hr.Common.csproj create mode 100644 samples/My.Hr/My.Hr.Database/Data/RefData.yaml create mode 100644 samples/My.Hr/My.Hr.Database/Migrations/20190101-000000-create-Ref-schema.sql create mode 100644 samples/My.Hr/My.Hr.Database/Migrations/20190101-000001-create-Hr-schema.sql create mode 100644 samples/My.Hr/My.Hr.Database/Migrations/20200909-162702-create-Hr-Employee.sql create mode 100644 samples/My.Hr/My.Hr.Database/Migrations/20200909-163321-create-Hr-EmergencyContact.sql create mode 100644 samples/My.Hr/My.Hr.Database/Migrations/20200909-164735-create-Ref-Gender.sql create mode 100644 samples/My.Hr/My.Hr.Database/Migrations/20200909-164828-create-Ref-TerminationReason.sql create mode 100644 samples/My.Hr/My.Hr.Database/Migrations/20200909-165308-create-Ref-RelationshipType.sql create mode 100644 samples/My.Hr/My.Hr.Database/Migrations/20200909-165752-create-Ref-USState.sql create mode 100644 samples/My.Hr/My.Hr.Database/Migrations/20200915-160812-create-Hr-PerformanceReview.sql create mode 100644 samples/My.Hr/My.Hr.Database/Migrations/20200915-161927-create-Ref-PerformanceOutcome.sql create mode 100644 samples/My.Hr/My.Hr.Database/My.Hr.Database.csproj create mode 100644 samples/My.Hr/My.Hr.Database/My.Hr.Database.xml create mode 100644 samples/My.Hr/My.Hr.Database/Program.cs create mode 100644 samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmergencyContactGetByEmployeeId.sql create mode 100644 samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmergencyContactMerge.sql create mode 100644 samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeCreate.sql create mode 100644 samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeDelete.sql create mode 100644 samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeGet.sql create mode 100644 samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeUpdate.sql create mode 100644 samples/My.Hr/My.Hr.Database/Schema/Hr/Types/User-Defined Table Types/Generated/udtEmergencyContactList.sql create mode 100644 samples/My.Hr/My.Hr.Test/Data/Data.yaml create mode 100644 samples/My.Hr/My.Hr.Test/Data/PerfReview.yaml create mode 100644 samples/My.Hr/My.Hr.Test/EmployeeTest.cs create mode 100644 samples/My.Hr/My.Hr.Test/FixtureSetup.cs create mode 100644 samples/My.Hr/My.Hr.Test/My.Hr.Test.csproj create mode 100644 samples/My.Hr/My.Hr.Test/PerformanceReviewTest.cs create mode 100644 samples/My.Hr/My.Hr.Test/Validators/EmployeeValidatorTest.cs create mode 100644 samples/My.Hr/My.Hr.Test/Validators/PerformanceReviewValidatorTest.cs create mode 100644 samples/My.Hr/README.md create mode 100644 samples/My.Hr/docs/Employee-Api.md create mode 100644 samples/My.Hr/docs/Employee-DB.md create mode 100644 samples/My.Hr/docs/Employee-Search.md create mode 100644 samples/My.Hr/docs/Employee-Terminate.md create mode 100644 samples/My.Hr/docs/Employee-Test.md create mode 100644 samples/My.Hr/docs/Performance-Review.md delete mode 100644 src/Beef.Core/CodeGen/CodeGenerator.cs create mode 100644 src/Beef.Core/StringConversion.cs rename templates/Beef.Template.Solution/content/Company.AppName.Test/Data/{Person.yaml => Data.yaml} (100%) create mode 100644 templates/Beef.Template.Solution/content/Company.AppName.Test/Validators/PersonValidatorTest.cs rename {src/Beef.Core/CodeGen => tools/Beef.CodeGen.Core}/CodeGenConfig.cs (98%) rename {src/Beef.Core/CodeGen => tools/Beef.CodeGen.Core}/CodeGenException.cs (100%) rename {src/Beef.Core/CodeGen => tools/Beef.CodeGen.Core}/CodeGenTemplate.cs (94%) create mode 100644 tools/Beef.CodeGen.Core/CodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Config/Database/CodeGenConfig.cs create mode 100644 tools/Beef.CodeGen.Core/Config/Database/ParameterConfig.cs create mode 100644 tools/Beef.CodeGen.Core/Config/Database/StoredProcedureConfig.cs create mode 100644 tools/Beef.CodeGen.Core/Config/Database/TableConfig.cs create mode 100644 tools/Beef.CodeGen.Core/Config/Entity/RefDataConfig.cs create mode 100644 tools/Beef.CodeGen.Core/Converters/JsonOrYamlToXmlConverter.cs create mode 100644 tools/Beef.CodeGen.Core/Converters/XmlToYamlConverter.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/CodeGeneratorBase.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/EntityCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/EntityDataCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/EntityDataSvcCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/EntityGrpcAgentCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/EntityGrpcProtoCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/EntityGrpcServiceCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/EntityManagerCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/EntityModelCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/EntityOmitBaseCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/EntityWebApiAgentCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/EntityWebApiControllerCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/HandlebarsHelpers.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/IEntityDataCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/IEntityDataSvcCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/IEntityManagerCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/ModelCodeGenerator.cs create mode 100644 tools/Beef.CodeGen.Core/Generators/RootEntityCodeGenerator.cs rename {src/Beef.Core/CodeGen => tools/Beef.CodeGen.Core}/ICodeGenConfigLoader.cs (100%) create mode 100644 tools/Beef.CodeGen.Core/Templates/EntityDataSvc_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/EntityDataSvc_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/EntityData_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/EntityData_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/EntityGrpcAgent_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/EntityGrpcAgent_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/EntityGrpcService_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/EntityGrpcService_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/EntityManager_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/EntityManager_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/EntityWebApiAgent_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/EntityWebApiAgent_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/EntityWebApiController_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/EntityWebApiCoreController_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/Entity_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/Entity_cs.xml rename tools/Beef.CodeGen.Core/Templates/{GrpcTransformers_cs.xml => GrpcTransformers_cs.hbs} (59%) create mode 100644 tools/Beef.CodeGen.Core/Templates/Grpc_proto.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/Grpc_proto.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/IEntityDataSvc_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/IEntityDataSvc_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/IEntityData_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/IEntityData_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/IEntityManager_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/IEntityManager_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/IReferenceDataDataSvc_cs.hbs create mode 100644 tools/Beef.CodeGen.Core/Templates/IReferenceDataData_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/IReferenceDataData_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/IReferenceData_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/IReferenceData_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/Model_cs.hbs create mode 100644 tools/Beef.CodeGen.Core/Templates/ReferenceDataDataSvc_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/ReferenceDataDataSvc_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/ReferenceDataData_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/ReferenceDataData_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/ReferenceDataProvider_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/ReferenceDataProvider_cs.xml rename tools/Beef.CodeGen.Core/Templates/{ReferenceDataWebApiAgentProvider_cs.xml => ReferenceDataWebApiAgentProvider_cs.hbs} (57%) rename tools/Beef.CodeGen.Core/Templates/{ReferenceDataWebApiAgent_cs.xml => ReferenceDataWebApiAgent_cs.hbs} (52%) create mode 100644 tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiController_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiCoreController_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/ReferenceData_cs.hbs create mode 100644 tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsDataSvc_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsDataSvc_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsData_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsData_cs.xml create mode 100644 tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsManager_cs.hbs delete mode 100644 tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsManager_cs.xml create mode 100644 tools/Beef.Test.NUnit/ValidationTester.cs diff --git a/Beef.sln b/Beef.sln index 3bf331eac..e1d899244 100644 --- a/Beef.sln +++ b/Beef.sln @@ -113,7 +113,38 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Beef.Demo.Abc.Database", "s EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Grpc", "Grpc", "{3C2DB788-C4D6-48E7-9E26-9563B1C59EB5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Beef.Grpc", "src\Beef.Grpc\Beef.Grpc.csproj", "{2AD5436B-10AB-4312-AD25-A9EB70E0C92D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Beef.Grpc", "src\Beef.Grpc\Beef.Grpc.csproj", "{2AD5436B-10AB-4312-AD25-A9EB70E0C92D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "My.Hr", "My.Hr", "{FEA11C67-E7C7-49BC-99F5-5E5BF8F8674F}" + ProjectSection(SolutionItems) = preProject + samples\My.Hr\README.md = samples\My.Hr\README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testing", "Testing", "{00C76124-DB2C-4653-B8C1-F7260784EA5A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{BFA3B544-8437-4158-A98F-84491B12F472}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "My.Hr.CodeGen", "samples\My.Hr\My.Hr.CodeGen\My.Hr.CodeGen.csproj", "{E773F133-A920-49A9-96BD-F7BD73A63D0C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "My.Hr.Database", "samples\My.Hr\My.Hr.Database\My.Hr.Database.csproj", "{9DEA75E3-5F77-40F5-B8E1-CB5028C41D9F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "My.Hr.Api", "samples\My.Hr\My.Hr.Api\My.Hr.Api.csproj", "{B7D52220-ABD0-40B7-9D23-E51B344D97B8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "My.Hr.Business", "samples\My.Hr\My.Hr.Business\My.Hr.Business.csproj", "{BD7BDE9B-7F1A-484A-95C3-A5ACCA2A2DA8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "My.Hr.Common", "samples\My.Hr\My.Hr.Common\My.Hr.Common.csproj", "{F962F1CC-DA7C-4146-8D36-0E6C74C261E5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "My.Hr.Test", "samples\My.Hr\My.Hr.Test\My.Hr.Test.csproj", "{54B168EB-C74D-4588-9D9E-BDD386D84C1F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{2429F084-A3F8-4F8A-88FF-E3BF5B210C13}" + ProjectSection(SolutionItems) = preProject + samples\My.Hr\docs\Employee-Api.md = samples\My.Hr\docs\Employee-Api.md + samples\My.Hr\docs\Employee-DB.md = samples\My.Hr\docs\Employee-DB.md + samples\My.Hr\docs\Employee-Search.md = samples\My.Hr\docs\Employee-Search.md + samples\My.Hr\docs\Employee-Terminate.md = samples\My.Hr\docs\Employee-Terminate.md + samples\My.Hr\docs\Employee-Test.md = samples\My.Hr\docs\Employee-Test.md + samples\My.Hr\docs\Performance-Review.md = samples\My.Hr\docs\Performance-Review.md + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -245,6 +276,30 @@ Global {2AD5436B-10AB-4312-AD25-A9EB70E0C92D}.Debug|Any CPU.Build.0 = Debug|Any CPU {2AD5436B-10AB-4312-AD25-A9EB70E0C92D}.Release|Any CPU.ActiveCfg = Release|Any CPU {2AD5436B-10AB-4312-AD25-A9EB70E0C92D}.Release|Any CPU.Build.0 = Release|Any CPU + {E773F133-A920-49A9-96BD-F7BD73A63D0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E773F133-A920-49A9-96BD-F7BD73A63D0C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E773F133-A920-49A9-96BD-F7BD73A63D0C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E773F133-A920-49A9-96BD-F7BD73A63D0C}.Release|Any CPU.Build.0 = Release|Any CPU + {9DEA75E3-5F77-40F5-B8E1-CB5028C41D9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9DEA75E3-5F77-40F5-B8E1-CB5028C41D9F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9DEA75E3-5F77-40F5-B8E1-CB5028C41D9F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9DEA75E3-5F77-40F5-B8E1-CB5028C41D9F}.Release|Any CPU.Build.0 = Release|Any CPU + {B7D52220-ABD0-40B7-9D23-E51B344D97B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B7D52220-ABD0-40B7-9D23-E51B344D97B8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B7D52220-ABD0-40B7-9D23-E51B344D97B8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B7D52220-ABD0-40B7-9D23-E51B344D97B8}.Release|Any CPU.Build.0 = Release|Any CPU + {BD7BDE9B-7F1A-484A-95C3-A5ACCA2A2DA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BD7BDE9B-7F1A-484A-95C3-A5ACCA2A2DA8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BD7BDE9B-7F1A-484A-95C3-A5ACCA2A2DA8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BD7BDE9B-7F1A-484A-95C3-A5ACCA2A2DA8}.Release|Any CPU.Build.0 = Release|Any CPU + {F962F1CC-DA7C-4146-8D36-0E6C74C261E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F962F1CC-DA7C-4146-8D36-0E6C74C261E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F962F1CC-DA7C-4146-8D36-0E6C74C261E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F962F1CC-DA7C-4146-8D36-0E6C74C261E5}.Release|Any CPU.Build.0 = Release|Any CPU + {54B168EB-C74D-4588-9D9E-BDD386D84C1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {54B168EB-C74D-4588-9D9E-BDD386D84C1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54B168EB-C74D-4588-9D9E-BDD386D84C1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {54B168EB-C74D-4588-9D9E-BDD386D84C1F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -293,6 +348,16 @@ Global {5D6F532E-7DAC-40B3-8A65-D557B34C3605} = {104CA8C9-84F6-4B5C-9576-F25ADA255CF0} {3C2DB788-C4D6-48E7-9E26-9563B1C59EB5} = {6C902314-82BD-48FD-8777-5C3AC986E4D3} {2AD5436B-10AB-4312-AD25-A9EB70E0C92D} = {3C2DB788-C4D6-48E7-9E26-9563B1C59EB5} + {FEA11C67-E7C7-49BC-99F5-5E5BF8F8674F} = {2CD70532-8E64-46DF-A895-B41234BAC290} + {00C76124-DB2C-4653-B8C1-F7260784EA5A} = {FEA11C67-E7C7-49BC-99F5-5E5BF8F8674F} + {BFA3B544-8437-4158-A98F-84491B12F472} = {FEA11C67-E7C7-49BC-99F5-5E5BF8F8674F} + {E773F133-A920-49A9-96BD-F7BD73A63D0C} = {BFA3B544-8437-4158-A98F-84491B12F472} + {9DEA75E3-5F77-40F5-B8E1-CB5028C41D9F} = {BFA3B544-8437-4158-A98F-84491B12F472} + {B7D52220-ABD0-40B7-9D23-E51B344D97B8} = {FEA11C67-E7C7-49BC-99F5-5E5BF8F8674F} + {BD7BDE9B-7F1A-484A-95C3-A5ACCA2A2DA8} = {FEA11C67-E7C7-49BC-99F5-5E5BF8F8674F} + {F962F1CC-DA7C-4146-8D36-0E6C74C261E5} = {FEA11C67-E7C7-49BC-99F5-5E5BF8F8674F} + {54B168EB-C74D-4588-9D9E-BDD386D84C1F} = {00C76124-DB2C-4653-B8C1-F7260784EA5A} + {2429F084-A3F8-4F8A-88FF-E3BF5B210C13} = {FEA11C67-E7C7-49BC-99F5-5E5BF8F8674F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {42D71086-61E6-4D31-B4B8-BFC8CC471428} diff --git a/README.md b/README.md index 55ccd42ea..729708978 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,7 @@ The following samples are provided to guide usage: Sample | Description -|- +[`My.Hr`](./samples/My.Hr) | A sample as an end-to-end solution walkthrough to demonstrate the usage of _Beef_ within the context of a fictitious Human Resources solution. The main intent is to show how _Beef_ can be used against a relational database (SQL Server) leveraging both direct ADO.NET (with stored procedures) and Entity Framework (EF) where applicable. [`Cdr.Banking`](./samples/Cdr.Banking) | A sample as an end-to-end solution to demonstrate _Beef_ being used to solve a real-world scenario. This demonstrates an implementation of the [CDR](https://consumerdatastandards.org.au/) [Banking](https://consumerdatastandardsaustralia.github.io/standards/#consumer-data-standards-banking-apis) APIs leveraging a Cosmos DB data source. [`Demo`](./samples/Demo) | A sample as an end-to-end solution to demonstrate the tiering & layering, code-generation, database management and automated intra-domain integration testing. This is primarily used to further test the key end-to-end capabilities enabled by _Beef_. @@ -219,11 +220,16 @@ _Beef_ is open source under the [MIT license](./LICENSE) and is free for commerc To start using _Beef_ you do not need to clone or fork the repo; you just need to create a solution with the underlying projects using the prescribed [solution structure](./docs/Solution-Structure.md), including referencing the appropriate [NuGet packages](#Framework). To accelerate this a .NET Core [template capability](./templates/Beef.Template.Solution/README.md) is provided to enable you to get up and running in minutes. -See the following for example end-to-end usage; each demonstrating the same API functionality leveraging different data sources to accomplish: +See the following for example end-to-end solution/project creation; each demonstrating the same API functionality leveraging different data sources to accomplish: - [Cosmos sample](./docs/Sample-Cosmos-GettingStarted.md) - [Database Stored Procedures sample](./docs/Sample-StoredProcs-GettingStarted.md) - [Database Entity Framework sample](./docs/Sample-EntityFramework-GettingStarted.md) +Otherwise, follow along with the following sample tutorials that will provide a more in-depth walkthrough solving a defined functional problem: + +- [`My.Hr`](./samples/My.Hr) +- [`Cdr.Banking`](./samples/Cdr.Banking) +
## Contributing diff --git a/samples/Cdr.Banking/Cdr.Banking.Api/Controllers/Generated/AccountController.cs b/samples/Cdr.Banking/Cdr.Banking.Api/Controllers/Generated/AccountController.cs index 56eaf8bb7..17a759345 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Api/Controllers/Generated/AccountController.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Api/Controllers/Generated/AccountController.cs @@ -21,28 +21,32 @@ namespace Cdr.Banking.Api.Controllers { /// - /// Provides the Account Web API functionality. + /// Provides the Web API functionality. /// + [AllowAnonymous] [Route("api/v1/banking/accounts")] public partial class AccountController : ControllerBase { private readonly IAccountManager _manager; - + /// /// Initializes a new instance of the class. /// /// The . - public AccountController(IAccountManager manager) => _manager = Check.NotNull(manager, nameof(manager)); + public AccountController(IAccountManager manager) + { _manager = Check.NotNull(manager, nameof(manager)); AccountControllerCtor(); } + + partial void AccountControllerCtor(); // Enables additional functionality to be added to the constructor. /// /// Get all accounts. /// /// The Product Category (see ). /// The Open Status (see ). - /// The Is Owned. - /// A . - [HttpGet()] - [Route("")] + /// Indicates whether Is Owned. + /// The + [AllowAnonymous] + [HttpGet("")] [ProducesResponseType(typeof(AccountCollection), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult GetAccounts([FromQuery(Name = "product-category")] string? productCategory = default, [FromQuery(Name = "open-status")] string? openStatus = default, [FromQuery(Name = "is-owned")] bool? isOwned = default) @@ -56,9 +60,9 @@ public IActionResult GetAccounts([FromQuery(Name = "product-category")] string? /// Get . /// /// The identifier. - /// The selected entity where found. - [HttpGet()] - [Route("{accountId}")] + /// The selected where found. + [AllowAnonymous] + [HttpGet("{accountId}")] [ProducesResponseType(typeof(AccountDetail), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult GetDetail(string? accountId) @@ -71,9 +75,9 @@ public IActionResult GetDetail(string? accountId) /// Get . /// /// The identifier. - /// The selected entity where found. - [HttpGet()] - [Route("{accountId}/balance")] + /// The selected where found. + [AllowAnonymous] + [HttpGet("{accountId}/balance")] [ProducesResponseType(typeof(Balance), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult GetBalance(string? accountId) diff --git a/samples/Cdr.Banking/Cdr.Banking.Api/Controllers/Generated/ReferenceDataController.cs b/samples/Cdr.Banking/Cdr.Banking.Api/Controllers/Generated/ReferenceDataController.cs index 01659da04..0abda80b9 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Api/Controllers/Generated/ReferenceDataController.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Api/Controllers/Generated/ReferenceDataController.cs @@ -24,14 +24,16 @@ namespace Cdr.Banking.Api.Controllers /// /// Provides the ReferenceData Web API functionality. /// + [AllowAnonymous] public partial class ReferenceDataController : ControllerBase { /// - /// Gets all of the reference data entities that match the specified criteria. + /// Gets all of the reference data items that match the specified criteria. /// /// The reference data code list. /// The reference data text (including wildcards). - /// A collection. + /// A RefDataNamespace.OpenStatus collection. + [AllowAnonymous] [HttpGet()] [Route("api/v1/ref/openStatuses")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] @@ -41,11 +43,12 @@ public partial class ReferenceDataController : ControllerBase operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); /// - /// Gets all of the reference data entities that match the specified criteria. + /// Gets all of the reference data items that match the specified criteria. /// /// The reference data code list. /// The reference data text (including wildcards). - /// A collection. + /// A RefDataNamespace.ProductCategory collection. + [AllowAnonymous] [HttpGet()] [Route("api/v1/ref/productCategories")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] @@ -55,11 +58,12 @@ public partial class ReferenceDataController : ControllerBase operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); /// - /// Gets all of the reference data entities that match the specified criteria. + /// Gets all of the reference data items that match the specified criteria. /// /// The reference data code list. /// The reference data text (including wildcards). - /// A collection. + /// A RefDataNamespace.AccountUType collection. + [AllowAnonymous] [HttpGet()] [Route("api/v1/ref/accountUTypes")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] @@ -69,11 +73,12 @@ public partial class ReferenceDataController : ControllerBase operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); /// - /// Gets all of the reference data entities that match the specified criteria. + /// Gets all of the reference data items that match the specified criteria. /// /// The reference data code list. /// The reference data text (including wildcards). - /// A collection. + /// A RefDataNamespace.MaturityInstructions collection. + [AllowAnonymous] [HttpGet()] [Route("api/v1/ref/maturityInstructions")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] @@ -83,11 +88,12 @@ public partial class ReferenceDataController : ControllerBase operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); /// - /// Gets all of the reference data entities that match the specified criteria. + /// Gets all of the reference data items that match the specified criteria. /// /// The reference data code list. /// The reference data text (including wildcards). - /// A collection. + /// A RefDataNamespace.TransactionType collection. + [AllowAnonymous] [HttpGet()] [Route("api/v1/ref/transactionTypes")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] @@ -97,11 +103,12 @@ public partial class ReferenceDataController : ControllerBase operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); /// - /// Gets all of the reference data entities that match the specified criteria. + /// Gets all of the reference data items that match the specified criteria. /// /// The reference data code list. /// The reference data text (including wildcards). - /// A collection. + /// A RefDataNamespace.TransactionStatus collection. + [AllowAnonymous] [HttpGet()] [Route("api/v1/ref/transactionStatuses")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] @@ -111,9 +118,10 @@ public partial class ReferenceDataController : ControllerBase operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); /// - /// Gets the reference data entries for the specified entities and codes from the query string; e.g: api/v1/ref?entity=codeX,codeY&entity2=codeZ&entity3 + /// Gets the reference data entries for the specified entities and codes from the query string; e.g: ?entity=codeX,codeY&entity2=codeZ&entity3 /// /// A . + [AllowAnonymous] [HttpGet()] [Route("api/v1/ref")] [ProducesResponseType(typeof(ReferenceDataMultiCollection), (int)HttpStatusCode.OK)] diff --git a/samples/Cdr.Banking/Cdr.Banking.Api/Controllers/Generated/TransactionController.cs b/samples/Cdr.Banking/Cdr.Banking.Api/Controllers/Generated/TransactionController.cs index 0646d741c..104058bf2 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Api/Controllers/Generated/TransactionController.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Api/Controllers/Generated/TransactionController.cs @@ -21,18 +21,22 @@ namespace Cdr.Banking.Api.Controllers { /// - /// Provides the Transaction Web API functionality. + /// Provides the Web API functionality. /// + [AllowAnonymous] [Route("api/v1/banking/accounts")] public partial class TransactionController : ControllerBase { private readonly ITransactionManager _manager; - + /// /// Initializes a new instance of the class. /// /// The . - public TransactionController(ITransactionManager manager) => _manager = Check.NotNull(manager, nameof(manager)); + public TransactionController(ITransactionManager manager) + { _manager = Check.NotNull(manager, nameof(manager)); TransactionControllerCtor(); } + + partial void TransactionControllerCtor(); // Enables additional functionality to be added to the constructor. /// /// Get transaction for account. @@ -43,9 +47,9 @@ public partial class TransactionController : ControllerBase /// The Min Amount. /// The Max Amount. /// The Text. - /// A . - [HttpGet()] - [Route("{accountId}/transactions")] + /// The + [AllowAnonymous] + [HttpGet("{accountId}/transactions")] [ProducesResponseType(typeof(TransactionCollection), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult GetTransactions([FromRoute] string? accountId, [FromQuery(Name = "oldest-time")] DateTime? fromDate = default, [FromQuery(Name = "newest-time")] DateTime? toDate = default, [FromQuery(Name = "min-amount")] decimal? minAmount = default, [FromQuery(Name = "max-amount")] decimal? maxAmount = default, string? text = default) diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/AccountData.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/AccountData.cs index 91bd3ef44..d8309cffa 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/AccountData.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/AccountData.cs @@ -10,9 +10,9 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Microsoft.Azure.Cosmos; using Beef; using Beef.Business; -using Microsoft.Azure.Cosmos; using Beef.Data.Cosmos; using Beef.Entities; using Beef.Mapper; @@ -23,7 +23,7 @@ namespace Cdr.Banking.Business.Data { /// - /// Provides the Account data access. + /// Provides the data access. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] public partial class AccountData : IAccountData @@ -43,19 +43,17 @@ public partial class AccountData : IAccountData /// Initializes a new instance of the class. /// /// The . - public AccountData(ICosmosDb cosmos) { _cosmos = Check.NotNull(cosmos, nameof(cosmos)); AccountDataCtor(); } + public AccountData(ICosmosDb cosmos) + { _cosmos = Check.NotNull(cosmos, nameof(cosmos)); AccountDataCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void AccountDataCtor(); + partial void AccountDataCtor(); // Enables additional functionality to be added to the constructor. /// /// Get all accounts. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetAccountsAsync(AccountArgs? args, PagingArgs? paging) { return DataInvoker.Current.InvokeAsync(this, async () => @@ -71,7 +69,7 @@ public Task GetAccountsAsync(AccountArgs? args, PagingA /// Get . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetDetailAsync(string? accountId) { return DataInvoker.Current.InvokeAsync(this, async () => @@ -85,14 +83,12 @@ public Task GetAccountsAsync(AccountArgs? args, PagingA /// Get . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetBalanceAsync(string? accountId) - { - return DataInvoker.Current.InvokeAsync(this, () => GetBalanceOnImplementationAsync(accountId)); - } + => DataInvoker.Current.InvokeAsync(this, () => GetBalanceOnImplementationAsync(accountId)); /// - /// Provides the entity and Cosmos property mapping. + /// Provides the and Cosmos property mapping. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] public partial class CosmosMapper : CosmosDbMapper @@ -102,7 +98,7 @@ public partial class CosmosMapper : CosmosDbMapper public CosmosMapper() { - Property(s => s.Id, d => d.Id).SetUniqueKey(true); + Property(s => s.Id, d => d.Id).SetUniqueKey(false); Property(s => s.CreationDate, d => d.CreationDate); Property(s => s.DisplayName, d => d.DisplayName); Property(s => s.Nickname, d => d.Nickname); @@ -115,10 +111,7 @@ public CosmosMapper() CosmosMapperCtor(); } - /// - /// Enables the constructor to be extended. - /// - partial void CosmosMapperCtor(); + partial void CosmosMapperCtor(); // Enables the CosmosMapper constructor to be extended. } } } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/AccountDetailData.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/AccountDetailData.cs index 38266fe9d..bd06ddf82 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/AccountDetailData.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/AccountDetailData.cs @@ -10,9 +10,9 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Microsoft.Azure.Cosmos; using Beef; using Beef.Business; -using Microsoft.Azure.Cosmos; using Beef.Data.Cosmos; using Beef.Entities; using Beef.Mapper; @@ -23,13 +23,14 @@ namespace Cdr.Banking.Business.Data { /// - /// Provides the Detail data access. + /// Provides the data access. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] public partial class AccountDetailData { + /// - /// Provides the entity and Cosmos property mapping. + /// Provides the and Cosmos property mapping. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] public partial class CosmosMapper : CosmosDbMapper @@ -50,10 +51,7 @@ public CosmosMapper() CosmosMapperCtor(); } - /// - /// Enables the constructor to be extended. - /// - partial void CosmosMapperCtor(); + partial void CosmosMapperCtor(); // Enables the CosmosMapper constructor to be extended. } } } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/IAccountData.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/IAccountData.cs index 005b65ab6..38ba8c4a5 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/IAccountData.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/IAccountData.cs @@ -17,30 +17,30 @@ namespace Cdr.Banking.Business.Data { /// - /// Defines the Account data access. + /// Defines the data access. /// public partial interface IAccountData { /// /// Get all accounts. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetAccountsAsync(AccountArgs? args, PagingArgs? paging); /// /// Get . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetDetailAsync(string? accountId); /// /// Get . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetBalanceAsync(string? accountId); } } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/IReferenceDataData.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/IReferenceDataData.cs index ac0b9f8d2..e66e6945a 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/IReferenceDataData.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/IReferenceDataData.cs @@ -15,44 +15,44 @@ namespace Cdr.Banking.Business.Data { /// - /// Provides the database access. + /// Provides the ReferenceData data access. /// public partial interface IReferenceDataData { /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . Task OpenStatusGetAllAsync(); /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . Task ProductCategoryGetAllAsync(); /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . Task AccountUTypeGetAllAsync(); /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . Task MaturityInstructionsGetAllAsync(); /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . Task TransactionTypeGetAllAsync(); /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . Task TransactionStatusGetAllAsync(); } } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/ITransactionData.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/ITransactionData.cs index eddf0f644..595968742 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/ITransactionData.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/ITransactionData.cs @@ -17,7 +17,7 @@ namespace Cdr.Banking.Business.Data { /// - /// Defines the Transaction data access. + /// Defines the data access. /// public partial interface ITransactionData { @@ -25,9 +25,9 @@ public partial interface ITransactionData /// Get transaction for account. /// /// The Account Id. - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetTransactionsAsync(string? accountId, TransactionArgs? args, PagingArgs? paging); } } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/ReferenceDataData.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/ReferenceDataData.cs index 02567bb8b..bb5ea36dd 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/ReferenceDataData.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/ReferenceDataData.cs @@ -9,42 +9,36 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; +using Microsoft.Azure.Cosmos; using Beef; using Beef.Business; +using Beef.Data.Cosmos; using Beef.Mapper; using Beef.Mapper.Converters; -using Beef.Data.Cosmos; using RefDataNamespace = Cdr.Banking.Common.Entities; namespace Cdr.Banking.Business.Data { /// - /// Provides the ReferenceData database access. + /// Provides the ReferenceData data access. /// public partial class ReferenceDataData : IReferenceDataData { private readonly ICosmosDb _cosmos; - /// - /// Parameterless constructor is explictly not supported. - /// - private ReferenceDataData() => throw new NotSupportedException(); - /// /// Initializes a new instance of the class. /// /// The . - public ReferenceDataData(ICosmosDb cosmos) { _cosmos = Check.NotNull(cosmos, nameof(cosmos)); ReferenceDataDataCtor(); } + public ReferenceDataData(ICosmosDb cosmos) + { _cosmos = Check.NotNull(cosmos, nameof(cosmos)); DataCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void ReferenceDataDataCtor(); + partial void DataCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . public async Task OpenStatusGetAllAsync() { var __coll = new RefDataNamespace.OpenStatusCollection(); @@ -53,9 +47,9 @@ public partial class ReferenceDataData : IReferenceDataData } /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . public async Task ProductCategoryGetAllAsync() { var __coll = new RefDataNamespace.ProductCategoryCollection(); @@ -64,9 +58,9 @@ public partial class ReferenceDataData : IReferenceDataData } /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . public async Task AccountUTypeGetAllAsync() { var __coll = new RefDataNamespace.AccountUTypeCollection(); @@ -75,9 +69,9 @@ public partial class ReferenceDataData : IReferenceDataData } /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . public async Task MaturityInstructionsGetAllAsync() { var __coll = new RefDataNamespace.MaturityInstructionsCollection(); @@ -86,9 +80,9 @@ public partial class ReferenceDataData : IReferenceDataData } /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . public async Task TransactionTypeGetAllAsync() { var __coll = new RefDataNamespace.TransactionTypeCollection(); @@ -97,9 +91,9 @@ public partial class ReferenceDataData : IReferenceDataData } /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . public async Task TransactionStatusGetAllAsync() { var __coll = new RefDataNamespace.TransactionStatusCollection(); @@ -108,39 +102,39 @@ public partial class ReferenceDataData : IReferenceDataData } /// - /// Provides the entity and Cosmos property mapping. + /// Provides the and Cosmos property mapping. /// - public static CosmosDbMapper OpenStatusMapper => CosmosDbMapper.CreateAuto() + public static CosmosDbMapper OpenStatusMapper => CosmosDbMapper.CreateAuto() .AddStandardProperties(); /// - /// Provides the entity and Cosmos property mapping. + /// Provides the and Cosmos property mapping. /// - public static CosmosDbMapper ProductCategoryMapper => CosmosDbMapper.CreateAuto() + public static CosmosDbMapper ProductCategoryMapper => CosmosDbMapper.CreateAuto() .AddStandardProperties(); /// - /// Provides the entity and Cosmos property mapping. + /// Provides the and Cosmos property mapping. /// - public static CosmosDbMapper AccountUTypeMapper => CosmosDbMapper.CreateAuto() + public static CosmosDbMapper AccountUTypeMapper => CosmosDbMapper.CreateAuto() .AddStandardProperties(); /// - /// Provides the entity and Cosmos property mapping. + /// Provides the and Cosmos property mapping. /// - public static CosmosDbMapper MaturityInstructionsMapper => CosmosDbMapper.CreateAuto() + public static CosmosDbMapper MaturityInstructionsMapper => CosmosDbMapper.CreateAuto() .AddStandardProperties(); /// - /// Provides the entity and Cosmos property mapping. + /// Provides the and Cosmos property mapping. /// - public static CosmosDbMapper TransactionTypeMapper => CosmosDbMapper.CreateAuto() + public static CosmosDbMapper TransactionTypeMapper => CosmosDbMapper.CreateAuto() .AddStandardProperties(); /// - /// Provides the entity and Cosmos property mapping. + /// Provides the and Cosmos property mapping. /// - public static CosmosDbMapper TransactionStatusMapper => CosmosDbMapper.CreateAuto() + public static CosmosDbMapper TransactionStatusMapper => CosmosDbMapper.CreateAuto() .AddStandardProperties(); } } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/TransactionData.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/TransactionData.cs index 84301822e..3097ab2ba 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/TransactionData.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Generated/TransactionData.cs @@ -10,9 +10,9 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Microsoft.Azure.Cosmos; using Beef; using Beef.Business; -using Microsoft.Azure.Cosmos; using Beef.Data.Cosmos; using Beef.Entities; using Beef.Mapper; @@ -23,7 +23,7 @@ namespace Cdr.Banking.Business.Data { /// - /// Provides the Transaction data access. + /// Provides the data access. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] public partial class TransactionData : ITransactionData @@ -43,20 +43,18 @@ public partial class TransactionData : ITransactionData /// Initializes a new instance of the class. /// /// The . - public TransactionData(ICosmosDb cosmos) { _cosmos = Check.NotNull(cosmos, nameof(cosmos)); TransactionDataCtor(); } + public TransactionData(ICosmosDb cosmos) + { _cosmos = Check.NotNull(cosmos, nameof(cosmos)); TransactionDataCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void TransactionDataCtor(); + partial void TransactionDataCtor(); // Enables additional functionality to be added to the constructor. /// /// Get transaction for account. /// /// The Account Id. - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetTransactionsAsync(string? accountId, TransactionArgs? args, PagingArgs? paging) { return DataInvoker.Current.InvokeAsync(this, async () => @@ -69,7 +67,7 @@ public Task GetTransactionsAsync(string? accountId, } /// - /// Provides the entity and Cosmos property mapping. + /// Provides the and Cosmos property mapping. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] public partial class CosmosMapper : CosmosDbMapper @@ -99,10 +97,7 @@ public CosmosMapper() CosmosMapperCtor(); } - /// - /// Enables the constructor to be extended. - /// - partial void CosmosMapperCtor(); + partial void CosmosMapperCtor(); // Enables the CosmosMapper constructor to be extended. } } } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/Account.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/Account.cs index fa5469b70..e2db6f222 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/Account.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/Account.cs @@ -8,8 +8,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Beef.Entities; using Newtonsoft.Json; @@ -17,7 +15,7 @@ namespace Cdr.Banking.Business.Data.Model { /// - /// Represents the Account model for data persistence entity. + /// Represents the Account model for data persistence model. /// [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public partial class Account : IStringIdentifier @@ -53,7 +51,7 @@ public partial class Account : IStringIdentifier public string? OpenStatus { get; set; } /// - /// Gets or sets a value indicating whether Is Owned. + /// Indicates whether Is Owned. /// [JsonProperty("isOwned", DefaultValueHandling = DefaultValueHandling.Ignore)] public bool IsOwned { get; set; } @@ -101,23 +99,23 @@ public partial class Account : IStringIdentifier public string? SpecificAccountUType { get; set; } /// - /// Gets or sets the Term Deposit (see ). + /// Gets or sets the Term Deposit (see ). /// [JsonProperty("termDeposit", DefaultValueHandling = DefaultValueHandling.Ignore)] public TermDepositAccount? TermDeposit { get; set; } /// - /// Gets or sets the Credit Card (see ). + /// Gets or sets the Credit Card (see ). /// [JsonProperty("creditCard", DefaultValueHandling = DefaultValueHandling.Ignore)] public CreditCardAccount? CreditCard { get; set; } /// - /// Gets or sets the Balance (see ). + /// Gets or sets the Balance (see ). /// [JsonProperty("balance", DefaultValueHandling = DefaultValueHandling.Ignore)] public Balance? Balance { get; set; } - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/AccountUType.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/AccountUType.cs new file mode 100644 index 000000000..aa7bc3998 --- /dev/null +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/AccountUType.cs @@ -0,0 +1,35 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData.Model; +using Newtonsoft.Json; + +namespace Cdr.Banking.Business.Data.Model +{ + /// + /// Represents the Account U Type model. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public partial class AccountUType : ReferenceDataBaseGuid + { + } + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class AccountUTypeCollection : List { } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/Balance.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/Balance.cs index 704b72e52..e18e1c5e8 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/Balance.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/Balance.cs @@ -8,8 +8,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Beef.Entities; using Newtonsoft.Json; @@ -17,7 +15,7 @@ namespace Cdr.Banking.Business.Data.Model { /// - /// Represents the Balance entity. + /// Represents the Balance model. /// [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public partial class Balance @@ -53,11 +51,11 @@ public partial class Balance public string? Currency { get; set; } /// - /// Gets or sets the Purses (see ). + /// Gets or sets the Purses. /// [JsonProperty("purses", DefaultValueHandling = DefaultValueHandling.Ignore)] public BalancePurseCollection? Purses { get; set; } - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/BalancePurse.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/BalancePurse.cs index 875a97a9f..1f4aee8dc 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/BalancePurse.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/BalancePurse.cs @@ -8,8 +8,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Beef.Entities; using Newtonsoft.Json; @@ -17,7 +15,7 @@ namespace Cdr.Banking.Business.Data.Model { /// - /// Represents the Balance Purse entity. + /// Represents the Balance Purse model. /// [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public partial class BalancePurse @@ -33,10 +31,10 @@ public partial class BalancePurse /// [JsonProperty("currency", DefaultValueHandling = DefaultValueHandling.Ignore)] public string? Currency { get; set; } - } + } /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class BalancePurseCollection : List { } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/CreditCardAccount.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/CreditCardAccount.cs index 1311e7df7..e4d20934b 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/CreditCardAccount.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/CreditCardAccount.cs @@ -8,8 +8,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Beef.Entities; using Newtonsoft.Json; @@ -17,7 +15,7 @@ namespace Cdr.Banking.Business.Data.Model { /// - /// Represents the Credit Card Account entity. + /// Represents the Credit Card Account model. /// [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public partial class CreditCardAccount @@ -45,7 +43,7 @@ public partial class CreditCardAccount /// [JsonProperty("paymentDueDate", DefaultValueHandling = DefaultValueHandling.Ignore)] public DateTime PaymentDueDate { get; set; } - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/MaturityInstructions.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/MaturityInstructions.cs new file mode 100644 index 000000000..da6eb1512 --- /dev/null +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/MaturityInstructions.cs @@ -0,0 +1,35 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData.Model; +using Newtonsoft.Json; + +namespace Cdr.Banking.Business.Data.Model +{ + /// + /// Represents the Maturity Instructions model. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public partial class MaturityInstructions : ReferenceDataBaseGuid + { + } + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class MaturityInstructionsCollection : List { } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/OpenStatus.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/OpenStatus.cs new file mode 100644 index 000000000..7f2d60bb6 --- /dev/null +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/OpenStatus.cs @@ -0,0 +1,35 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData.Model; +using Newtonsoft.Json; + +namespace Cdr.Banking.Business.Data.Model +{ + /// + /// Represents the Open Status model. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public partial class OpenStatus : ReferenceDataBaseGuid + { + } + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class OpenStatusCollection : List { } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/ProductCategory.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/ProductCategory.cs new file mode 100644 index 000000000..ff4f09c91 --- /dev/null +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/ProductCategory.cs @@ -0,0 +1,35 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData.Model; +using Newtonsoft.Json; + +namespace Cdr.Banking.Business.Data.Model +{ + /// + /// Represents the Product Category model. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public partial class ProductCategory : ReferenceDataBaseGuid + { + } + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class ProductCategoryCollection : List { } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/TermDepositAccount.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/TermDepositAccount.cs index ac60e0d07..a89cc7df4 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/TermDepositAccount.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/TermDepositAccount.cs @@ -8,8 +8,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Beef.Entities; using Newtonsoft.Json; @@ -17,7 +15,7 @@ namespace Cdr.Banking.Business.Data.Model { /// - /// Represents the Term Deposit Account entity. + /// Represents the Term Deposit Account model. /// [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public partial class TermDepositAccount @@ -51,7 +49,7 @@ public partial class TermDepositAccount /// [JsonProperty("maturityInstructions", DefaultValueHandling = DefaultValueHandling.Ignore)] public string? MaturityInstructions { get; set; } - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/Transaction.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/Transaction.cs index 2365c3f76..98da78003 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/Transaction.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/Transaction.cs @@ -8,8 +8,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Beef.Entities; using Newtonsoft.Json; @@ -17,7 +15,7 @@ namespace Cdr.Banking.Business.Data.Model { /// - /// Represents the Transaction model for data persistence entity. + /// Represents the Transaction model for data persistence model. /// [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public partial class Transaction : IStringIdentifier @@ -35,7 +33,7 @@ public partial class Transaction : IStringIdentifier public string? AccountId { get; set; } /// - /// Gets or sets a value indicating whether Is Detail Available. + /// Indicates whether Is Detail Available. /// [JsonProperty("isDetailAvailable", DefaultValueHandling = DefaultValueHandling.Ignore)] public bool IsDetailAvailable { get; set; } @@ -123,7 +121,7 @@ public partial class Transaction : IStringIdentifier /// [JsonProperty("transactionDateTime", DefaultValueHandling = DefaultValueHandling.Ignore)] public DateTime TransactionDateTime { get; set; } - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/TransactionStatus.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/TransactionStatus.cs new file mode 100644 index 000000000..122c52e9b --- /dev/null +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/TransactionStatus.cs @@ -0,0 +1,35 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData.Model; +using Newtonsoft.Json; + +namespace Cdr.Banking.Business.Data.Model +{ + /// + /// Represents the Transaction Status model. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public partial class TransactionStatus : ReferenceDataBaseGuid + { + } + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class TransactionStatusCollection : List { } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/TransactionType.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/TransactionType.cs new file mode 100644 index 000000000..8dba21449 --- /dev/null +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Data/Model/Generated/TransactionType.cs @@ -0,0 +1,35 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData.Model; +using Newtonsoft.Json; + +namespace Cdr.Banking.Business.Data.Model +{ + /// + /// Represents the Transaction Type model. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public partial class TransactionType : ReferenceDataBaseGuid + { + } + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class TransactionTypeCollection : List { } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/AccountDataSvc.cs b/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/AccountDataSvc.cs index 57a5c0cb3..8d7d96482 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/AccountDataSvc.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/AccountDataSvc.cs @@ -20,7 +20,7 @@ namespace Cdr.Banking.Business.DataSvc { /// - /// Provides the Account data repository services. + /// Provides the data repository services. /// public partial class AccountDataSvc : IAccountDataSvc { @@ -35,20 +35,17 @@ public partial class AccountDataSvc : IAccountDataSvc public AccountDataSvc(IAccountData data, IRequestCache cache) { _data = Check.NotNull(data, nameof(data)); _cache = Check.NotNull(cache, nameof(cache)); AccountDataSvcCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void AccountDataSvcCtor(); + partial void AccountDataSvcCtor(); // Enables additional functionality to be added to the constructor. /// /// Get all accounts. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetAccountsAsync(AccountArgs? args, PagingArgs? paging) { - return DataSvcInvoker.Current.InvokeAsync(typeof(AccountDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.GetAccountsAsync(args, paging).ConfigureAwait(false); return __result; @@ -59,17 +56,17 @@ public Task GetAccountsAsync(AccountArgs? args, PagingA /// Get . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetDetailAsync(string? accountId) { - return DataSvcInvoker.Current.InvokeAsync(typeof(AccountDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __key = new UniqueKey(accountId); - if (_cache.TryGetValue(__key, out AccountDetail __val)) + if (_cache.TryGetValue(__key, out AccountDetail? __val)) return __val; var __result = await _data.GetDetailAsync(accountId).ConfigureAwait(false); - _cache.SetValue(__key, __result!); + _cache.SetValue(__key, __result); return __result; }); } @@ -78,17 +75,17 @@ public Task GetAccountsAsync(AccountArgs? args, PagingA /// Get . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetBalanceAsync(string? accountId) { - return DataSvcInvoker.Current.InvokeAsync(typeof(AccountDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __key = new UniqueKey(accountId); - if (_cache.TryGetValue(__key, out Balance __val)) + if (_cache.TryGetValue(__key, out Balance? __val)) return __val; var __result = await _data.GetBalanceAsync(accountId).ConfigureAwait(false); - _cache.SetValue(__key, __result!); + _cache.SetValue(__key, __result); return __result; }); } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/IAccountDataSvc.cs b/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/IAccountDataSvc.cs index 228bdab7f..4934ebfa8 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/IAccountDataSvc.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/IAccountDataSvc.cs @@ -17,30 +17,30 @@ namespace Cdr.Banking.Business.DataSvc { /// - /// Defines the Account data repository services. + /// Defines the data repository services. /// public partial interface IAccountDataSvc { /// /// Get all accounts. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetAccountsAsync(AccountArgs? args, PagingArgs? paging); /// /// Get . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetDetailAsync(string? accountId); /// /// Get . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetBalanceAsync(string? accountId); } } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/IReferenceDataDataSvc.cs b/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/IReferenceDataDataSvc.cs index 8fa899190..d31f36556 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/IReferenceDataDataSvc.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/IReferenceDataDataSvc.cs @@ -5,9 +5,8 @@ #nullable enable #pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options -using Beef.RefData; using System; - +using Beef.RefData; using RefDataNamespace = Cdr.Banking.Common.Entities; namespace Cdr.Banking.Business.DataSvc @@ -15,7 +14,7 @@ namespace Cdr.Banking.Business.DataSvc /// /// Provides the ReferenceData data services. /// - public interface IReferenceDataDataSvc + public partial interface IReferenceDataDataSvc { /// /// Gets the for the associated . diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/ITransactionDataSvc.cs b/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/ITransactionDataSvc.cs index 88fa610f4..1ca8af329 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/ITransactionDataSvc.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/ITransactionDataSvc.cs @@ -17,7 +17,7 @@ namespace Cdr.Banking.Business.DataSvc { /// - /// Defines the Transaction data repository services. + /// Defines the data repository services. /// public partial interface ITransactionDataSvc { @@ -25,9 +25,9 @@ public partial interface ITransactionDataSvc /// Get transaction for account. /// /// The Account Id. - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetTransactionsAsync(string? accountId, TransactionArgs? args, PagingArgs? paging); } } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/ReferenceDataDataSvc.cs b/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/ReferenceDataDataSvc.cs index 492b35739..bfe4dcb8f 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/ReferenceDataDataSvc.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/ReferenceDataDataSvc.cs @@ -23,7 +23,7 @@ namespace Cdr.Banking.Business.DataSvc /// /// Provides the ReferenceData data services. /// - public class ReferenceDataDataSvc : IReferenceDataDataSvc + public partial class ReferenceDataDataSvc : IReferenceDataDataSvc { private readonly IServiceProvider _provider; private readonly Dictionary _cacheDict = new Dictionary(); @@ -41,8 +41,11 @@ public ReferenceDataDataSvc(IServiceProvider provider) _cacheDict.Add(typeof(RefDataNamespace.MaturityInstructions), new ReferenceDataCache(() => DataSvcInvoker.Current.InvokeAsync(typeof(ReferenceDataDataSvc), () => GetDataAsync(data => data.MaturityInstructionsGetAllAsync())))); _cacheDict.Add(typeof(RefDataNamespace.TransactionType), new ReferenceDataCache(() => DataSvcInvoker.Current.InvokeAsync(typeof(ReferenceDataDataSvc), () => GetDataAsync(data => data.TransactionTypeGetAllAsync())))); _cacheDict.Add(typeof(RefDataNamespace.TransactionStatus), new ReferenceDataCache(() => DataSvcInvoker.Current.InvokeAsync(typeof(ReferenceDataDataSvc), () => GetDataAsync(data => data.TransactionStatusGetAllAsync())))); + ReferenceDataDataSvcCtor(); } + partial void ReferenceDataDataSvcCtor(); // Enables the ReferenceDataDataSvc constructor to be extended. + /// /// Gets the data within a new scope; each reference data request needs to occur separately and independently. /// @@ -57,13 +60,9 @@ private async Task GetDataAsync(Func> func) /// /// The type associated /// A . - public IReferenceDataCollection GetCollection(Type type) - { - if (_cacheDict.TryGetValue(type ?? throw new ArgumentNullException(nameof(type)), out var rdc)) - return rdc.GetCollection(); - else + public IReferenceDataCollection GetCollection(Type type) => + _cacheDict.TryGetValue(type ?? throw new ArgumentNullException(nameof(type)), out var rdc) ? rdc.GetCollection() : throw new ArgumentException($"Type {type.Name} does not exist within the ReferenceDataDataSvc cache.", nameof(type)); - } } } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/TransactionDataSvc.cs b/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/TransactionDataSvc.cs index b582312a5..331976c01 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/TransactionDataSvc.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/DataSvc/Generated/TransactionDataSvc.cs @@ -19,7 +19,7 @@ namespace Cdr.Banking.Business.DataSvc { /// - /// Provides the Transaction data repository services. + /// Provides the data repository services. /// public partial class TransactionDataSvc : ITransactionDataSvc { @@ -32,21 +32,18 @@ public partial class TransactionDataSvc : ITransactionDataSvc public TransactionDataSvc(ITransactionData data) { _data = Check.NotNull(data, nameof(data)); TransactionDataSvcCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void TransactionDataSvcCtor(); + partial void TransactionDataSvcCtor(); // Enables additional functionality to be added to the constructor. /// /// Get transaction for account. /// /// The Account Id. - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetTransactionsAsync(string? accountId, TransactionArgs? args, PagingArgs? paging) { - return DataSvcInvoker.Current.InvokeAsync(typeof(TransactionDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.GetTransactionsAsync(accountId, args, paging).ConfigureAwait(false); return __result; diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Generated/AccountManager.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Generated/AccountManager.cs index ea53c0420..bf0624dec 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Generated/AccountManager.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Generated/AccountManager.cs @@ -14,14 +14,14 @@ using Beef.Entities; using Beef.Validation; using Cdr.Banking.Common.Entities; -using Cdr.Banking.Business.Validation; using Cdr.Banking.Business.DataSvc; +using Cdr.Banking.Business.Validation; using RefDataNamespace = Cdr.Banking.Common.Entities; namespace Cdr.Banking.Business { /// - /// Provides the Account business functionality. + /// Provides the business functionality. /// public partial class AccountManager : IAccountManager { @@ -31,29 +31,24 @@ public partial class AccountManager : IAccountManager /// Initializes a new instance of the class. /// /// The . - public AccountManager(IAccountDataSvc dataService) { _dataService = Check.NotNull(dataService, nameof(dataService)); AccountManagerCtor(); } + public AccountManager(IAccountDataSvc dataService) + { _dataService = Check.NotNull(dataService, nameof(dataService)); AccountManagerCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void AccountManagerCtor(); + partial void AccountManagerCtor(); // Enables additional functionality to be added to the constructor. /// /// Get all accounts. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetAccountsAsync(AccountArgs? args, PagingArgs? paging) { return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Read; Cleaner.CleanUp(args); - MultiValidator.Create() - .Add(args.Validate(nameof(args)).Entity(AccountArgsValidator.Default)) - .Run().ThrowOnError(); - + args.Validate(nameof(args)).Entity(AccountArgsValidator.Default).Run().ThrowOnError(); return Cleaner.Clean(await _dataService.GetAccountsAsync(args, paging).ConfigureAwait(false)); }); } @@ -62,17 +57,14 @@ public Task GetAccountsAsync(AccountArgs? args, PagingA /// Get . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetDetailAsync(string? accountId) { return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Read; Cleaner.CleanUp(accountId); - MultiValidator.Create() - .Add(accountId.Validate(nameof(accountId)).Mandatory()) - .Run().ThrowOnError(); - + accountId.Validate(nameof(accountId)).Mandatory().Run().ThrowOnError(); return Cleaner.Clean(await _dataService.GetDetailAsync(accountId).ConfigureAwait(false)); }); } @@ -81,17 +73,14 @@ public Task GetAccountsAsync(AccountArgs? args, PagingA /// Get . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetBalanceAsync(string? accountId) { return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Read; Cleaner.CleanUp(accountId); - MultiValidator.Create() - .Add(accountId.Validate(nameof(accountId)).Mandatory()) - .Run().ThrowOnError(); - + accountId.Validate(nameof(accountId)).Mandatory().Run().ThrowOnError(); return Cleaner.Clean(await _dataService.GetBalanceAsync(accountId).ConfigureAwait(false)); }); } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Generated/IAccountManager.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Generated/IAccountManager.cs index afa149c49..1a0f877d5 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Generated/IAccountManager.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Generated/IAccountManager.cs @@ -17,30 +17,30 @@ namespace Cdr.Banking.Business { /// - /// Defines the Account business functionality. + /// Defines the business functionality. /// public partial interface IAccountManager { /// /// Get all accounts. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetAccountsAsync(AccountArgs? args, PagingArgs? paging); /// /// Get . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetDetailAsync(string? accountId); /// /// Get . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetBalanceAsync(string? accountId); } } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Generated/ITransactionManager.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Generated/ITransactionManager.cs index f4dae8d62..c676ec501 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Generated/ITransactionManager.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Generated/ITransactionManager.cs @@ -17,7 +17,7 @@ namespace Cdr.Banking.Business { /// - /// Defines the Transaction business functionality. + /// Defines the business functionality. /// public partial interface ITransactionManager { @@ -25,9 +25,9 @@ public partial interface ITransactionManager /// Get transaction for account. /// /// The Account Id. - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetTransactionsAsync(string? accountId, TransactionArgs? args, PagingArgs? paging); } } diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Generated/ReferenceDataProvider.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Generated/ReferenceDataProvider.cs index 45720fe2f..77cb76d84 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Generated/ReferenceDataProvider.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Generated/ReferenceDataProvider.cs @@ -20,7 +20,7 @@ namespace Cdr.Banking.Business /// /// Provides the implementation using the corresponding data services. /// - public class ReferenceDataProvider : RefDataNamespace.ReferenceData + public partial class ReferenceDataProvider : RefDataNamespace.ReferenceData { private readonly IReferenceDataDataSvc _dataService; @@ -28,8 +28,10 @@ public class ReferenceDataProvider : RefDataNamespace.ReferenceData /// Initializes a new instance of the class. /// /// The . - public ReferenceDataProvider(IReferenceDataDataSvc dataService) => _dataService = Check.NotNull(dataService, nameof(dataService)); - + public ReferenceDataProvider(IReferenceDataDataSvc dataService) { _dataService = Check.NotNull(dataService, nameof(dataService)); ReferenceDataProviderCtor(); } + + partial void ReferenceDataProviderCtor(); // Enables the ReferenceDataProvider constructor to be extended. + #region Collections /// @@ -63,7 +65,7 @@ public class ReferenceDataProvider : RefDataNamespace.ReferenceData public override RefDataNamespace.TransactionStatusCollection TransactionStatus => (RefDataNamespace.TransactionStatusCollection)this[typeof(RefDataNamespace.TransactionStatus)]; #endregion - + /// /// Gets the for the associated . /// diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Generated/TransactionManager.cs b/samples/Cdr.Banking/Cdr.Banking.Business/Generated/TransactionManager.cs index 321e7fb4c..bf70f76ac 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Generated/TransactionManager.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Generated/TransactionManager.cs @@ -14,14 +14,14 @@ using Beef.Entities; using Beef.Validation; using Cdr.Banking.Common.Entities; -using Cdr.Banking.Business.Validation; using Cdr.Banking.Business.DataSvc; +using Cdr.Banking.Business.Validation; using RefDataNamespace = Cdr.Banking.Common.Entities; namespace Cdr.Banking.Business { /// - /// Provides the Transaction business functionality. + /// Provides the business functionality. /// public partial class TransactionManager : ITransactionManager { @@ -31,20 +31,18 @@ public partial class TransactionManager : ITransactionManager /// Initializes a new instance of the class. /// /// The . - public TransactionManager(ITransactionDataSvc dataService) { _dataService = Check.NotNull(dataService, nameof(dataService)); TransactionManagerCtor(); } + public TransactionManager(ITransactionDataSvc dataService) + { _dataService = Check.NotNull(dataService, nameof(dataService)); TransactionManagerCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void TransactionManagerCtor(); + partial void TransactionManagerCtor(); // Enables additional functionality to be added to the constructor. /// /// Get transaction for account. /// /// The Account Id. - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetTransactionsAsync(string? accountId, TransactionArgs? args, PagingArgs? paging) { return ManagerInvoker.Current.InvokeAsync(this, async () => diff --git a/samples/Cdr.Banking/Cdr.Banking.CodeGen/Cdr.RefData.xml b/samples/Cdr.Banking/Cdr.Banking.CodeGen/Cdr.RefData.xml index 0d8d8f092..b035c5299 100644 --- a/samples/Cdr.Banking/Cdr.Banking.CodeGen/Cdr.RefData.xml +++ b/samples/Cdr.Banking/Cdr.Banking.CodeGen/Cdr.RefData.xml @@ -3,14 +3,14 @@ - + - - - - - + + + + + \ No newline at end of file diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/AccountAgent.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/AccountAgent.cs index 1803d1a1b..ca9e1e793 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/AccountAgent.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/AccountAgent.cs @@ -1,5 +1,5 @@ /* - * This file is automatically generated; any changes will be lost. + * This file is automatically generated; any changes will be lost. */ #nullable enable @@ -19,14 +19,14 @@ namespace Cdr.Banking.Common.Agents { /// - /// Defines the Account Web API agent. + /// Defines the Web API agent. /// public partial interface IAccountAgent { /// /// Get all accounts. /// - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . @@ -38,7 +38,7 @@ public partial interface IAccountAgent /// The identifier. /// The optional . /// A . - Task> GetDetailAsync(string? accountId, WebApiRequestOptions? requestOptions = null); + Task> GetDetailAsync(string? accountId, WebApiRequestOptions? requestOptions = null); /// /// Get . @@ -46,11 +46,11 @@ public partial interface IAccountAgent /// The identifier. /// The optional . /// A . - Task> GetBalanceAsync(string? accountId, WebApiRequestOptions? requestOptions = null); + Task> GetBalanceAsync(string? accountId, WebApiRequestOptions? requestOptions = null); } /// - /// Provides the Account Web API agent. + /// Provides the Web API agent. /// public partial class AccountAgent : WebApiAgentBase, IAccountAgent { @@ -63,15 +63,13 @@ public AccountAgent(IWebApiAgentArgs args) : base(args) { } /// /// Get all accounts. /// - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . - public Task> GetAccountsAsync(AccountArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) - { - return GetCollectionResultAsync("api/v1/banking/accounts", requestOptions: requestOptions, + public Task> GetAccountsAsync(AccountArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) => + GetCollectionResultAsync("api/v1/banking/accounts", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args, WebApiArgType.FromUriUseProperties), new WebApiPagingArgsArg("paging", paging) }); - } /// /// Get . @@ -79,11 +77,9 @@ public Task> GetAccountsAsync(Account /// The identifier. /// The optional . /// A . - public Task> GetDetailAsync(string? accountId, WebApiRequestOptions? requestOptions = null) - { - return GetAsync("api/v1/banking/accounts/{accountId}", requestOptions: requestOptions, + public Task> GetDetailAsync(string? accountId, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/banking/accounts/{accountId}", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("accountId", accountId) }); - } /// /// Get . @@ -91,11 +87,9 @@ public Task> GetDetailAsync(string? accountId, /// The identifier. /// The optional . /// A . - public Task> GetBalanceAsync(string? accountId, WebApiRequestOptions? requestOptions = null) - { - return GetAsync("api/v1/banking/accounts/{accountId}/balance", requestOptions: requestOptions, + public Task> GetBalanceAsync(string? accountId, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/banking/accounts/{accountId}/balance", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("accountId", accountId) }); - } } } diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/ReferenceDataAgent.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/ReferenceDataAgent.cs index 0332100a8..0ceb2ec5c 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/ReferenceDataAgent.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/ReferenceDataAgent.cs @@ -23,7 +23,7 @@ namespace Cdr.Banking.Common.Agents public partial interface IReferenceDataAgent { /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -31,7 +31,7 @@ public partial interface IReferenceDataAgent Task> OpenStatusGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -39,7 +39,7 @@ public partial interface IReferenceDataAgent Task> ProductCategoryGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -47,7 +47,7 @@ public partial interface IReferenceDataAgent Task> AccountUTypeGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -55,7 +55,7 @@ public partial interface IReferenceDataAgent Task> MaturityInstructionsGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -63,7 +63,7 @@ public partial interface IReferenceDataAgent Task> TransactionTypeGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -92,7 +92,7 @@ public partial class ReferenceDataAgent : WebApiAgentBase, IReferenceDataAgent public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -101,7 +101,7 @@ public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } GetAsync("api/v1/ref/openStatuses", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -110,7 +110,7 @@ public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } GetAsync("api/v1/ref/productCategories", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -119,7 +119,7 @@ public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } GetAsync("api/v1/ref/accountUTypes", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -128,7 +128,7 @@ public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } GetAsync("api/v1/ref/maturityInstructions", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -137,7 +137,7 @@ public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } GetAsync("api/v1/ref/transactionTypes", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -148,7 +148,7 @@ public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } /// /// Gets the reference data entries for the specified entities and codes from the query string; e.g: api/v1/ref?entity=codeX,codeY&entity2=codeZ&entity3 /// - /// The list of reference data names. + /// The optional list of reference data names. /// The optional . /// A . /// The reference data objects will need to be manually extracted from the corresponding response content. diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/ReferenceDataAgentProvider.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/ReferenceDataAgentProvider.cs index 93fa85cd5..8a2c26d2a 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/ReferenceDataAgentProvider.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/ReferenceDataAgentProvider.cs @@ -23,7 +23,7 @@ namespace Cdr.Banking.Common.Agents /// /// Provides the implementation using the corresponding Web API agent. /// - public class ReferenceDataAgentProvider : RefDataNamespace.ReferenceData + public partial class ReferenceDataAgentProvider : RefDataNamespace.ReferenceData { private readonly Dictionary _nameDict = new Dictionary(); private readonly Dictionary _typeDict = new Dictionary(); @@ -64,50 +64,53 @@ public ReferenceDataAgentProvider(IReferenceDataAgent agent) _typeDict.Add(typeof(RefDataNamespace.TransactionStatus), nameof(TransactionStatus)); _cacheDict.Add(typeof(RefDataNamespace.TransactionStatus), new ReferenceDataCache(() => _agent.TransactionStatusGetAllAsync().ContinueWith((t) => t.Result.Value, TaskScheduler.Current))); + ReferenceDataAgentProviderCtor(); } + partial void ReferenceDataAgentProviderCtor(); // Enables the ReferenceDataAgentProvider constructor to be extended. + #endregion #region Collections - /// + /// /// Gets the . /// /// The . public override RefDataNamespace.OpenStatusCollection OpenStatus => (RefDataNamespace.OpenStatusCollection)this[typeof(RefDataNamespace.OpenStatus)]; - /// + /// /// Gets the . /// /// The . public override RefDataNamespace.ProductCategoryCollection ProductCategory => (RefDataNamespace.ProductCategoryCollection)this[typeof(RefDataNamespace.ProductCategory)]; - /// + /// /// Gets the . /// /// The . public override RefDataNamespace.AccountUTypeCollection AccountUType => (RefDataNamespace.AccountUTypeCollection)this[typeof(RefDataNamespace.AccountUType)]; - /// + /// /// Gets the . /// /// The . public override RefDataNamespace.MaturityInstructionsCollection MaturityInstructions => (RefDataNamespace.MaturityInstructionsCollection)this[typeof(RefDataNamespace.MaturityInstructions)]; - /// + /// /// Gets the . /// /// The . public override RefDataNamespace.TransactionTypeCollection TransactionType => (RefDataNamespace.TransactionTypeCollection)this[typeof(RefDataNamespace.TransactionType)]; - /// + /// /// Gets the . /// /// The . public override RefDataNamespace.TransactionStatusCollection TransactionStatus => (RefDataNamespace.TransactionStatusCollection)this[typeof(RefDataNamespace.TransactionStatus)]; #endregion - + #region This/GetCache/PrefetchAsync /// diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/TransactionAgent.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/TransactionAgent.cs index ce875334e..6f9c3d854 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/TransactionAgent.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Agents/Generated/TransactionAgent.cs @@ -1,5 +1,5 @@ /* - * This file is automatically generated; any changes will be lost. + * This file is automatically generated; any changes will be lost. */ #nullable enable @@ -19,7 +19,7 @@ namespace Cdr.Banking.Common.Agents { /// - /// Defines the Transaction Web API agent. + /// Defines the Web API agent. /// public partial interface ITransactionAgent { @@ -27,7 +27,7 @@ public partial interface ITransactionAgent /// Get transaction for account. /// /// The Account Id. - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . @@ -35,7 +35,7 @@ public partial interface ITransactionAgent } /// - /// Provides the Transaction Web API agent. + /// Provides the Web API agent. /// public partial class TransactionAgent : WebApiAgentBase, ITransactionAgent { @@ -49,15 +49,13 @@ public TransactionAgent(IWebApiAgentArgs args) : base(args) { } /// Get transaction for account. /// /// The Account Id. - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . - public Task> GetTransactionsAsync(string? accountId, TransactionArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) - { - return GetCollectionResultAsync("api/v1/banking/accounts/{accountId}/transactions", requestOptions: requestOptions, + public Task> GetTransactionsAsync(string? accountId, TransactionArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) => + GetCollectionResultAsync("api/v1/banking/accounts/{accountId}/transactions", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("accountId", accountId), new WebApiArg("args", args, WebApiArgType.FromUriUseProperties), new WebApiPagingArgsArg("paging", paging) }); - } } } diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/Account.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/Account.cs index 142dbab6d..ce6053200 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/Account.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/Account.cs @@ -43,12 +43,12 @@ public partial class Account : EntityBase, IStringIdentifier, IEquatable /// Gets or sets the identifier. /// - [JsonProperty("accountId", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("accountId", DefaultValueHandling = DefaultValueHandling.Include)] [Display(Name="Identifier")] public string? Id { get => _id; - set => SetValue(ref _id, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Id)); + set => SetValue(ref _id, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Id)); } /// @@ -56,11 +56,10 @@ public string? Id /// [JsonProperty("creationDate", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Creation Date")] - [DisplayFormat(DataFormatString = Beef.Entities.StringFormat.DateOnlyFormat)] public DateTime CreationDate { get => _creationDate; - set => SetValue(ref _creationDate, value, false, DateTimeTransform.DateOnly, nameof(CreationDate)); + set => SetValue(ref _creationDate, value, false, DateTimeTransform.DateOnly, nameof(CreationDate)); } /// @@ -71,7 +70,7 @@ public DateTime CreationDate public string? DisplayName { get => _displayName; - set => SetValue(ref _displayName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(DisplayName)); + set => SetValue(ref _displayName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(DisplayName)); } /// @@ -82,7 +81,7 @@ public string? DisplayName public string? Nickname { get => _nickname; - set => SetValue(ref _nickname, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Nickname)); + set => SetValue(ref _nickname, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Nickname)); } /// @@ -108,14 +107,14 @@ public RefDataNamespace.OpenStatus? OpenStatus } /// - /// Gets or sets a value indicating whether Is Owned. + /// Indicates whether Is Owned. /// [JsonProperty("isOwned", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Is Owned")] public bool IsOwned { get => _isOwned; - set => SetValue(ref _isOwned, value, false, false, nameof(IsOwned)); + set => SetValue(ref _isOwned, value, false, false, nameof(IsOwned)); } /// @@ -126,7 +125,7 @@ public bool IsOwned public string? MaskedNumber { get => _maskedNumber; - set => SetValue(ref _maskedNumber, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(MaskedNumber)); + set => SetValue(ref _maskedNumber, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(MaskedNumber)); } /// @@ -159,13 +158,13 @@ public RefDataNamespace.ProductCategory? ProductCategory public string? ProductName { get => _productName; - set => SetValue(ref _productName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ProductName)); + set => SetValue(ref _productName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ProductName)); } #endregion - #region UniqueKey - + #region IUniqueKey + /// /// Indicates whether the has a value. /// @@ -175,20 +174,17 @@ public string? ProductName /// Gets the list of property names that represent the unique key. /// public override string[] UniqueKeyProperties => new string[] { nameof(Id) }; - + /// /// Creates the . /// /// The . /// The . - public static UniqueKey CreateUniqueKey(string accountId) => new UniqueKey(accountId); - + public static UniqueKey CreateUniqueKey(string? accountId) => new UniqueKey(accountId); + /// - /// Gets the . + /// Gets the (consists of the following property(s): ). /// - /// - /// The UniqueKey key consists of the following property(s): . - /// public override UniqueKey UniqueKey => new UniqueKey(Id); #endregion @@ -200,36 +196,30 @@ public string? ProductName /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is Account val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is Account val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(Account? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(Account? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(Id, obj.Id) - && Equals(CreationDate, obj.CreationDate) - && Equals(DisplayName, obj.DisplayName) - && Equals(Nickname, obj.Nickname) - && Equals(OpenStatusSid, obj.OpenStatusSid) - && Equals(IsOwned, obj.IsOwned) - && Equals(MaskedNumber, obj.MaskedNumber) - && Equals(ProductCategorySid, obj.ProductCategorySid) - && Equals(ProductName, obj.ProductName); + return base.Equals((object)value) + && Equals(Id, value.Id) + && Equals(CreationDate, value.CreationDate) + && Equals(DisplayName, value.DisplayName) + && Equals(Nickname, value.Nickname) + && Equals(OpenStatusSid, value.OpenStatusSid) + && Equals(IsOwned, value.IsOwned) + && Equals(MaskedNumber, value.MaskedNumber) + && Equals(ProductCategorySid, value.ProductCategorySid) + && Equals(ProductName, value.ProductName); } /// @@ -249,9 +239,9 @@ public bool Equals(Account? obj) public static bool operator != (Account? a, Account? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -268,7 +258,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -287,8 +277,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(Account from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); Id = from.Id; @@ -303,9 +293,9 @@ public void CopyFrom(Account from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -341,7 +331,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -371,31 +361,27 @@ public override bool IsInitial partial void OnAfterCopyFrom(Account from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class AccountCollection : EntityBaseCollection { - #region Constructors - /// /// Initializes a new instance of the class. /// - public AccountCollection(){ } + public AccountCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public AccountCollection(IEnumerable entities) => AddRange(entities); - #endregion - - #region ICloneable - /// /// Creates a deep copy of the . /// @@ -403,31 +389,29 @@ public AccountCollection(){ } public override object Clone() { var clone = new AccountCollection(); - foreach (Account item in this) + foreach (var item in this) { clone.Add((Account)item.Clone()); } return clone; } - - #endregion - - #region Operator /// - /// An implicit cast from a to a . + /// An implicit cast from the to a corresponding . /// /// The . /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] public static implicit operator AccountCollection(AccountCollectionResult result) => result?.Result!; - - #endregion } + #endregion + + #region CollectionResult + /// - /// Represents a collection result. + /// Represents the collection result. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public class AccountCollectionResult : EntityCollectionResult @@ -438,7 +422,7 @@ public class AccountCollectionResult : EntityCollectionResult - /// Initializes a new instance of the class with default . + /// Initializes a new instance of the class with . /// /// The . public AccountCollectionResult(PagingArgs? paging) : base(paging) { } @@ -461,6 +445,8 @@ public override object Clone() return clone; } } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/AccountArgs.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/AccountArgs.cs index e16b298d2..fd663b7e9 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/AccountArgs.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/AccountArgs.cs @@ -79,14 +79,14 @@ public RefDataNamespace.OpenStatus? OpenStatus } /// - /// Gets or sets a value indicating whether Is Owned. + /// Indicates whether Is Owned. /// [JsonProperty("is-owned", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Is Owned")] public bool? IsOwned { get => _isOwned; - set => SetValue(ref _isOwned, value, false, false, nameof(IsOwned)); + set => SetValue(ref _isOwned, value, false, false, nameof(IsOwned)); } #endregion @@ -98,30 +98,24 @@ public bool? IsOwned /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is AccountArgs val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is AccountArgs val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(AccountArgs? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(AccountArgs? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(ProductCategorySid, obj.ProductCategorySid) - && Equals(OpenStatusSid, obj.OpenStatusSid) - && Equals(IsOwned, obj.IsOwned); + return base.Equals((object)value) + && Equals(ProductCategorySid, value.ProductCategorySid) + && Equals(OpenStatusSid, value.OpenStatusSid) + && Equals(IsOwned, value.IsOwned); } /// @@ -141,9 +135,9 @@ public bool Equals(AccountArgs? obj) public static bool operator != (AccountArgs? a, AccountArgs? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -154,7 +148,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -173,8 +167,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(AccountArgs from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); ProductCategorySid = from.ProductCategorySid; @@ -183,9 +177,9 @@ public void CopyFrom(AccountArgs from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -215,7 +209,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -239,7 +233,7 @@ public override bool IsInitial partial void OnAfterCopyFrom(AccountArgs from); #endregion - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/AccountDetail.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/AccountDetail.cs index 705074001..75fcd88e5 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/AccountDetail.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/AccountDetail.cs @@ -22,7 +22,7 @@ namespace Cdr.Banking.Common.Entities /// Represents the Detail entity. /// [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public partial class AccountDetail : Account + public partial class AccountDetail : Account, IEquatable { #region Privates @@ -45,7 +45,7 @@ public partial class AccountDetail : Account public string? Bsb { get => _bsb; - set => SetValue(ref _bsb, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Bsb)); + set => SetValue(ref _bsb, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Bsb)); } /// @@ -56,7 +56,7 @@ public string? Bsb public string? AccountNumber { get => _accountNumber; - set => SetValue(ref _accountNumber, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(AccountNumber)); + set => SetValue(ref _accountNumber, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(AccountNumber)); } /// @@ -67,7 +67,7 @@ public string? AccountNumber public string? BundleName { get => _bundleName; - set => SetValue(ref _bundleName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(BundleName)); + set => SetValue(ref _bundleName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(BundleName)); } /// @@ -93,31 +93,31 @@ public RefDataNamespace.AccountUType? SpecificAccountUType } /// - /// Gets or sets the Term Deposit (see ). + /// Gets or sets the Term Deposit (see ). /// [JsonProperty("termDeposit", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Term Deposit")] public TermDepositAccount? TermDeposit { get => _termDeposit; - set => SetValue(ref _termDeposit, value, false, true, nameof(TermDeposit)); + set => SetValue(ref _termDeposit, value, false, true, nameof(TermDeposit)); } /// - /// Gets or sets the Credit Card (see ). + /// Gets or sets the Credit Card (see ). /// [JsonProperty("creditCard", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Credit Card")] public CreditCardAccount? CreditCard { get => _creditCard; - set => SetValue(ref _creditCard, value, false, true, nameof(CreditCard)); + set => SetValue(ref _creditCard, value, false, true, nameof(CreditCard)); } #endregion #region IChangeTracking - + /// /// Resets the entity state to unchanged by accepting the changes (resets ). /// @@ -148,33 +148,27 @@ public override void TrackChanges() /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is AccountDetail val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is AccountDetail val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(AccountDetail? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(AccountDetail? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(Bsb, obj.Bsb) - && Equals(AccountNumber, obj.AccountNumber) - && Equals(BundleName, obj.BundleName) - && Equals(SpecificAccountUTypeSid, obj.SpecificAccountUTypeSid) - && Equals(TermDeposit, obj.TermDeposit) - && Equals(CreditCard, obj.CreditCard); + return base.Equals((object)value) + && Equals(Bsb, value.Bsb) + && Equals(AccountNumber, value.AccountNumber) + && Equals(BundleName, value.BundleName) + && Equals(SpecificAccountUTypeSid, value.SpecificAccountUTypeSid) + && Equals(TermDeposit, value.TermDeposit) + && Equals(CreditCard, value.CreditCard); } /// @@ -194,9 +188,9 @@ public bool Equals(AccountDetail? obj) public static bool operator != (AccountDetail? a, AccountDetail? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -210,7 +204,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -229,8 +223,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(AccountDetail from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((Account)from); Bsb = from.Bsb; @@ -242,9 +236,9 @@ public void CopyFrom(AccountDetail from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -277,7 +271,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -307,7 +301,7 @@ public override bool IsInitial partial void OnAfterCopyFrom(AccountDetail from); #endregion - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/AccountUType.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/AccountUType.cs index 85286c42a..617b26a06 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/AccountUType.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/AccountUType.cs @@ -33,10 +33,7 @@ public partial class AccountUType : ReferenceDataBaseGuid /// The Id. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator AccountUType(Guid id) - { - return ConvertFromId(id); - } + public static implicit operator AccountUType(Guid id) => ConvertFromId(id); /// /// An implicit cast from a Code to a . @@ -44,13 +41,10 @@ public static implicit operator AccountUType(Guid id) /// The Code. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator AccountUType(string? code) - { - return ConvertFromCode(code); - } + public static implicit operator AccountUType(string? code) => ConvertFromCode(code); #endregion - + #region ICopyFrom /// @@ -69,16 +63,16 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(AccountUType from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((ReferenceDataBaseGuid)from); OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -105,7 +99,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -130,29 +124,29 @@ public override bool IsInitial partial void OnAfterCopyFrom(AccountUType from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class AccountUTypeCollection : ReferenceDataCollectionBase { - #region Constructors - /// /// Initializes a new instance of the class. /// - public AccountUTypeCollection(){ } + public AccountUTypeCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public AccountUTypeCollection(IEnumerable entities) => AddRange(entities); - - #endregion } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/Balance.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/Balance.cs index 9ff028dc9..299fbda76 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/Balance.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/Balance.cs @@ -46,7 +46,7 @@ public partial class Balance : EntityBase, IStringIdentifier, IEquatable _id; - set => SetValue(ref _id, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Id)); + set => SetValue(ref _id, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Id)); } /// @@ -57,7 +57,7 @@ public string? Id public decimal CurrentBalance { get => _currentBalance; - set => SetValue(ref _currentBalance, value, false, false, nameof(CurrentBalance)); + set => SetValue(ref _currentBalance, value, false, false, nameof(CurrentBalance)); } /// @@ -68,7 +68,7 @@ public decimal CurrentBalance public decimal AvailableBalance { get => _availableBalance; - set => SetValue(ref _availableBalance, value, false, false, nameof(AvailableBalance)); + set => SetValue(ref _availableBalance, value, false, false, nameof(AvailableBalance)); } /// @@ -79,7 +79,7 @@ public decimal AvailableBalance public decimal CreditLimit { get => _creditLimit; - set => SetValue(ref _creditLimit, value, false, false, nameof(CreditLimit)); + set => SetValue(ref _creditLimit, value, false, false, nameof(CreditLimit)); } /// @@ -90,7 +90,7 @@ public decimal CreditLimit public decimal AmortisedLimit { get => _amortisedLimit; - set => SetValue(ref _amortisedLimit, value, false, false, nameof(AmortisedLimit)); + set => SetValue(ref _amortisedLimit, value, false, false, nameof(AmortisedLimit)); } /// @@ -101,24 +101,24 @@ public decimal AmortisedLimit public string? Currency { get => _currency; - set => SetValue(ref _currency, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Currency)); + set => SetValue(ref _currency, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Currency)); } /// - /// Gets or sets the Purses (see ). + /// Gets or sets the Purses. /// [JsonProperty("purses", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Purses")] public BalancePurseCollection? Purses { get => _purses; - set => SetValue(ref _purses, value, false, true, nameof(Purses)); + set => SetValue(ref _purses, value, false, true, nameof(Purses)); } #endregion #region IChangeTracking - + /// /// Resets the entity state to unchanged by accepting the changes (resets ). /// @@ -147,34 +147,28 @@ public override void TrackChanges() /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is Balance val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is Balance val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(Balance? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(Balance? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(Id, obj.Id) - && Equals(CurrentBalance, obj.CurrentBalance) - && Equals(AvailableBalance, obj.AvailableBalance) - && Equals(CreditLimit, obj.CreditLimit) - && Equals(AmortisedLimit, obj.AmortisedLimit) - && Equals(Currency, obj.Currency) - && Equals(Purses, obj.Purses); + return base.Equals((object)value) + && Equals(Id, value.Id) + && Equals(CurrentBalance, value.CurrentBalance) + && Equals(AvailableBalance, value.AvailableBalance) + && Equals(CreditLimit, value.CreditLimit) + && Equals(AmortisedLimit, value.AmortisedLimit) + && Equals(Currency, value.Currency) + && Equals(Purses, value.Purses); } /// @@ -194,9 +188,9 @@ public bool Equals(Balance? obj) public static bool operator != (Balance? a, Balance? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -211,7 +205,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -230,8 +224,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(Balance from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); Id = from.Id; @@ -244,9 +238,9 @@ public void CopyFrom(Balance from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -280,7 +274,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -308,7 +302,7 @@ public override bool IsInitial partial void OnAfterCopyFrom(Balance from); #endregion - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/BalancePurse.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/BalancePurse.cs index c7059bb09..b56be20df 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/BalancePurse.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/BalancePurse.cs @@ -41,7 +41,7 @@ public partial class BalancePurse : EntityBase, IEquatable public decimal Amount { get => _amount; - set => SetValue(ref _amount, value, false, false, nameof(Amount)); + set => SetValue(ref _amount, value, false, false, nameof(Amount)); } /// @@ -52,7 +52,7 @@ public decimal Amount public string? Currency { get => _currency; - set => SetValue(ref _currency, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Currency)); + set => SetValue(ref _currency, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Currency)); } #endregion @@ -64,29 +64,23 @@ public string? Currency /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is BalancePurse val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is BalancePurse val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(BalancePurse? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(BalancePurse? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(Amount, obj.Amount) - && Equals(Currency, obj.Currency); + return base.Equals((object)value) + && Equals(Amount, value.Amount) + && Equals(Currency, value.Currency); } /// @@ -106,9 +100,9 @@ public bool Equals(BalancePurse? obj) public static bool operator != (BalancePurse? a, BalancePurse? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -118,7 +112,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -137,8 +131,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(BalancePurse from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); Amount = from.Amount; @@ -146,9 +140,9 @@ public void CopyFrom(BalancePurse from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -177,7 +171,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -200,31 +194,27 @@ public override bool IsInitial partial void OnAfterCopyFrom(BalancePurse from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class BalancePurseCollection : EntityBaseCollection { - #region Constructors - /// /// Initializes a new instance of the class. /// - public BalancePurseCollection(){ } + public BalancePurseCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public BalancePurseCollection(IEnumerable entities) => AddRange(entities); - #endregion - - #region ICloneable - /// /// Creates a deep copy of the . /// @@ -232,16 +222,16 @@ public BalancePurseCollection(){ } public override object Clone() { var clone = new BalancePurseCollection(); - foreach (BalancePurse item in this) + foreach (var item in this) { clone.Add((BalancePurse)item.Clone()); } return clone; } - - #endregion } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/CreditCardAccount.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/CreditCardAccount.cs index f1aa9969e..006a7fd9c 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/CreditCardAccount.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/CreditCardAccount.cs @@ -43,7 +43,7 @@ public partial class CreditCardAccount : EntityBase, IEquatable _minPaymentAmount; - set => SetValue(ref _minPaymentAmount, value, false, false, nameof(MinPaymentAmount)); + set => SetValue(ref _minPaymentAmount, value, false, false, nameof(MinPaymentAmount)); } /// @@ -54,7 +54,7 @@ public decimal MinPaymentAmount public decimal PaymentDueAmount { get => _paymentDueAmount; - set => SetValue(ref _paymentDueAmount, value, false, false, nameof(PaymentDueAmount)); + set => SetValue(ref _paymentDueAmount, value, false, false, nameof(PaymentDueAmount)); } /// @@ -65,7 +65,7 @@ public decimal PaymentDueAmount public string? PaymentCurrency { get => _paymentCurrency; - set => SetValue(ref _paymentCurrency, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(PaymentCurrency)); + set => SetValue(ref _paymentCurrency, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(PaymentCurrency)); } /// @@ -73,11 +73,10 @@ public string? PaymentCurrency /// [JsonProperty("paymentDueDate", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Payment Due Date")] - [DisplayFormat(DataFormatString = Beef.Entities.StringFormat.DateOnlyFormat)] public DateTime PaymentDueDate { get => _paymentDueDate; - set => SetValue(ref _paymentDueDate, value, false, DateTimeTransform.DateOnly, nameof(PaymentDueDate)); + set => SetValue(ref _paymentDueDate, value, false, DateTimeTransform.DateOnly, nameof(PaymentDueDate)); } #endregion @@ -89,31 +88,25 @@ public DateTime PaymentDueDate /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is CreditCardAccount val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is CreditCardAccount val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(CreditCardAccount? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(CreditCardAccount? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(MinPaymentAmount, obj.MinPaymentAmount) - && Equals(PaymentDueAmount, obj.PaymentDueAmount) - && Equals(PaymentCurrency, obj.PaymentCurrency) - && Equals(PaymentDueDate, obj.PaymentDueDate); + return base.Equals((object)value) + && Equals(MinPaymentAmount, value.MinPaymentAmount) + && Equals(PaymentDueAmount, value.PaymentDueAmount) + && Equals(PaymentCurrency, value.PaymentCurrency) + && Equals(PaymentDueDate, value.PaymentDueDate); } /// @@ -133,9 +126,9 @@ public bool Equals(CreditCardAccount? obj) public static bool operator != (CreditCardAccount? a, CreditCardAccount? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -147,7 +140,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -166,8 +159,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(CreditCardAccount from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); MinPaymentAmount = from.MinPaymentAmount; @@ -177,9 +170,9 @@ public void CopyFrom(CreditCardAccount from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -210,7 +203,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -235,7 +228,7 @@ public override bool IsInitial partial void OnAfterCopyFrom(CreditCardAccount from); #endregion - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/IReferenceData.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/IReferenceData.cs index 8548d0051..d7ddcbc63 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/IReferenceData.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/IReferenceData.cs @@ -14,7 +14,7 @@ namespace Cdr.Banking.Common.Entities /// /// Provides for the required ReferenceData capabilities. /// - public interface IReferenceData : IReferenceDataProvider + public partial interface IReferenceData : IReferenceDataProvider { #region Collections diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/MaturityInstructions.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/MaturityInstructions.cs index 702d9067a..327bab0f2 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/MaturityInstructions.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/MaturityInstructions.cs @@ -33,10 +33,7 @@ public partial class MaturityInstructions : ReferenceDataBaseGuid /// The Id. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator MaturityInstructions(Guid id) - { - return ConvertFromId(id); - } + public static implicit operator MaturityInstructions(Guid id) => ConvertFromId(id); /// /// An implicit cast from a Code to a . @@ -44,13 +41,10 @@ public static implicit operator MaturityInstructions(Guid id) /// The Code. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator MaturityInstructions(string? code) - { - return ConvertFromCode(code); - } + public static implicit operator MaturityInstructions(string? code) => ConvertFromCode(code); #endregion - + #region ICopyFrom /// @@ -69,16 +63,16 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(MaturityInstructions from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((ReferenceDataBaseGuid)from); OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -105,7 +99,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -130,29 +124,29 @@ public override bool IsInitial partial void OnAfterCopyFrom(MaturityInstructions from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class MaturityInstructionsCollection : ReferenceDataCollectionBase { - #region Constructors - /// /// Initializes a new instance of the class. /// - public MaturityInstructionsCollection(){ } + public MaturityInstructionsCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public MaturityInstructionsCollection(IEnumerable entities) => AddRange(entities); - - #endregion } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/OpenStatus.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/OpenStatus.cs index 1980a5636..bb9536f34 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/OpenStatus.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/OpenStatus.cs @@ -42,10 +42,7 @@ public partial class OpenStatus : ReferenceDataBaseGuid /// The Id. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator OpenStatus(Guid id) - { - return ConvertFromId(id); - } + public static implicit operator OpenStatus(Guid id) => ConvertFromId(id); /// /// An implicit cast from a Code to a . @@ -53,13 +50,10 @@ public static implicit operator OpenStatus(Guid id) /// The Code. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator OpenStatus(string? code) - { - return ConvertFromCode(code); - } + public static implicit operator OpenStatus(string? code) => ConvertFromCode(code); #endregion - + #region ICopyFrom /// @@ -78,16 +72,16 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(OpenStatus from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((ReferenceDataBaseGuid)from); OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -114,7 +108,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -139,29 +133,29 @@ public override bool IsInitial partial void OnAfterCopyFrom(OpenStatus from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class OpenStatusCollection : ReferenceDataCollectionBase { - #region Constructors - /// /// Initializes a new instance of the class. /// - public OpenStatusCollection(){ } + public OpenStatusCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public OpenStatusCollection(IEnumerable entities) => AddRange(entities); - - #endregion } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/ProductCategory.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/ProductCategory.cs index 53fce3460..418731d91 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/ProductCategory.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/ProductCategory.cs @@ -33,10 +33,7 @@ public partial class ProductCategory : ReferenceDataBaseGuid /// The Id. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator ProductCategory(Guid id) - { - return ConvertFromId(id); - } + public static implicit operator ProductCategory(Guid id) => ConvertFromId(id); /// /// An implicit cast from a Code to a . @@ -44,13 +41,10 @@ public static implicit operator ProductCategory(Guid id) /// The Code. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator ProductCategory(string? code) - { - return ConvertFromCode(code); - } + public static implicit operator ProductCategory(string? code) => ConvertFromCode(code); #endregion - + #region ICopyFrom /// @@ -69,16 +63,16 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(ProductCategory from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((ReferenceDataBaseGuid)from); OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -105,7 +99,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -130,29 +124,29 @@ public override bool IsInitial partial void OnAfterCopyFrom(ProductCategory from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class ProductCategoryCollection : ReferenceDataCollectionBase { - #region Constructors - /// /// Initializes a new instance of the class. /// - public ProductCategoryCollection(){ } + public ProductCategoryCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public ProductCategoryCollection(IEnumerable entities) => AddRange(entities); - - #endregion } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/ReferenceData.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/ReferenceData.cs index 4beea3e24..8986e51b4 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/ReferenceData.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/ReferenceData.cs @@ -35,7 +35,7 @@ public abstract partial class ReferenceData : IReferenceData typeof(TransactionType), typeof(TransactionStatus) }; - + /// /// Gets the provider interface cref="Type"/> used for . The value is . /// diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TermDepositAccount.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TermDepositAccount.cs index c02bd1f94..e3e37d4d5 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TermDepositAccount.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TermDepositAccount.cs @@ -41,11 +41,10 @@ public partial class TermDepositAccount : EntityBase, IEquatable [JsonProperty("lodgementDate", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Lodgement Date")] - [DisplayFormat(DataFormatString = Beef.Entities.StringFormat.DateOnlyFormat)] public DateTime LodgementDate { get => _lodgementDate; - set => SetValue(ref _lodgementDate, value, false, DateTimeTransform.DateOnly, nameof(LodgementDate)); + set => SetValue(ref _lodgementDate, value, false, DateTimeTransform.DateOnly, nameof(LodgementDate)); } /// @@ -53,11 +52,10 @@ public DateTime LodgementDate /// [JsonProperty("maturityDate", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Maturity Date")] - [DisplayFormat(DataFormatString = Beef.Entities.StringFormat.DateOnlyFormat)] public DateTime MaturityDate { get => _maturityDate; - set => SetValue(ref _maturityDate, value, false, DateTimeTransform.DateOnly, nameof(MaturityDate)); + set => SetValue(ref _maturityDate, value, false, DateTimeTransform.DateOnly, nameof(MaturityDate)); } /// @@ -68,7 +66,7 @@ public DateTime MaturityDate public decimal MaturityAmount { get => _maturityAmount; - set => SetValue(ref _maturityAmount, value, false, false, nameof(MaturityAmount)); + set => SetValue(ref _maturityAmount, value, false, false, nameof(MaturityAmount)); } /// @@ -79,7 +77,7 @@ public decimal MaturityAmount public string? MaturityCurrency { get => _maturityCurrency; - set => SetValue(ref _maturityCurrency, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(MaturityCurrency)); + set => SetValue(ref _maturityCurrency, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(MaturityCurrency)); } /// @@ -113,32 +111,26 @@ public RefDataNamespace.MaturityInstructions? MaturityInstructions /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is TermDepositAccount val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is TermDepositAccount val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(TermDepositAccount? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(TermDepositAccount? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(LodgementDate, obj.LodgementDate) - && Equals(MaturityDate, obj.MaturityDate) - && Equals(MaturityAmount, obj.MaturityAmount) - && Equals(MaturityCurrency, obj.MaturityCurrency) - && Equals(MaturityInstructionsSid, obj.MaturityInstructionsSid); + return base.Equals((object)value) + && Equals(LodgementDate, value.LodgementDate) + && Equals(MaturityDate, value.MaturityDate) + && Equals(MaturityAmount, value.MaturityAmount) + && Equals(MaturityCurrency, value.MaturityCurrency) + && Equals(MaturityInstructionsSid, value.MaturityInstructionsSid); } /// @@ -158,9 +150,9 @@ public bool Equals(TermDepositAccount? obj) public static bool operator != (TermDepositAccount? a, TermDepositAccount? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -173,7 +165,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -192,8 +184,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(TermDepositAccount from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); LodgementDate = from.LodgementDate; @@ -204,9 +196,9 @@ public void CopyFrom(TermDepositAccount from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -238,7 +230,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -264,7 +256,7 @@ public override bool IsInitial partial void OnAfterCopyFrom(TermDepositAccount from); #endregion - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/Transaction.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/Transaction.cs index 3b06e6402..d836d0b91 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/Transaction.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/Transaction.cs @@ -50,12 +50,12 @@ public partial class Transaction : EntityBase, IStringIdentifier, IEquatable /// Gets or sets the identifier. /// - [JsonProperty("transactionId", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("transactionId", DefaultValueHandling = DefaultValueHandling.Include)] [Display(Name="Identifier")] public string? Id { get => _id; - set => SetValue(ref _id, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Id)); + set => SetValue(ref _id, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Id)); } /// @@ -66,18 +66,18 @@ public string? Id public string? AccountId { get => _accountId; - set => SetValue(ref _accountId, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(AccountId)); + set => SetValue(ref _accountId, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(AccountId)); } /// - /// Gets or sets a value indicating whether Is Detail Available. + /// Indicates whether Is Detail Available. /// [JsonProperty("isDetailAvailable", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Is Detail Available")] public bool IsDetailAvailable { get => _isDetailAvailable; - set => SetValue(ref _isDetailAvailable, value, false, false, nameof(IsDetailAvailable)); + set => SetValue(ref _isDetailAvailable, value, false, false, nameof(IsDetailAvailable)); } /// @@ -132,7 +132,7 @@ public RefDataNamespace.TransactionStatus? Status public string? Description { get => _description; - set => SetValue(ref _description, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Description)); + set => SetValue(ref _description, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Description)); } /// @@ -140,11 +140,10 @@ public string? Description /// [JsonProperty("postingDateTime", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Posting Date Time")] - [DisplayFormat(DataFormatString = Beef.Entities.StringFormat.DateTimeFormat)] public DateTime PostingDateTime { get => _postingDateTime; - set => SetValue(ref _postingDateTime, value, false, DateTimeTransform.UseDefault, nameof(PostingDateTime)); + set => SetValue(ref _postingDateTime, value, false, DateTimeTransform.UseDefault, nameof(PostingDateTime)); } /// @@ -152,11 +151,10 @@ public DateTime PostingDateTime /// [JsonProperty("executionDateTime", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Execution Date Time")] - [DisplayFormat(DataFormatString = Beef.Entities.StringFormat.DateTimeFormat)] public DateTime ExecutionDateTime { get => _executionDateTime; - set => SetValue(ref _executionDateTime, value, false, DateTimeTransform.UseDefault, nameof(ExecutionDateTime)); + set => SetValue(ref _executionDateTime, value, false, DateTimeTransform.UseDefault, nameof(ExecutionDateTime)); } /// @@ -167,7 +165,7 @@ public DateTime ExecutionDateTime public decimal Amount { get => _amount; - set => SetValue(ref _amount, value, false, false, nameof(Amount)); + set => SetValue(ref _amount, value, false, false, nameof(Amount)); } /// @@ -178,7 +176,7 @@ public decimal Amount public string? Currency { get => _currency; - set => SetValue(ref _currency, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Currency)); + set => SetValue(ref _currency, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Currency)); } /// @@ -189,7 +187,7 @@ public string? Currency public string? Reference { get => _reference; - set => SetValue(ref _reference, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Reference)); + set => SetValue(ref _reference, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Reference)); } /// @@ -200,7 +198,7 @@ public string? Reference public string? MerchantName { get => _merchantName; - set => SetValue(ref _merchantName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(MerchantName)); + set => SetValue(ref _merchantName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(MerchantName)); } /// @@ -211,7 +209,7 @@ public string? MerchantName public string? MerchantCategoryCode { get => _merchantCategoryCode; - set => SetValue(ref _merchantCategoryCode, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(MerchantCategoryCode)); + set => SetValue(ref _merchantCategoryCode, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(MerchantCategoryCode)); } /// @@ -222,7 +220,7 @@ public string? MerchantCategoryCode public string? BillerCode { get => _billerCode; - set => SetValue(ref _billerCode, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(BillerCode)); + set => SetValue(ref _billerCode, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(BillerCode)); } /// @@ -233,7 +231,7 @@ public string? BillerCode public string? BillerName { get => _billerName; - set => SetValue(ref _billerName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(BillerName)); + set => SetValue(ref _billerName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(BillerName)); } /// @@ -244,13 +242,13 @@ public string? BillerName public string? ApcaNumber { get => _apcaNumber; - set => SetValue(ref _apcaNumber, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ApcaNumber)); + set => SetValue(ref _apcaNumber, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ApcaNumber)); } #endregion - #region UniqueKey - + #region IUniqueKey + /// /// Indicates whether the has a value. /// @@ -260,20 +258,17 @@ public string? ApcaNumber /// Gets the list of property names that represent the unique key. /// public override string[] UniqueKeyProperties => new string[] { nameof(Id) }; - + /// /// Creates the . /// /// The . /// The . - public static UniqueKey CreateUniqueKey(string id) => new UniqueKey(id); - + public static UniqueKey CreateUniqueKey(string? id) => new UniqueKey(id); + /// - /// Gets the . + /// Gets the (consists of the following property(s): ). /// - /// - /// The UniqueKey key consists of the following property(s): . - /// public override UniqueKey UniqueKey => new UniqueKey(Id); #endregion @@ -285,43 +280,37 @@ public string? ApcaNumber /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is Transaction val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is Transaction val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(Transaction? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(Transaction? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(Id, obj.Id) - && Equals(AccountId, obj.AccountId) - && Equals(IsDetailAvailable, obj.IsDetailAvailable) - && Equals(TypeSid, obj.TypeSid) - && Equals(StatusSid, obj.StatusSid) - && Equals(Description, obj.Description) - && Equals(PostingDateTime, obj.PostingDateTime) - && Equals(ExecutionDateTime, obj.ExecutionDateTime) - && Equals(Amount, obj.Amount) - && Equals(Currency, obj.Currency) - && Equals(Reference, obj.Reference) - && Equals(MerchantName, obj.MerchantName) - && Equals(MerchantCategoryCode, obj.MerchantCategoryCode) - && Equals(BillerCode, obj.BillerCode) - && Equals(BillerName, obj.BillerName) - && Equals(ApcaNumber, obj.ApcaNumber); + return base.Equals((object)value) + && Equals(Id, value.Id) + && Equals(AccountId, value.AccountId) + && Equals(IsDetailAvailable, value.IsDetailAvailable) + && Equals(TypeSid, value.TypeSid) + && Equals(StatusSid, value.StatusSid) + && Equals(Description, value.Description) + && Equals(PostingDateTime, value.PostingDateTime) + && Equals(ExecutionDateTime, value.ExecutionDateTime) + && Equals(Amount, value.Amount) + && Equals(Currency, value.Currency) + && Equals(Reference, value.Reference) + && Equals(MerchantName, value.MerchantName) + && Equals(MerchantCategoryCode, value.MerchantCategoryCode) + && Equals(BillerCode, value.BillerCode) + && Equals(BillerName, value.BillerName) + && Equals(ApcaNumber, value.ApcaNumber); } /// @@ -341,9 +330,9 @@ public bool Equals(Transaction? obj) public static bool operator != (Transaction? a, Transaction? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -367,7 +356,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -386,8 +375,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(Transaction from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); Id = from.Id; @@ -409,9 +398,9 @@ public void CopyFrom(Transaction from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -454,7 +443,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -491,31 +480,27 @@ public override bool IsInitial partial void OnAfterCopyFrom(Transaction from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class TransactionCollection : EntityBaseCollection { - #region Constructors - /// /// Initializes a new instance of the class. /// - public TransactionCollection(){ } + public TransactionCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public TransactionCollection(IEnumerable entities) => AddRange(entities); - #endregion - - #region ICloneable - /// /// Creates a deep copy of the . /// @@ -523,31 +508,29 @@ public TransactionCollection(){ } public override object Clone() { var clone = new TransactionCollection(); - foreach (Transaction item in this) + foreach (var item in this) { clone.Add((Transaction)item.Clone()); } return clone; } - - #endregion - - #region Operator /// - /// An implicit cast from a to a . + /// An implicit cast from the to a corresponding . /// /// The . /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] public static implicit operator TransactionCollection(TransactionCollectionResult result) => result?.Result!; - - #endregion } + #endregion + + #region CollectionResult + /// - /// Represents a collection result. + /// Represents the collection result. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public class TransactionCollectionResult : EntityCollectionResult @@ -558,7 +541,7 @@ public class TransactionCollectionResult : EntityCollectionResult - /// Initializes a new instance of the class with default . + /// Initializes a new instance of the class with . /// /// The . public TransactionCollectionResult(PagingArgs? paging) : base(paging) { } @@ -581,6 +564,8 @@ public override object Clone() return clone; } } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TransactionArgs.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TransactionArgs.cs index fd732f6b9..8f3183e60 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TransactionArgs.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TransactionArgs.cs @@ -41,11 +41,10 @@ public partial class TransactionArgs : EntityBase, IEquatable /// [JsonProperty("oldest-time", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Oldest time")] - [DisplayFormat(DataFormatString = Beef.Entities.StringFormat.DateTimeFormat)] public DateTime? FromDate { get => _fromDate; - set => SetValue(ref _fromDate, value, false, DateTimeTransform.UseDefault, nameof(FromDate)); + set => SetValue(ref _fromDate, value, false, DateTimeTransform.UseDefault, nameof(FromDate)); } /// @@ -53,11 +52,10 @@ public DateTime? FromDate /// [JsonProperty("newest-time", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Newest time")] - [DisplayFormat(DataFormatString = Beef.Entities.StringFormat.DateTimeFormat)] public DateTime? ToDate { get => _toDate; - set => SetValue(ref _toDate, value, false, DateTimeTransform.UseDefault, nameof(ToDate)); + set => SetValue(ref _toDate, value, false, DateTimeTransform.UseDefault, nameof(ToDate)); } /// @@ -68,7 +66,7 @@ public DateTime? ToDate public decimal? MinAmount { get => _minAmount; - set => SetValue(ref _minAmount, value, false, false, nameof(MinAmount)); + set => SetValue(ref _minAmount, value, false, false, nameof(MinAmount)); } /// @@ -79,7 +77,7 @@ public decimal? MinAmount public decimal? MaxAmount { get => _maxAmount; - set => SetValue(ref _maxAmount, value, false, false, nameof(MaxAmount)); + set => SetValue(ref _maxAmount, value, false, false, nameof(MaxAmount)); } /// @@ -90,7 +88,7 @@ public decimal? MaxAmount public string? Text { get => _text; - set => SetValue(ref _text, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Text)); + set => SetValue(ref _text, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Text)); } #endregion @@ -102,32 +100,26 @@ public string? Text /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is TransactionArgs val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is TransactionArgs val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(TransactionArgs? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(TransactionArgs? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(FromDate, obj.FromDate) - && Equals(ToDate, obj.ToDate) - && Equals(MinAmount, obj.MinAmount) - && Equals(MaxAmount, obj.MaxAmount) - && Equals(Text, obj.Text); + return base.Equals((object)value) + && Equals(FromDate, value.FromDate) + && Equals(ToDate, value.ToDate) + && Equals(MinAmount, value.MinAmount) + && Equals(MaxAmount, value.MaxAmount) + && Equals(Text, value.Text); } /// @@ -147,9 +139,9 @@ public bool Equals(TransactionArgs? obj) public static bool operator != (TransactionArgs? a, TransactionArgs? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -162,7 +154,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -181,8 +173,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(TransactionArgs from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); FromDate = from.FromDate; @@ -193,9 +185,9 @@ public void CopyFrom(TransactionArgs from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -227,7 +219,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -253,7 +245,7 @@ public override bool IsInitial partial void OnAfterCopyFrom(TransactionArgs from); #endregion - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TransactionStatus.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TransactionStatus.cs index cf1419fab..12622c57a 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TransactionStatus.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TransactionStatus.cs @@ -33,10 +33,7 @@ public partial class TransactionStatus : ReferenceDataBaseGuid /// The Id. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator TransactionStatus(Guid id) - { - return ConvertFromId(id); - } + public static implicit operator TransactionStatus(Guid id) => ConvertFromId(id); /// /// An implicit cast from a Code to a . @@ -44,13 +41,10 @@ public static implicit operator TransactionStatus(Guid id) /// The Code. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator TransactionStatus(string? code) - { - return ConvertFromCode(code); - } + public static implicit operator TransactionStatus(string? code) => ConvertFromCode(code); #endregion - + #region ICopyFrom /// @@ -69,16 +63,16 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(TransactionStatus from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((ReferenceDataBaseGuid)from); OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -105,7 +99,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -130,29 +124,29 @@ public override bool IsInitial partial void OnAfterCopyFrom(TransactionStatus from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class TransactionStatusCollection : ReferenceDataCollectionBase { - #region Constructors - /// /// Initializes a new instance of the class. /// - public TransactionStatusCollection(){ } + public TransactionStatusCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public TransactionStatusCollection(IEnumerable entities) => AddRange(entities); - - #endregion } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TransactionType.cs b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TransactionType.cs index 3579f009f..be54a60fd 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TransactionType.cs +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Entities/Generated/TransactionType.cs @@ -33,10 +33,7 @@ public partial class TransactionType : ReferenceDataBaseGuid /// The Id. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator TransactionType(Guid id) - { - return ConvertFromId(id); - } + public static implicit operator TransactionType(Guid id) => ConvertFromId(id); /// /// An implicit cast from a Code to a . @@ -44,13 +41,10 @@ public static implicit operator TransactionType(Guid id) /// The Code. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator TransactionType(string? code) - { - return ConvertFromCode(code); - } + public static implicit operator TransactionType(string? code) => ConvertFromCode(code); #endregion - + #region ICopyFrom /// @@ -69,16 +63,16 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(TransactionType from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((ReferenceDataBaseGuid)from); OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -105,7 +99,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -130,29 +124,29 @@ public override bool IsInitial partial void OnAfterCopyFrom(TransactionType from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class TransactionTypeCollection : ReferenceDataCollectionBase { - #region Constructors - /// /// Initializes a new instance of the class. /// - public TransactionTypeCollection(){ } + public TransactionTypeCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public TransactionTypeCollection(IEnumerable entities) => AddRange(entities); - - #endregion } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Api/Controllers/Generated/ConfigController.cs b/samples/Demo/Beef.Demo.Api/Controllers/Generated/ConfigController.cs index 4e724e82e..ed336a9b2 100644 --- a/samples/Demo/Beef.Demo.Api/Controllers/Generated/ConfigController.cs +++ b/samples/Demo/Beef.Demo.Api/Controllers/Generated/ConfigController.cs @@ -23,22 +23,27 @@ namespace Beef.Demo.Api.Controllers /// /// Provides the Config Web API functionality. /// + [AllowAnonymous] [Route("api/v1/envvars")] public partial class ConfigController : ControllerBase { private readonly IConfigManager _manager; - + /// /// Initializes a new instance of the class. /// /// The . - public ConfigController(IConfigManager manager) => _manager = Check.NotNull(manager, nameof(manager)); + public ConfigController(IConfigManager manager) + { _manager = Check.NotNull(manager, nameof(manager)); ConfigControllerCtor(); } + + partial void ConfigControllerCtor(); // Enables additional functionality to be added to the constructor. /// /// Get Env Vars. /// /// A resultant . - [HttpPost] + [AllowAnonymous] + [HttpPost("")] [ProducesResponseType(typeof(System.Collections.IDictionary), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult GetEnvVars() diff --git a/samples/Demo/Beef.Demo.Api/Controllers/Generated/ContactController.cs b/samples/Demo/Beef.Demo.Api/Controllers/Generated/ContactController.cs index 6d1d6d784..564bfe327 100644 --- a/samples/Demo/Beef.Demo.Api/Controllers/Generated/ContactController.cs +++ b/samples/Demo/Beef.Demo.Api/Controllers/Generated/ContactController.cs @@ -21,25 +21,29 @@ namespace Beef.Demo.Api.Controllers { /// - /// Provides the Contact Web API functionality. + /// Provides the Web API functionality. /// + [AllowAnonymous] [Route("api/v1/contacts")] public partial class ContactController : ControllerBase { private readonly IContactManager _manager; - + /// /// Initializes a new instance of the class. /// /// The . - public ContactController(IContactManager manager) => _manager = Check.NotNull(manager, nameof(manager)); + public ContactController(IContactManager manager) + { _manager = Check.NotNull(manager, nameof(manager)); ContactControllerCtor(); } + + partial void ContactControllerCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the collection entity that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . - [HttpGet()] - [Route("")] + /// The + [AllowAnonymous] + [HttpGet("")] [ProducesResponseType(typeof(ContactCollection), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult GetAll() @@ -49,12 +53,12 @@ public IActionResult GetAll() } /// - /// Gets the entity that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected entity where found. - [HttpGet()] - [Route("{id}")] + /// The selected where found. + [AllowAnonymous] + [HttpGet("{id}")] [ProducesResponseType(typeof(Contact), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult Get(Guid id) @@ -64,12 +68,12 @@ public IActionResult Get(Guid id) } /// - /// Creates the entity. + /// Creates a new . /// - /// The entity. - /// The created entity. - [HttpPost()] - [Route("")] + /// The . + /// The created . + [AllowAnonymous] + [HttpPost("")] [ProducesResponseType(typeof(Contact), (int)HttpStatusCode.Created)] public IActionResult Create([FromBody] Contact value) { @@ -78,13 +82,13 @@ public IActionResult Create([FromBody] Contact value) } /// - /// Updates the entity. + /// Updates an existing . /// - /// The entity. + /// The . /// The identifier. - /// The updated entity. - [HttpPut()] - [Route("{id}")] + /// The updated . + [AllowAnonymous] + [HttpPut("{id}")] [ProducesResponseType(typeof(Contact), (int)HttpStatusCode.OK)] public IActionResult Update([FromBody] Contact value, Guid id) { @@ -93,11 +97,11 @@ public IActionResult Update([FromBody] Contact value, Guid id) } /// - /// Deletes the entity that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. - [HttpDelete()] - [Route("{id}")] + [AllowAnonymous] + [HttpDelete("{id}")] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult Delete(Guid id) { diff --git a/samples/Demo/Beef.Demo.Api/Controllers/Generated/GenderController.cs b/samples/Demo/Beef.Demo.Api/Controllers/Generated/GenderController.cs index 547e2ff25..5c6450d34 100644 --- a/samples/Demo/Beef.Demo.Api/Controllers/Generated/GenderController.cs +++ b/samples/Demo/Beef.Demo.Api/Controllers/Generated/GenderController.cs @@ -21,26 +21,30 @@ namespace Beef.Demo.Api.Controllers { /// - /// Provides the Gender Web API functionality. + /// Provides the Web API functionality. /// + [AllowAnonymous] [Route("api/v1/demo/ref/genders")] public partial class GenderController : ControllerBase { private readonly IGenderManager _manager; - + /// /// Initializes a new instance of the class. /// /// The . - public GenderController(IGenderManager manager) => _manager = Check.NotNull(manager, nameof(manager)); + public GenderController(IGenderManager manager) + { _manager = Check.NotNull(manager, nameof(manager)); GenderControllerCtor(); } + + partial void GenderControllerCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the entity that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected entity where found. - [HttpGet()] - [Route("{id}")] + /// The selected where found. + [AllowAnonymous] + [HttpGet("{id}")] [ProducesResponseType(typeof(Gender), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult Get(Guid id) @@ -50,12 +54,12 @@ public IActionResult Get(Guid id) } /// - /// Creates the entity. + /// Creates a new . /// - /// The entity. - /// The created entity. - [HttpPost()] - [Route("")] + /// The . + /// The created . + [AllowAnonymous] + [HttpPost("")] [ProducesResponseType(typeof(Gender), (int)HttpStatusCode.Created)] public IActionResult Create([FromBody] Gender value) { @@ -64,13 +68,13 @@ public IActionResult Create([FromBody] Gender value) } /// - /// Updates the entity. + /// Updates an existing . /// - /// The entity. + /// The . /// The identifier. - /// The updated entity. - [HttpPut()] - [Route("{id}")] + /// The updated . + [AllowAnonymous] + [HttpPut("{id}")] [ProducesResponseType(typeof(Gender), (int)HttpStatusCode.OK)] public IActionResult Update([FromBody] Gender value, Guid id) { diff --git a/samples/Demo/Beef.Demo.Api/Controllers/Generated/PersonController.cs b/samples/Demo/Beef.Demo.Api/Controllers/Generated/PersonController.cs index 320048175..f8b15f628 100644 --- a/samples/Demo/Beef.Demo.Api/Controllers/Generated/PersonController.cs +++ b/samples/Demo/Beef.Demo.Api/Controllers/Generated/PersonController.cs @@ -21,26 +21,32 @@ namespace Beef.Demo.Api.Controllers { /// - /// Provides the Person Web API functionality. + /// Provides the Web API functionality. /// + [AllowAnonymous] [Route("api/v1/persons")] public partial class PersonController : ControllerBase { private readonly IPersonManager _manager; - + private readonly IPersonManager _personManager; + /// /// Initializes a new instance of the class. /// /// The . - public PersonController(IPersonManager manager) => _manager = Check.NotNull(manager, nameof(manager)); + /// The . + public PersonController(IPersonManager manager, IPersonManager personManager) + { _manager = Check.NotNull(manager, nameof(manager)); _personManager = Check.NotNull(personManager, nameof(personManager)); PersonControllerCtor(); } + + partial void PersonControllerCtor(); // Enables additional functionality to be added to the constructor. /// - /// Creates the entity. + /// Creates a new . /// - /// The entity. - /// The created entity. - [HttpPost()] - [Route("")] + /// The . + /// The created . + [AllowAnonymous] + [HttpPost("")] [ProducesResponseType(typeof(Person), (int)HttpStatusCode.Created)] public IActionResult Create([FromBody] Person value) { @@ -49,11 +55,11 @@ public IActionResult Create([FromBody] Person value) } /// - /// Deletes the entity that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. - [HttpDelete()] - [Route("{id}")] + [AllowAnonymous] + [HttpDelete("{id}")] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult Delete(Guid id) { @@ -62,12 +68,12 @@ public IActionResult Delete(Guid id) } /// - /// Gets the entity that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected entity where found. - [HttpGet()] - [Route("{id}")] + /// The selected where found. + [AllowAnonymous] + [HttpGet("{id}")] [ProducesResponseType(typeof(Person), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult Get(Guid id) @@ -77,13 +83,13 @@ public IActionResult Get(Guid id) } /// - /// Updates the entity. + /// Updates an existing . /// - /// The entity. + /// The . /// The identifier. - /// The updated entity. - [HttpPut()] - [Route("{id}")] + /// The updated . + [AllowAnonymous] + [HttpPut("{id}")] [ProducesResponseType(typeof(Person), (int)HttpStatusCode.OK)] public IActionResult Update([FromBody] Person value, Guid id) { @@ -92,13 +98,13 @@ public IActionResult Update([FromBody] Person value, Guid id) } /// - /// Patches the entity. + /// Patches an existing . /// - /// The value that contains the patch content for the entity. + /// The that contains the patch content for the . /// The identifier. - /// The patched entity. - [HttpPatch()] - [Route("{id}")] + /// The patched . + [AllowAnonymous] + [HttpPatch("{id}")] [ProducesResponseType(typeof(Person), (int)HttpStatusCode.OK)] public IActionResult Patch([FromBody] JToken value, Guid id) { @@ -107,11 +113,11 @@ public IActionResult Patch([FromBody] JToken value, Guid id) } /// - /// Gets the collection entity that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . - [HttpGet()] - [Route("all")] + /// The + [AllowAnonymous] + [HttpGet("all")] [ProducesResponseType(typeof(PersonCollection), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult GetAll() @@ -121,11 +127,11 @@ public IActionResult GetAll() } /// - /// Gets the collection entity that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . - [HttpGet()] - [Route("allnopaging")] + /// The + [AllowAnonymous] + [HttpGet("allnopaging")] [ProducesResponseType(typeof(PersonCollection), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult GetAll2() @@ -135,14 +141,14 @@ public IActionResult GetAll2() } /// - /// Gets the collection entity that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The First Name. /// The Last Name. - /// The Genders (see ). - /// A . - [HttpGet()] - [Route("")] + /// The Genders (see ). + /// The + [AllowAnonymous] + [HttpGet("")] [ProducesResponseType(typeof(PersonCollection), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult GetByArgs(string? firstName = default, string? lastName = default, List? genders = default) @@ -153,14 +159,14 @@ public IActionResult GetByArgs(string? firstName = default, string? lastName = d } /// - /// Gets the collection entity that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The First Name. /// The Last Name. - /// The Genders (see ). - /// A . - [HttpGet()] - [Route("argsdetail")] + /// The Genders (see ). + /// The + [AllowAnonymous] + [HttpGet("argsdetail")] [ProducesResponseType(typeof(PersonDetailCollection), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult GetDetailByArgs(string? firstName = default, string? lastName = default, List? genders = default) @@ -176,8 +182,8 @@ public IActionResult GetDetailByArgs(string? firstName = default, string? lastNa /// The from identifier. /// The to identifier. /// A resultant . - [HttpPost] - [Route("merge")] + [AllowAnonymous] + [HttpPost("merge")] [ProducesResponseType(typeof(Person), (int)HttpStatusCode.OK)] public IActionResult Merge(Guid fromId, Guid toId) { @@ -188,8 +194,8 @@ public IActionResult Merge(Guid fromId, Guid toId) /// /// Mark . /// - [HttpPost] - [Route("mark")] + [AllowAnonymous] + [HttpPost("mark")] [ProducesResponseType((int)HttpStatusCode.Accepted)] public IActionResult Mark() { @@ -200,10 +206,10 @@ public IActionResult Mark() /// /// Get at specified . /// - /// The Coordinates (see ). + /// The Coordinates (see ). /// A resultant . - [HttpPost] - [Route("map")] + [AllowAnonymous] + [HttpPost("map")] [ProducesResponseType(typeof(MapCoordinates), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult Map(string? coordinates = default) @@ -216,9 +222,9 @@ public IActionResult Map(string? coordinates = default) /// /// Get no arguments. /// - /// The selected entity where found. - [HttpGet()] - [Route("noargsforme")] + /// The selected where found. + [AllowAnonymous] + [HttpGet("noargsforme")] [ProducesResponseType(typeof(Person), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult GetNoArgs() @@ -228,12 +234,12 @@ public IActionResult GetNoArgs() } /// - /// Gets the entity that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected entity where found. - [HttpGet()] - [Route("{id}/detail")] + /// The selected where found. + [AllowAnonymous] + [HttpGet("{id}/detail")] [ProducesResponseType(typeof(PersonDetail), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult GetDetail(Guid id) @@ -243,13 +249,13 @@ public IActionResult GetDetail(Guid id) } /// - /// Updates the entity. + /// Updates an existing . /// - /// The entity. + /// The . /// The identifier. - /// The updated entity. - [HttpPut()] - [Route("{id}/detail")] + /// The updated . + [AllowAnonymous] + [HttpPut("{id}/detail")] [ProducesResponseType(typeof(PersonDetail), (int)HttpStatusCode.OK)] public IActionResult UpdateDetail([FromBody] PersonDetail value, Guid id) { @@ -258,28 +264,28 @@ public IActionResult UpdateDetail([FromBody] PersonDetail value, Guid id) } /// - /// Patches the entity. + /// Patches an existing . /// - /// The value that contains the patch content for the entity. + /// The that contains the patch content for the . /// The identifier. - /// The patched entity. - [HttpPatch()] - [Route("{id}/detail")] + /// The patched . + [AllowAnonymous] + [HttpPatch("{id}/detail")] [ProducesResponseType(typeof(PersonDetail), (int)HttpStatusCode.OK)] public IActionResult PatchDetail([FromBody] JToken value, Guid id) { - return new WebApiPatch(this, value, () => _manager.GetDetailAsync(id), (__value) => _manager.UpdateDetailAsync(__value, id), + return new WebApiPatch(this, value, () => _manager.GetDetailAsync(id), (__value) => _personManager.UpdateDetailAsync(__value, id), operationType: OperationType.Update, statusCode: HttpStatusCode.OK, alternateStatusCode: null); } /// /// Actually validating the FromBody parameter generation. /// - /// The Person (see ). - [HttpPost] - [Route("fromBody")] + /// The Person (see ). + [AllowAnonymous] + [HttpPost("fromBody")] [ProducesResponseType((int)HttpStatusCode.Created)] - public IActionResult Add([FromBody] Person? person) + public IActionResult Add([FromBody] Person person) { return new WebApiPost(this, () => _manager.AddAsync(person), operationType: OperationType.Unspecified, statusCode: HttpStatusCode.Created); @@ -289,9 +295,9 @@ public IActionResult Add([FromBody] Person? person) /// Get Null. /// /// The Name. - /// A resultant . - [HttpGet] - [Route("null")] + /// A resultant . + [AllowAnonymous] + [HttpGet("null")] [ProducesResponseType(typeof(Person), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult GetNull(string? name) @@ -301,14 +307,14 @@ public IActionResult GetNull(string? name) } /// - /// Gets the collection entity that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The First Name. /// The Last Name. - /// The Genders (see ). - /// A . - [HttpGet()] - [Route("args")] + /// The Genders (see ). + /// The + [AllowAnonymous] + [HttpGet("args")] [ProducesResponseType(typeof(PersonCollection), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult GetByArgsWithEf(string? firstName = default, string? lastName = default, List? genders = default) @@ -321,8 +327,8 @@ public IActionResult GetByArgsWithEf(string? firstName = default, string? lastNa /// /// Throw Error. /// - [HttpPost] - [Route("error")] + [AllowAnonymous] + [HttpPost("error")] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult ThrowError() { @@ -331,12 +337,12 @@ public IActionResult ThrowError() } /// - /// Gets the entity that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected entity where found. - [HttpGet()] - [Route("ef/{id}")] + /// The selected where found. + [AllowAnonymous] + [HttpGet("ef/{id}")] [ProducesResponseType(typeof(Person), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult GetWithEf(Guid id) @@ -346,12 +352,12 @@ public IActionResult GetWithEf(Guid id) } /// - /// Creates the entity. + /// Creates a new . /// - /// The entity. - /// The created entity. - [HttpPost()] - [Route("ef")] + /// The . + /// The created . + [AllowAnonymous] + [HttpPost("ef")] [ProducesResponseType(typeof(Person), (int)HttpStatusCode.Created)] public IActionResult CreateWithEf([FromBody] Person value) { @@ -360,13 +366,13 @@ public IActionResult CreateWithEf([FromBody] Person value) } /// - /// Updates the entity. + /// Updates an existing . /// - /// The entity. + /// The . /// The identifier. - /// The updated entity. - [HttpPut()] - [Route("ef/{id}")] + /// The updated . + [AllowAnonymous] + [HttpPut("ef/{id}")] [ProducesResponseType(typeof(Person), (int)HttpStatusCode.OK)] public IActionResult UpdateWithEf([FromBody] Person value, Guid id) { @@ -375,11 +381,11 @@ public IActionResult UpdateWithEf([FromBody] Person value, Guid id) } /// - /// Deletes the entity that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. - [HttpDelete()] - [Route("ef/{id}")] + [AllowAnonymous] + [HttpDelete("ef/{id}")] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult DeleteWithEf(Guid id) { @@ -388,13 +394,13 @@ public IActionResult DeleteWithEf(Guid id) } /// - /// Patches the entity. + /// Patches an existing . /// - /// The value that contains the patch content for the entity. + /// The that contains the patch content for the . /// The identifier. - /// The patched entity. - [HttpPatch()] - [Route("ef/{id}")] + /// The patched . + [AllowAnonymous] + [HttpPatch("ef/{id}")] [ProducesResponseType(typeof(Person), (int)HttpStatusCode.OK)] public IActionResult PatchWithEf([FromBody] JToken value, Guid id) { diff --git a/samples/Demo/Beef.Demo.Api/Controllers/Generated/ProductController.cs b/samples/Demo/Beef.Demo.Api/Controllers/Generated/ProductController.cs index 3ad3e0a14..1ec967d90 100644 --- a/samples/Demo/Beef.Demo.Api/Controllers/Generated/ProductController.cs +++ b/samples/Demo/Beef.Demo.Api/Controllers/Generated/ProductController.cs @@ -21,26 +21,30 @@ namespace Beef.Demo.Api.Controllers { /// - /// Provides the Product Web API functionality. + /// Provides the Web API functionality. /// + [AllowAnonymous] [Route("api/v1/products")] public partial class ProductController : ControllerBase { private readonly IProductManager _manager; - + /// /// Initializes a new instance of the class. /// /// The . - public ProductController(IProductManager manager) => _manager = Check.NotNull(manager, nameof(manager)); + public ProductController(IProductManager manager) + { _manager = Check.NotNull(manager, nameof(manager)); ProductControllerCtor(); } + + partial void ProductControllerCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the entity that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected entity where found. - [HttpGet()] - [Route("{id}")] + /// The selected where found. + [AllowAnonymous] + [HttpGet("{id}")] [ProducesResponseType(typeof(Product), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult Get(int id) @@ -50,13 +54,13 @@ public IActionResult Get(int id) } /// - /// Gets the collection entity that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The Name. /// The Description. - /// A . - [HttpGet()] - [Route("")] + /// The + [AllowAnonymous] + [HttpGet("")] [ProducesResponseType(typeof(ProductCollection), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult GetByArgs(string? name = default, string? description = default) diff --git a/samples/Demo/Beef.Demo.Api/Controllers/Generated/ReferenceDataController.cs b/samples/Demo/Beef.Demo.Api/Controllers/Generated/ReferenceDataController.cs index 84a47535d..bfcd8fe2e 100644 --- a/samples/Demo/Beef.Demo.Api/Controllers/Generated/ReferenceDataController.cs +++ b/samples/Demo/Beef.Demo.Api/Controllers/Generated/ReferenceDataController.cs @@ -24,14 +24,16 @@ namespace Beef.Demo.Api.Controllers /// /// Provides the ReferenceData Web API functionality. /// + [AllowAnonymous] public partial class ReferenceDataController : ControllerBase { /// - /// Gets all of the reference data entities that match the specified criteria. + /// Gets all of the reference data items that match the specified criteria. /// /// The reference data code list. /// The reference data text (including wildcards). - /// A collection. + /// A RefDataNamespace.Country collection. + [AllowAnonymous] [HttpGet()] [Route("api/v1/demo/ref/countries")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] @@ -41,11 +43,12 @@ public partial class ReferenceDataController : ControllerBase operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); /// - /// Gets all of the reference data entities that match the specified criteria. + /// Gets all of the reference data items that match the specified criteria. /// /// The reference data code list. /// The reference data text (including wildcards). - /// A collection. + /// A RefDataNamespace.USState collection. + [AllowAnonymous] [HttpGet()] [Route("api/v1/demo/ref/usStates")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] @@ -55,11 +58,12 @@ public partial class ReferenceDataController : ControllerBase operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); /// - /// Gets all of the reference data entities that match the specified criteria. + /// Gets all of the reference data items that match the specified criteria. /// /// The reference data code list. /// The reference data text (including wildcards). - /// A collection. + /// A RefDataNamespace.Gender collection. + [AllowAnonymous] [HttpGet()] [Route("api/v1/demo/ref/genders")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] @@ -69,11 +73,12 @@ public partial class ReferenceDataController : ControllerBase operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); /// - /// Gets all of the reference data entities that match the specified criteria. + /// Gets all of the reference data items that match the specified criteria. /// /// The reference data code list. /// The reference data text (including wildcards). - /// A collection. + /// A RefDataNamespace.EyeColor collection. + [AllowAnonymous] [HttpGet()] [Route("api/v1/demo/ref/eyeColors")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] @@ -83,11 +88,12 @@ public partial class ReferenceDataController : ControllerBase operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); /// - /// Gets all of the reference data entities that match the specified criteria. + /// Gets all of the reference data items that match the specified criteria. /// /// The reference data code list. /// The reference data text (including wildcards). - /// A collection. + /// A RefDataNamespace.PowerSource collection. + [AllowAnonymous] [HttpGet()] [Route("api/v1/demo/ref/powerSources")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] @@ -97,11 +103,12 @@ public partial class ReferenceDataController : ControllerBase operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); /// - /// Gets all of the reference data entities that match the specified criteria. + /// Gets all of the reference data items that match the specified criteria. /// /// The reference data code list. /// The reference data text (including wildcards). - /// A collection. + /// A RefDataNamespace.Company collection. + [AllowAnonymous] [HttpGet()] [Route("api/v1/demo/ref/companies")] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] @@ -111,9 +118,10 @@ public partial class ReferenceDataController : ControllerBase operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); /// - /// Gets the reference data entries for the specified entities and codes from the query string; e.g: api/v1/demo/ref?entity=codeX,codeY&entity2=codeZ&entity3 + /// Gets the reference data entries for the specified entities and codes from the query string; e.g: ?entity=codeX,codeY&entity2=codeZ&entity3 /// /// A . + [AllowAnonymous] [HttpGet()] [Route("api/v1/demo/ref")] [ProducesResponseType(typeof(ReferenceDataMultiCollection), (int)HttpStatusCode.OK)] diff --git a/samples/Demo/Beef.Demo.Api/Controllers/Generated/RobotController.cs b/samples/Demo/Beef.Demo.Api/Controllers/Generated/RobotController.cs index af93005fc..1b1f47039 100644 --- a/samples/Demo/Beef.Demo.Api/Controllers/Generated/RobotController.cs +++ b/samples/Demo/Beef.Demo.Api/Controllers/Generated/RobotController.cs @@ -21,26 +21,30 @@ namespace Beef.Demo.Api.Controllers { /// - /// Provides the Robot Web API functionality. + /// Provides the Web API functionality. /// + [AllowAnonymous] [Route("api/v1/robots")] public partial class RobotController : ControllerBase { private readonly IRobotManager _manager; - + /// /// Initializes a new instance of the class. /// /// The . - public RobotController(IRobotManager manager) => _manager = Check.NotNull(manager, nameof(manager)); + public RobotController(IRobotManager manager) + { _manager = Check.NotNull(manager, nameof(manager)); RobotControllerCtor(); } + + partial void RobotControllerCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the entity that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected entity where found. - [HttpGet()] - [Route("{id}")] + /// The selected where found. + [AllowAnonymous] + [HttpGet("{id}")] [ProducesResponseType(typeof(Robot), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult Get(Guid id) @@ -50,12 +54,12 @@ public IActionResult Get(Guid id) } /// - /// Creates the entity. + /// Creates a new . /// - /// The entity. - /// The created entity. - [HttpPost()] - [Route("")] + /// The . + /// The created . + [AllowAnonymous] + [HttpPost("")] [ProducesResponseType(typeof(Robot), (int)HttpStatusCode.Created)] public IActionResult Create([FromBody] Robot value) { @@ -64,13 +68,13 @@ public IActionResult Create([FromBody] Robot value) } /// - /// Updates the entity. + /// Updates an existing . /// - /// The entity. + /// The . /// The identifier. - /// The updated entity. - [HttpPut()] - [Route("{id}")] + /// The updated . + [AllowAnonymous] + [HttpPut("{id}")] [ProducesResponseType(typeof(Robot), (int)HttpStatusCode.OK)] public IActionResult Update([FromBody] Robot value, Guid id) { @@ -79,13 +83,13 @@ public IActionResult Update([FromBody] Robot value, Guid id) } /// - /// Patches the entity. + /// Patches an existing . /// - /// The value that contains the patch content for the entity. + /// The that contains the patch content for the . /// The identifier. - /// The patched entity. - [HttpPatch()] - [Route("{id}")] + /// The patched . + [AllowAnonymous] + [HttpPatch("{id}")] [ProducesResponseType(typeof(Robot), (int)HttpStatusCode.OK)] public IActionResult Patch([FromBody] JToken value, Guid id) { @@ -94,11 +98,11 @@ public IActionResult Patch([FromBody] JToken value, Guid id) } /// - /// Deletes the entity that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. - [HttpDelete()] - [Route("{id}")] + [AllowAnonymous] + [HttpDelete("{id}")] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult Delete(Guid id) { @@ -107,14 +111,14 @@ public IActionResult Delete(Guid id) } /// - /// Gets the collection entity that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The Model number. /// The Unique serial number. /// The Power Sources (see ). - /// A . - [HttpGet()] - [Route("")] + /// The + [AllowAnonymous] + [HttpGet("")] [ProducesResponseType(typeof(RobotCollection), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult GetByArgs([FromQuery(Name = "model-no")] string? modelNo = default, [FromQuery(Name = "serial-no")] string? serialNo = default, [FromQuery(Name = "power-sources")] List? powerSources = default) @@ -129,8 +133,8 @@ public IActionResult GetByArgs([FromQuery(Name = "model-no")] string? modelNo = /// /// The identifier. /// The Power Source (see ). - [HttpPost] - [Route("{id}/powerSource/{powerSource}")] + [AllowAnonymous] + [HttpPost("{id}/powerSource/{powerSource}")] [ProducesResponseType((int)HttpStatusCode.Accepted)] public IActionResult RaisePowerSourceChange(Guid id, string? powerSource) { diff --git a/samples/Demo/Beef.Demo.Api/Controllers/Generated/TripPersonController.cs b/samples/Demo/Beef.Demo.Api/Controllers/Generated/TripPersonController.cs index 5fc359b39..ae0decabc 100644 --- a/samples/Demo/Beef.Demo.Api/Controllers/Generated/TripPersonController.cs +++ b/samples/Demo/Beef.Demo.Api/Controllers/Generated/TripPersonController.cs @@ -21,26 +21,30 @@ namespace Beef.Demo.Api.Controllers { /// - /// Provides the TripPerson Web API functionality. + /// Provides the Web API functionality. /// + [AllowAnonymous] [Route("api/v1/tripPeople")] public partial class TripPersonController : ControllerBase { private readonly ITripPersonManager _manager; - + /// /// Initializes a new instance of the class. /// /// The . - public TripPersonController(ITripPersonManager manager) => _manager = Check.NotNull(manager, nameof(manager)); + public TripPersonController(ITripPersonManager manager) + { _manager = Check.NotNull(manager, nameof(manager)); TripPersonControllerCtor(); } + + partial void TripPersonControllerCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the entity that matches the selection criteria. + /// Gets the specified . /// /// The identifier (username). - /// The selected entity where found. - [HttpGet()] - [Route("{id}")] + /// The selected where found. + [AllowAnonymous] + [HttpGet("{id}")] [ProducesResponseType(typeof(TripPerson), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NotFound)] public IActionResult Get(string? id) @@ -50,12 +54,12 @@ public IActionResult Get(string? id) } /// - /// Creates the entity. + /// Creates a new . /// - /// The entity. - /// The created entity. - [HttpPost()] - [Route("")] + /// The . + /// The created . + [AllowAnonymous] + [HttpPost("")] [ProducesResponseType(typeof(TripPerson), (int)HttpStatusCode.Created)] public IActionResult Create([FromBody] TripPerson value) { @@ -64,13 +68,13 @@ public IActionResult Create([FromBody] TripPerson value) } /// - /// Updates the entity. + /// Updates an existing . /// - /// The entity. + /// The . /// The identifier (username). - /// The updated entity. - [HttpPut()] - [Route("{id}")] + /// The updated . + [AllowAnonymous] + [HttpPut("{id}")] [ProducesResponseType(typeof(TripPerson), (int)HttpStatusCode.OK)] public IActionResult Update([FromBody] TripPerson value, string? id) { @@ -79,11 +83,11 @@ public IActionResult Update([FromBody] TripPerson value, string? id) } /// - /// Deletes the entity that matches the selection criteria. + /// Deletes the specified . /// /// The identifier (username). - [HttpDelete()] - [Route("{id}")] + [AllowAnonymous] + [HttpDelete("{id}")] [ProducesResponseType((int)HttpStatusCode.NoContent)] public IActionResult Delete(string? id) { diff --git a/samples/Demo/Beef.Demo.Api/Grpc/Generated/RobotService.cs b/samples/Demo/Beef.Demo.Api/Grpc/Generated/RobotService.cs index bbb31ea10..5572c8272 100644 --- a/samples/Demo/Beef.Demo.Api/Grpc/Generated/RobotService.cs +++ b/samples/Demo/Beef.Demo.Api/Grpc/Generated/RobotService.cs @@ -5,36 +5,44 @@ #nullable enable #pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options -using Beef.Demo.Business; -using Beef.Grpc; -using Grpc.Core; using System; using System.Net; using System.Threading.Tasks; +using Beef; +using Beef.Grpc; +using Grpc.Core; +using Microsoft.AspNetCore.Authorization; +using Beef.Demo.Business; using Beef.Demo.Common.Grpc; using Beef.Demo.Common.Grpc.Proto; +using RefDataNamespace = Beef.Demo.Common.Entities; namespace Beef.Demo.Api.Grpc { /// - /// Provides the Robot gRPC Server functionality. + /// Provides the gRPC Server functionality. /// + [AllowAnonymous] public partial class RobotService : RobotGrpcService.RobotGrpcServiceBase { private readonly IRobotManager _manager; - + /// /// Initializes a new instance of the class. /// /// The . - public RobotService(IRobotManager manager) => _manager = Check.NotNull(manager, nameof(manager)); + public RobotService(IRobotManager manager) + { _manager = Check.NotNull(manager, nameof(manager)); ServiceCtor(); } + + partial void ServiceCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the entity that matches the selection criteria. + /// Gets the specified . /// /// The . - /// The . - /// A . + /// The . + /// The . + [AllowAnonymous] public override Task Get(RobotGetRequest request, ServerCallContext context) { return new GrpcService(context, async () => @@ -46,11 +54,12 @@ public override Task Get(RobotGetRequest request, ServerCallContext conte } /// - /// Creates the entity. + /// Creates a new . /// /// The . - /// The . - /// A . + /// The . + /// The . + [AllowAnonymous] public override Task Create(RobotCreateRequest request, ServerCallContext context) { return new GrpcService(context, async () => @@ -62,11 +71,12 @@ public override Task Create(RobotCreateRequest request, ServerCallContext } /// - /// Updates the entity. + /// Updates an existing . /// /// The . - /// The . - /// A . + /// The . + /// The . + [AllowAnonymous] public override Task Update(RobotUpdateRequest request, ServerCallContext context) { return new GrpcService(context, async () => @@ -78,11 +88,12 @@ public override Task Update(RobotUpdateRequest request, ServerCallContext } /// - /// Deletes the entity that matches the selection criteria. + /// Deletes the specified . /// /// The . - /// The . - /// A . + /// The . + /// The . + [AllowAnonymous] public override Task Delete(RobotDeleteRequest request, ServerCallContext context) { return new GrpcService(context, async () => @@ -90,15 +101,16 @@ public override Task Update(RobotUpdateRequest request, ServerCallContext var __req = request ?? new RobotDeleteRequest(); await _manager.DeleteAsync(Transformers.GuidToStringConverter.ConvertToSrce(__req.Id)); return new Google.Protobuf.WellKnownTypes.Empty(); - }, operationType: OperationType.Delete, statusCode: HttpStatusCode.NoContent, alternateStatusCode: null).ExecuteAsync(); + }, operationType: OperationType.Delete, statusCode: HttpStatusCode.NoContent).ExecuteAsync(); } /// - /// Gets the collection entity that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The . - /// The . - /// A . + /// The . + /// The . + [AllowAnonymous] public override Task GetByArgs(RobotGetByArgsRequest request, ServerCallContext context) { return new GrpcService(context, async () => diff --git a/samples/Demo/Beef.Demo.Business/Data/EfDb.cs b/samples/Demo/Beef.Demo.Business/Data/EfDb.cs index ce7c32fa0..7eaa091fa 100644 --- a/samples/Demo/Beef.Demo.Business/Data/EfDb.cs +++ b/samples/Demo/Beef.Demo.Business/Data/EfDb.cs @@ -11,6 +11,6 @@ public class EfDb : EfDbBase /// Initializes a new instance of the class. /// /// The entity framework database context. - public EfDb(EfDbContext dbContext) : base(dbContext) => OnUpdatePreReadForNotFound = true; + public EfDb(EfDbContext dbContext) : base(dbContext) { } } } \ No newline at end of file diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/AddressData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/AddressData.cs index e86ca97be..9731c2c2b 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/AddressData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/AddressData.cs @@ -22,13 +22,14 @@ namespace Beef.Demo.Business.Data { /// - /// Provides the Address data access. + /// Provides the data access. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] public partial class AddressData { + /// - /// Provides the entity and database property mapping. + /// Provides the property and database column mapping. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] public partial class DbMapper : DatabaseMapper @@ -44,10 +45,7 @@ public DbMapper() DbMapperCtor(); } - /// - /// Enables the constructor to be extended. - /// - partial void DbMapperCtor(); + partial void DbMapperCtor(); // Enables the DbMapper constructor to be extended. } } } diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/ContactData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/ContactData.cs index 9f359d6f7..56cf91f12 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/ContactData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/ContactData.cs @@ -22,7 +22,7 @@ namespace Beef.Demo.Business.Data { /// - /// Provides the Contact data access. + /// Provides the data access. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] public partial class ContactData : IContactData @@ -41,17 +41,15 @@ public partial class ContactData : IContactData /// Initializes a new instance of the class. /// /// The . - public ContactData(IEfDb ef) { _ef = Check.NotNull(ef, nameof(ef)); ContactDataCtor(); } + public ContactData(IEfDb ef) + { _ef = Check.NotNull(ef, nameof(ef)); ContactDataCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void ContactDataCtor(); + partial void ContactDataCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . + /// The . public Task GetAllAsync() { return DataInvoker.Current.InvokeAsync(this, async () => @@ -64,10 +62,10 @@ public Task GetAllAsync() } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(Guid id) { return DataInvoker.Current.InvokeAsync(this, async () => @@ -78,41 +76,35 @@ public Task GetAllAsync() } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(Contact value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - return DataInvoker.Current.InvokeAsync(this, async () => { var __dataArgs = EfMapper.Default.CreateArgs(); - return await _ef.CreateAsync(__dataArgs, value).ConfigureAwait(false); + return await _ef.CreateAsync(__dataArgs, Check.NotNull(value, nameof(value))).ConfigureAwait(false); }); } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . public Task UpdateAsync(Contact value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - return DataInvoker.Current.InvokeAsync(this, async () => { var __dataArgs = EfMapper.Default.CreateArgs(); - return await _ef.UpdateAsync(__dataArgs, value).ConfigureAwait(false); + return await _ef.UpdateAsync(__dataArgs, Check.NotNull(value, nameof(value))).ConfigureAwait(false); }); } /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. public Task DeleteAsync(Guid id) @@ -125,7 +117,7 @@ public Task DeleteAsync(Guid id) } /// - /// Provides the entity and Entity Framework property mapping. + /// Provides the and Entity Framework property mapping. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] public partial class EfMapper : EfDbMapper @@ -142,10 +134,7 @@ public EfMapper() EfMapperCtor(); } - /// - /// Enables the constructor to be extended. - /// - partial void EfMapperCtor(); + partial void EfMapperCtor(); // Enables the EfMapper constructor to be extended. } } } diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/GenderData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/GenderData.cs index 39e0b7346..cbb421b26 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/GenderData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/GenderData.cs @@ -22,7 +22,7 @@ namespace Beef.Demo.Business.Data { /// - /// Provides the Gender data access. + /// Provides the data access. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] public partial class GenderData : IGenderData @@ -33,18 +33,16 @@ public partial class GenderData : IGenderData /// Initializes a new instance of the class. /// /// The . - public GenderData(IDatabase db) { _db = Check.NotNull(db, nameof(db)); GenderDataCtor(); } + public GenderData(IDatabase db) + { _db = Check.NotNull(db, nameof(db)); GenderDataCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void GenderDataCtor(); + partial void GenderDataCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(Guid id) { return DataInvoker.Current.InvokeAsync(this, async () => @@ -55,41 +53,35 @@ public partial class GenderData : IGenderData } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(Gender value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - return DataInvoker.Current.InvokeAsync(this, async () => { var __dataArgs = DbMapper.Default.CreateArgs("[Ref].[spGenderCreate]"); - return await _db.CreateAsync(__dataArgs, value).ConfigureAwait(false); + return await _db.CreateAsync(__dataArgs, Check.NotNull(value, nameof(value))).ConfigureAwait(false); }); } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . public Task UpdateAsync(Gender value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - return DataInvoker.Current.InvokeAsync(this, async () => { var __dataArgs = DbMapper.Default.CreateArgs("[Ref].[spGenderUpdate]"); - return await _db.UpdateAsync(__dataArgs, value).ConfigureAwait(false); + return await _db.UpdateAsync(__dataArgs, Check.NotNull(value, nameof(value))).ConfigureAwait(false); }); } /// - /// Provides the entity and database property mapping. + /// Provides the property and database column mapping. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] public partial class DbMapper : DatabaseMapper @@ -110,10 +102,7 @@ public DbMapper() DbMapperCtor(); } - /// - /// Enables the constructor to be extended. - /// - partial void DbMapperCtor(); + partial void DbMapperCtor(); // Enables the DbMapper constructor to be extended. } } } diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/IContactData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/IContactData.cs index 9b0c028c3..a7677f9cb 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/IContactData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/IContactData.cs @@ -17,39 +17,39 @@ namespace Beef.Demo.Business.Data { /// - /// Defines the Contact data access. + /// Defines the data access. /// public partial interface IContactData { /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . + /// The . Task GetAllAsync(); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(Guid id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(Contact value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . Task UpdateAsync(Contact value); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. Task DeleteAsync(Guid id); diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/IGenderData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/IGenderData.cs index cb77b4e2d..2643b1eb9 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/IGenderData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/IGenderData.cs @@ -17,29 +17,29 @@ namespace Beef.Demo.Business.Data { /// - /// Defines the Gender data access. + /// Defines the data access. /// public partial interface IGenderData { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(Guid id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(Gender value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . Task UpdateAsync(Gender value); } } diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/IPersonData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/IPersonData.cs index 37086314f..7080778c7 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/IPersonData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/IPersonData.cs @@ -17,64 +17,64 @@ namespace Beef.Demo.Business.Data { /// - /// Defines the Person data access. + /// Defines the data access. /// public partial interface IPersonData { /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(Person value); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. Task DeleteAsync(Guid id); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(Guid id); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . Task UpdateAsync(Person value); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The . - /// A . + /// The . Task GetAllAsync(PagingArgs? paging); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . + /// The . Task GetAll2Async(); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetByArgsAsync(PersonArgs? args, PagingArgs? paging); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetDetailByArgsAsync(PersonArgs? args, PagingArgs? paging); /// @@ -93,43 +93,43 @@ public partial interface IPersonData /// /// Get at specified . /// - /// The Args (see ). + /// The Args (see ). /// A resultant . Task MapAsync(MapArgs? args); /// /// Get no arguments. /// - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetNoArgsAsync(); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetDetailAsync(Guid id); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . Task UpdateDetailAsync(PersonDetail value); /// /// Get Null. /// /// The Name. - /// A resultant . + /// A resultant . Task GetNullAsync(string? name); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetByArgsWithEfAsync(PersonArgs? args, PagingArgs? paging); /// @@ -138,28 +138,28 @@ public partial interface IPersonData Task ThrowErrorAsync(); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetWithEfAsync(Guid id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateWithEfAsync(Person value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . Task UpdateWithEfAsync(Person value); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. Task DeleteWithEfAsync(Guid id); diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/IProductData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/IProductData.cs index 332097261..5426ae7b3 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/IProductData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/IProductData.cs @@ -17,23 +17,23 @@ namespace Beef.Demo.Business.Data { /// - /// Defines the Product data access. + /// Defines the data access. /// public partial interface IProductData { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(int id); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetByArgsAsync(ProductArgs? args, PagingArgs? paging); } } diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/IReferenceDataData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/IReferenceDataData.cs index 44dd6ba3a..84cb62650 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/IReferenceDataData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/IReferenceDataData.cs @@ -15,44 +15,44 @@ namespace Beef.Demo.Business.Data { /// - /// Provides the database access. + /// Provides the ReferenceData data access. /// public partial interface IReferenceDataData { /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . Task CountryGetAllAsync(); /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . Task USStateGetAllAsync(); /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . Task GenderGetAllAsync(); /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . Task EyeColorGetAllAsync(); /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . Task PowerSourceGetAllAsync(); /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . Task CompanyGetAllAsync(); } } diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/IRobotData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/IRobotData.cs index 68c4bfc1e..75f9d9a47 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/IRobotData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/IRobotData.cs @@ -17,43 +17,43 @@ namespace Beef.Demo.Business.Data { /// - /// Defines the Robot data access. + /// Defines the data access. /// public partial interface IRobotData { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(Guid id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(Robot value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . Task UpdateAsync(Robot value); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. Task DeleteAsync(Guid id); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetByArgsAsync(RobotArgs? args, PagingArgs? paging); } } diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/ITripPersonData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/ITripPersonData.cs index f4027d568..4c2c050cd 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/ITripPersonData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/ITripPersonData.cs @@ -17,33 +17,33 @@ namespace Beef.Demo.Business.Data { /// - /// Defines the Trip Person data access. + /// Defines the data access. /// public partial interface ITripPersonData { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier (username). - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(string? id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(TripPerson value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . Task UpdateAsync(TripPerson value); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier (username). Task DeleteAsync(string? id); diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/PersonData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/PersonData.cs index 973184814..116701b5f 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/PersonData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/PersonData.cs @@ -23,7 +23,7 @@ namespace Beef.Demo.Business.Data { /// - /// Provides the Person data access. + /// Provides the data access. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] public partial class PersonData : IPersonData @@ -37,69 +37,49 @@ public partial class PersonData : IPersonData private Func? _createOnBeforeAsync; private Func? _createOnAfterAsync; private Action? _createOnException; - private Func? _deleteOnBeforeAsync; private Func? _deleteOnAfterAsync; private Action? _deleteOnException; - private Func? _getOnBeforeAsync; private Func? _getOnAfterAsync; private Action? _getOnException; - private Func? _updateOnBeforeAsync; private Func? _updateOnAfterAsync; private Action? _updateOnException; - private Action? _getAllOnQuery; private Func? _getAllOnBeforeAsync; private Func? _getAllOnAfterAsync; private Action? _getAllOnException; - private Action? _getAll2OnQuery; private Func? _getAll2OnBeforeAsync; private Func? _getAll2OnAfterAsync; private Action? _getAll2OnException; - private Action? _getByArgsOnQuery; private Func? _getByArgsOnBeforeAsync; private Func? _getByArgsOnAfterAsync; private Action? _getByArgsOnException; - private Action? _getDetailByArgsOnException; - private Action? _mergeOnException; - private Action? _markOnException; - private Action? _mapOnException; - private Action? _getNoArgsOnException; - private Action? _getDetailOnException; - private Action? _updateDetailOnException; - private Action? _getNullOnException; - private Func, PersonArgs?, IEfDbArgs, IQueryable>? _getByArgsWithEfOnQuery; private Func? _getByArgsWithEfOnBeforeAsync; private Func? _getByArgsWithEfOnAfterAsync; private Action? _getByArgsWithEfOnException; - private Action? _throwErrorOnException; - private Func? _getWithEfOnBeforeAsync; private Func? _getWithEfOnAfterAsync; private Action? _getWithEfOnException; - private Func? _createWithEfOnBeforeAsync; private Func? _createWithEfOnAfterAsync; private Action? _createWithEfOnException; - private Func? _updateWithEfOnBeforeAsync; private Func? _updateWithEfOnAfterAsync; private Action? _updateWithEfOnException; - private Func? _deleteWithEfOnBeforeAsync; private Func? _deleteWithEfOnAfterAsync; private Action? _deleteWithEfOnException; @@ -112,36 +92,31 @@ public partial class PersonData : IPersonData /// /// The . /// The . - private PersonData(IDatabase db, IEfDb ef) { _db = Check.NotNull(db, nameof(db)); _ef = Check.NotNull(ef, nameof(ef)); PersonDataCtor(); } + private PersonData(IDatabase db, IEfDb ef) + { _db = Check.NotNull(db, nameof(db)); _ef = Check.NotNull(ef, nameof(ef)); PersonDataCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void PersonDataCtor(); + partial void PersonDataCtor(); // Enables additional functionality to be added to the constructor. /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(Person value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - return DataInvoker.Current.InvokeAsync(this, async () => { Person __result; var __dataArgs = DbMapper.Default.CreateArgs("[Demo].[spPersonCreate]"); if (_createOnBeforeAsync != null) await _createOnBeforeAsync(value, __dataArgs).ConfigureAwait(false); - __result = await _db.CreateAsync(__dataArgs, value).ConfigureAwait(false); + __result = await _db.CreateAsync(__dataArgs, Check.NotNull(value, nameof(value))).ConfigureAwait(false); if (_createOnAfterAsync != null) await _createOnAfterAsync(__result).ConfigureAwait(false); return __result; }, new BusinessInvokerArgs { ExceptionHandler = _createOnException }); } /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. public Task DeleteAsync(Guid id) @@ -156,10 +131,10 @@ public Task DeleteAsync(Guid id) } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(Guid id) { return DataInvoker.Current.InvokeAsync(this, async () => @@ -174,31 +149,28 @@ public Task DeleteAsync(Guid id) } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . public Task UpdateAsync(Person value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - return DataInvoker.Current.InvokeAsync(this, async () => { Person __result; var __dataArgs = DbMapper.Default.CreateArgs("[Demo].[spPersonUpdate]"); if (_updateOnBeforeAsync != null) await _updateOnBeforeAsync(value, __dataArgs).ConfigureAwait(false); - __result = await _db.UpdateAsync(__dataArgs, value).ConfigureAwait(false); + __result = await _db.UpdateAsync(__dataArgs, Check.NotNull(value, nameof(value))).ConfigureAwait(false); if (_updateOnAfterAsync != null) await _updateOnAfterAsync(__result).ConfigureAwait(false); return __result; }, new BusinessInvokerArgs { ExceptionHandler = _updateOnException }); } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The . - /// A . + /// The . public Task GetAllAsync(PagingArgs? paging) { return DataInvoker.Current.InvokeAsync(this, async () => @@ -213,9 +185,9 @@ public Task GetAllAsync(PagingArgs? paging) } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . + /// The . public Task GetAll2Async() { return DataInvoker.Current.InvokeAsync(this, async () => @@ -230,11 +202,11 @@ public Task GetAll2Async() } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetByArgsAsync(PersonArgs? args, PagingArgs? paging) { return DataInvoker.Current.InvokeAsync(this, async () => @@ -249,15 +221,13 @@ public Task GetByArgsAsync(PersonArgs? args, PagingArgs? } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetDetailByArgsAsync(PersonArgs? args, PagingArgs? paging) - { - return DataInvoker.Current.InvokeAsync(this, () => GetDetailByArgsOnImplementationAsync(args, paging), new BusinessInvokerArgs { ExceptionHandler = _getDetailByArgsOnException }); - } + => DataInvoker.Current.InvokeAsync(this, () => GetDetailByArgsOnImplementationAsync(args, paging), new BusinessInvokerArgs { ExceptionHandler = _getDetailByArgsOnException }); /// /// Merge first into second. @@ -266,76 +236,59 @@ public Task GetDetailByArgsAsync(PersonArgs? args, /// The to identifier. /// A resultant . public Task MergeAsync(Guid fromId, Guid toId) - { - return DataInvoker.Current.InvokeAsync(this, () => MergeOnImplementationAsync(fromId, toId), new BusinessInvokerArgs { ExceptionHandler = _mergeOnException }); - } + => DataInvoker.Current.InvokeAsync(this, () => MergeOnImplementationAsync(fromId, toId), new BusinessInvokerArgs { ExceptionHandler = _mergeOnException }); /// /// Mark . /// public Task MarkAsync() - { - return DataInvoker.Current.InvokeAsync(this, () => MarkOnImplementationAsync(), new BusinessInvokerArgs { ExceptionHandler = _markOnException }); - } + => DataInvoker.Current.InvokeAsync(this, () => MarkOnImplementationAsync(), new BusinessInvokerArgs { ExceptionHandler = _markOnException }); /// /// Get at specified . /// - /// The Args (see ). + /// The Args (see ). /// A resultant . public Task MapAsync(MapArgs? args) - { - return DataInvoker.Current.InvokeAsync(this, () => MapOnImplementationAsync(args), new BusinessInvokerArgs { ExceptionHandler = _mapOnException }); - } + => DataInvoker.Current.InvokeAsync(this, () => MapOnImplementationAsync(args), new BusinessInvokerArgs { ExceptionHandler = _mapOnException }); /// /// Get no arguments. /// - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetNoArgsAsync() - { - return DataInvoker.Current.InvokeAsync(this, () => GetNoArgsOnImplementationAsync(), new BusinessInvokerArgs { ExceptionHandler = _getNoArgsOnException }); - } + => DataInvoker.Current.InvokeAsync(this, () => GetNoArgsOnImplementationAsync(), new BusinessInvokerArgs { ExceptionHandler = _getNoArgsOnException }); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetDetailAsync(Guid id) - { - return DataInvoker.Current.InvokeAsync(this, () => GetDetailOnImplementationAsync(id), new BusinessInvokerArgs { ExceptionHandler = _getDetailOnException }); - } + => DataInvoker.Current.InvokeAsync(this, () => GetDetailOnImplementationAsync(id), new BusinessInvokerArgs { ExceptionHandler = _getDetailOnException }); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . public Task UpdateDetailAsync(PersonDetail value) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return DataInvoker.Current.InvokeAsync(this, () => UpdateDetailOnImplementationAsync(value), new BusinessInvokerArgs { ExceptionHandler = _updateDetailOnException }); - } + => DataInvoker.Current.InvokeAsync(this, () => UpdateDetailOnImplementationAsync(Check.NotNull(value, nameof(value))), new BusinessInvokerArgs { ExceptionHandler = _updateDetailOnException }); /// /// Get Null. /// /// The Name. - /// A resultant . + /// A resultant . public Task GetNullAsync(string? name) - { - return DataInvoker.Current.InvokeAsync(this, () => GetNullOnImplementationAsync(name), new BusinessInvokerArgs { ExceptionHandler = _getNullOnException }); - } + => DataInvoker.Current.InvokeAsync(this, () => GetNullOnImplementationAsync(name), new BusinessInvokerArgs { ExceptionHandler = _getNullOnException }); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetByArgsWithEfAsync(PersonArgs? args, PagingArgs? paging) { return DataInvoker.Current.InvokeAsync(this, async () => @@ -353,15 +306,13 @@ public Task GetByArgsWithEfAsync(PersonArgs? args, Pagin /// Throw Error. /// public Task ThrowErrorAsync() - { - return DataInvoker.Current.InvokeAsync(this, () => ThrowErrorOnImplementationAsync(), new BusinessInvokerArgs { ExceptionHandler = _throwErrorOnException }); - } + => DataInvoker.Current.InvokeAsync(this, () => ThrowErrorOnImplementationAsync(), new BusinessInvokerArgs { ExceptionHandler = _throwErrorOnException }); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetWithEfAsync(Guid id) { return DataInvoker.Current.InvokeAsync(this, async () => @@ -376,49 +327,43 @@ public Task ThrowErrorAsync() } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateWithEfAsync(Person value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - return DataInvoker.Current.InvokeAsync(this, async () => { Person __result; var __dataArgs = EfMapper.Default.CreateArgs(); if (_createWithEfOnBeforeAsync != null) await _createWithEfOnBeforeAsync(value, __dataArgs).ConfigureAwait(false); - __result = await _ef.CreateAsync(__dataArgs, value).ConfigureAwait(false); + __result = await _ef.CreateAsync(__dataArgs, Check.NotNull(value, nameof(value))).ConfigureAwait(false); if (_createWithEfOnAfterAsync != null) await _createWithEfOnAfterAsync(__result).ConfigureAwait(false); return __result; }, new BusinessInvokerArgs { ExceptionHandler = _createWithEfOnException }); } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . public Task UpdateWithEfAsync(Person value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - return DataInvoker.Current.InvokeAsync(this, async () => { Person __result; var __dataArgs = EfMapper.Default.CreateArgs(); if (_updateWithEfOnBeforeAsync != null) await _updateWithEfOnBeforeAsync(value, __dataArgs).ConfigureAwait(false); - __result = await _ef.UpdateAsync(__dataArgs, value).ConfigureAwait(false); + __result = await _ef.UpdateAsync(__dataArgs, Check.NotNull(value, nameof(value))).ConfigureAwait(false); if (_updateWithEfOnAfterAsync != null) await _updateWithEfOnAfterAsync(__result).ConfigureAwait(false); return __result; }, new BusinessInvokerArgs { ExceptionHandler = _updateWithEfOnException }); } /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. public Task DeleteWithEfAsync(Guid id) @@ -433,7 +378,7 @@ public Task DeleteWithEfAsync(Guid id) } /// - /// Provides the entity and database property mapping. + /// Provides the property and database column mapping. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] public partial class DbMapper : DatabaseMapper @@ -455,14 +400,11 @@ public DbMapper() DbMapperCtor(); } - /// - /// Enables the constructor to be extended. - /// - partial void DbMapperCtor(); + partial void DbMapperCtor(); // Enables the DbMapper constructor to be extended. } /// - /// Provides the entity and Entity Framework property mapping. + /// Provides the and Entity Framework property mapping. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] public partial class EfMapper : EfDbMapper @@ -483,10 +425,7 @@ public EfMapper() EfMapperCtor(); } - /// - /// Enables the constructor to be extended. - /// - partial void EfMapperCtor(); + partial void EfMapperCtor(); // Enables the EfMapper constructor to be extended. } } } diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/ProductData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/ProductData.cs index 56ec6bd3f..b0a00f701 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/ProductData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/ProductData.cs @@ -13,17 +13,17 @@ using Beef; using Beef.Business; using Beef.Data.OData; -using Soc = Simple.OData.Client; using Beef.Entities; using Beef.Mapper; using Beef.Mapper.Converters; using Beef.Demo.Common.Entities; using RefDataNamespace = Beef.Demo.Common.Entities; +using Soc = Simple.OData.Client; namespace Beef.Demo.Business.Data { /// - /// Provides the Product data access. + /// Provides the data access. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] public partial class ProductData : IProductData @@ -42,46 +42,44 @@ public partial class ProductData : IProductData /// Initializes a new instance of the class. /// /// The . - public ProductData(ITestOData odata) { _odata = Check.NotNull(odata, nameof(odata)); ProductDataCtor(); } + public ProductData(ITestOData odata) + { _odata = Check.NotNull(odata, nameof(odata)); ProductDataCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void ProductDataCtor(); + partial void ProductDataCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(int id) { return DataInvoker.Current.InvokeAsync(this, async () => { - var __dataArgs = ODataMapper.Default.CreateArgs(); + var __dataArgs = ODataMapper.Default.CreateArgs("Products"); return await _odata.GetAsync(__dataArgs, id).ConfigureAwait(false); }); } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetByArgsAsync(ProductArgs? args, PagingArgs? paging) { return DataInvoker.Current.InvokeAsync(this, async () => { ProductCollectionResult __result = new ProductCollectionResult(paging); - var __dataArgs = ODataMapper.Default.CreateArgs(__result.Paging!); + var __dataArgs = ODataMapper.Default.CreateArgs(__result.Paging!, "Products"); __result.Result = _odata.Query(__dataArgs, q => _getByArgsOnQuery?.Invoke(q, args, __dataArgs) ?? q).SelectQuery(); return await Task.FromResult(__result).ConfigureAwait(false); }); } /// - /// Provides the entity and OData property mapping. + /// Provides the and OData property mapping. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] public partial class ODataMapper : ODataMapper @@ -94,13 +92,11 @@ public ODataMapper() Property(s => s.Id, d => d.ID).SetUniqueKey(false); Property(s => s.Name, d => d.Name); Property(s => s.Description, d => d.Description); + AddStandardProperties(); ODataMapperCtor(); } - /// - /// Enables the constructor to be extended. - /// - partial void ODataMapperCtor(); + partial void ODataMapperCtor(); // Enables the ODataMapper constructor to be extended. } } } diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/ReferenceDataData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/ReferenceDataData.cs index 2d4f6f8f8..235c87b88 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/ReferenceDataData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/ReferenceDataData.cs @@ -9,19 +9,20 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; +using Microsoft.Azure.Cosmos; using Beef; using Beef.Business; -using Beef.Mapper; -using Beef.Mapper.Converters; +using Beef.Data.Cosmos; using Beef.Data.Database; using Beef.Data.EntityFrameworkCore; -using Beef.Data.Cosmos; +using Beef.Mapper; +using Beef.Mapper.Converters; using RefDataNamespace = Beef.Demo.Common.Entities; namespace Beef.Demo.Business.Data { /// - /// Provides the ReferenceData database access. + /// Provides the ReferenceData data access. /// public partial class ReferenceDataData : IReferenceDataData { @@ -29,28 +30,21 @@ public partial class ReferenceDataData : IReferenceDataData private readonly IEfDb _ef; private readonly ICosmosDb _cosmos; - /// - /// Parameterless constructor is explictly not supported. - /// - private ReferenceDataData() => throw new NotSupportedException(); - /// /// Initializes a new instance of the class. /// /// The . /// The . /// The . - public ReferenceDataData(IDatabase db, IEfDb ef, ICosmosDb cosmos) { _db = Check.NotNull(db, nameof(db)); _ef = Check.NotNull(ef, nameof(ef)); _cosmos = Check.NotNull(cosmos, nameof(cosmos)); ReferenceDataDataCtor(); } + public ReferenceDataData(IDatabase db, IEfDb ef, ICosmosDb cosmos) + { _db = Check.NotNull(db, nameof(db)); _ef = Check.NotNull(ef, nameof(ef)); _cosmos = Check.NotNull(cosmos, nameof(cosmos)); DataCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void ReferenceDataDataCtor(); + partial void DataCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . public async Task CountryGetAllAsync() { var __coll = new RefDataNamespace.CountryCollection(); @@ -63,9 +57,9 @@ await DataInvoker.Current.InvokeAsync(this, async () => } /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . public async Task USStateGetAllAsync() { var __coll = new RefDataNamespace.USStateCollection(); @@ -78,9 +72,9 @@ await DataInvoker.Current.InvokeAsync(this, async () => } /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . public async Task GenderGetAllAsync() { var __coll = new RefDataNamespace.GenderCollection(); @@ -97,9 +91,9 @@ await DataInvoker.Current.InvokeAsync(this, async () => } /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . public async Task EyeColorGetAllAsync() { var __coll = new RefDataNamespace.EyeColorCollection(); @@ -108,9 +102,9 @@ await DataInvoker.Current.InvokeAsync(this, async () => } /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . public async Task PowerSourceGetAllAsync() { var __coll = new RefDataNamespace.PowerSourceCollection(); @@ -119,9 +113,9 @@ await DataInvoker.Current.InvokeAsync(this, async () => } /// - /// Gets all the objects. + /// Gets all the items. /// - /// A . + /// The . public async Task CompanyGetAllAsync() { var __coll = new RefDataNamespace.CompanyCollection(); @@ -130,18 +124,18 @@ await DataInvoker.Current.InvokeAsync(this, async () => } /// - /// Provides the entity and Entity Framework property mapping. + /// Provides the and Entity Framework property mapping. /// public static EfDbMapper EyeColorMapper => EfDbMapper.CreateAuto() .HasProperty(s => s.Id, d => d.EyeColorId) .AddStandardProperties(); /// - /// Provides the entity and Cosmos property mapping. + /// Provides the and Cosmos property mapping. /// - public static CosmosDbMapper PowerSourceMapper => CosmosDbMapper.CreateAuto() - .AddStandardProperties() - .HasProperty(s => s.AdditionalInfo, d => d.AdditionalInfo, p => p.SetOperationTypes(OperationTypes.Any)); + public static CosmosDbMapper PowerSourceMapper => CosmosDbMapper.CreateAuto() + .HasProperty(s => s.AdditionalInfo, d => d.AdditionalInfo, p => p.SetOperationTypes(OperationTypes.Any)) + .AddStandardProperties(); } } diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/RobotData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/RobotData.cs index 69697f487..ccf9f08d8 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/RobotData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/RobotData.cs @@ -10,9 +10,9 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using Microsoft.Azure.Cosmos; using Beef; using Beef.Business; -using Microsoft.Azure.Cosmos; using Beef.Data.Cosmos; using Beef.Entities; using Beef.Mapper; @@ -23,7 +23,7 @@ namespace Beef.Demo.Business.Data { /// - /// Provides the Robot data access. + /// Provides the data access. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] public partial class RobotData : IRobotData @@ -43,18 +43,16 @@ public partial class RobotData : IRobotData /// Initializes a new instance of the class. /// /// The . - public RobotData(ICosmosDb cosmos) { _cosmos = Check.NotNull(cosmos, nameof(cosmos)); RobotDataCtor(); } + public RobotData(ICosmosDb cosmos) + { _cosmos = Check.NotNull(cosmos, nameof(cosmos)); RobotDataCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void RobotDataCtor(); + partial void RobotDataCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(Guid id) { return DataInvoker.Current.InvokeAsync(this, async () => @@ -65,41 +63,35 @@ public partial class RobotData : IRobotData } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(Robot value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - return DataInvoker.Current.InvokeAsync(this, async () => { var __dataArgs = CosmosMapper.Default.CreateArgs("Items", PartitionKey.None, onCreate: _onDataArgsCreate); - return await _cosmos.Container(__dataArgs).CreateAsync(value).ConfigureAwait(false); + return await _cosmos.Container(__dataArgs).CreateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); }); } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . public Task UpdateAsync(Robot value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - return DataInvoker.Current.InvokeAsync(this, async () => { var __dataArgs = CosmosMapper.Default.CreateArgs("Items", PartitionKey.None, onCreate: _onDataArgsCreate); - return await _cosmos.Container(__dataArgs).UpdateAsync(value).ConfigureAwait(false); + return await _cosmos.Container(__dataArgs).UpdateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); }); } /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. public Task DeleteAsync(Guid id) @@ -112,11 +104,11 @@ public Task DeleteAsync(Guid id) } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetByArgsAsync(RobotArgs? args, PagingArgs? paging) { return DataInvoker.Current.InvokeAsync(this, async () => @@ -129,7 +121,7 @@ public Task GetByArgsAsync(RobotArgs? args, PagingArgs? p } /// - /// Provides the entity and Cosmos property mapping. + /// Provides the and Cosmos property mapping. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] public partial class CosmosMapper : CosmosDbMapper @@ -139,7 +131,7 @@ public partial class CosmosMapper : CosmosDbMapper public CosmosMapper() { - Property(s => s.Id, d => d.Id).SetUniqueKey(true); + Property(s => s.Id, d => d.Id).SetUniqueKey(false); Property(s => s.ModelNo, d => d.ModelNo); Property(s => s.SerialNo, d => d.SerialNo); Property(s => s.EyeColorSid, d => d.EyeColor); @@ -148,10 +140,7 @@ public CosmosMapper() CosmosMapperCtor(); } - /// - /// Enables the constructor to be extended. - /// - partial void CosmosMapperCtor(); + partial void CosmosMapperCtor(); // Enables the CosmosMapper constructor to be extended. } } } diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/TripPersonData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/TripPersonData.cs index 9dda0a135..5c482de58 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/TripPersonData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/TripPersonData.cs @@ -13,17 +13,17 @@ using Beef; using Beef.Business; using Beef.Data.OData; -using Soc = Simple.OData.Client; using Beef.Entities; using Beef.Mapper; using Beef.Mapper.Converters; using Beef.Demo.Common.Entities; using RefDataNamespace = Beef.Demo.Common.Entities; +using Soc = Simple.OData.Client; namespace Beef.Demo.Business.Data { /// - /// Provides the Trip Person data access. + /// Provides the data access. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] public partial class TripPersonData : ITripPersonData @@ -34,18 +34,16 @@ public partial class TripPersonData : ITripPersonData /// Initializes a new instance of the class. /// /// The . - public TripPersonData(ITripOData odata) { _odata = Check.NotNull(odata, nameof(odata)); TripPersonDataCtor(); } + public TripPersonData(ITripOData odata) + { _odata = Check.NotNull(odata, nameof(odata)); TripPersonDataCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void TripPersonDataCtor(); + partial void TripPersonDataCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier (username). - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(string? id) { return DataInvoker.Current.InvokeAsync(this, async () => @@ -56,41 +54,35 @@ public partial class TripPersonData : ITripPersonData } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(TripPerson value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - return DataInvoker.Current.InvokeAsync(this, async () => { var __dataArgs = ODataMapper.Default.CreateArgs(); - return await _odata.CreateAsync(__dataArgs, value).ConfigureAwait(false); + return await _odata.CreateAsync(__dataArgs, Check.NotNull(value, nameof(value))).ConfigureAwait(false); }); } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . public Task UpdateAsync(TripPerson value) { - if (value == null) - throw new ArgumentNullException(nameof(value)); - return DataInvoker.Current.InvokeAsync(this, async () => { var __dataArgs = ODataMapper.Default.CreateArgs(); - return await _odata.UpdateAsync(__dataArgs, value).ConfigureAwait(false); + return await _odata.UpdateAsync(__dataArgs, Check.NotNull(value, nameof(value))).ConfigureAwait(false); }); } /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier (username). public Task DeleteAsync(string? id) @@ -103,7 +95,7 @@ public Task DeleteAsync(string? id) } /// - /// Provides the entity and OData property mapping. + /// Provides the and OData property mapping. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] public partial class ODataMapper : ODataMapper @@ -116,13 +108,11 @@ public ODataMapper() Property(s => s.Id, d => d.UserName).SetUniqueKey(false); Property(s => s.FirstName, d => d.FirstName); Property(s => s.LastName, d => d.LastName); + AddStandardProperties(); ODataMapperCtor(); } - /// - /// Enables the constructor to be extended. - /// - partial void ODataMapperCtor(); + partial void ODataMapperCtor(); // Enables the ODataMapper constructor to be extended. } } } diff --git a/samples/Demo/Beef.Demo.Business/Data/Generated/WorkHistoryData.cs b/samples/Demo/Beef.Demo.Business/Data/Generated/WorkHistoryData.cs index a8f62e7a7..f0826d03e 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Generated/WorkHistoryData.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Generated/WorkHistoryData.cs @@ -22,13 +22,14 @@ namespace Beef.Demo.Business.Data { /// - /// Provides the Work History data access. + /// Provides the data access. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] public partial class WorkHistoryData { + /// - /// Provides the entity and database property mapping. + /// Provides the property and database column mapping. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] public partial class DbMapper : DatabaseMapper @@ -46,10 +47,7 @@ public DbMapper() DbMapperCtor(); } - /// - /// Enables the constructor to be extended. - /// - partial void DbMapperCtor(); + partial void DbMapperCtor(); // Enables the DbMapper constructor to be extended. } } } diff --git a/samples/Demo/Beef.Demo.Business/Data/Model/Generated/Person.cs b/samples/Demo/Beef.Demo.Business/Data/Model/Generated/Person.cs index 2d3b6443a..87fb1a08b 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Model/Generated/Person.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Model/Generated/Person.cs @@ -8,15 +8,13 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Beef.Entities; namespace Beef.Demo.Business.Data.Model { /// - /// Represents the Person entity. + /// Represents the Person model. /// public partial class Person { @@ -34,7 +32,7 @@ public partial class Person /// Gets or sets the Last Name. /// public string? LastName { get; set; } - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Business/Data/Model/Generated/PowerSource.cs b/samples/Demo/Beef.Demo.Business/Data/Model/Generated/PowerSource.cs new file mode 100644 index 000000000..cf9067697 --- /dev/null +++ b/samples/Demo/Beef.Demo.Business/Data/Model/Generated/PowerSource.cs @@ -0,0 +1,40 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData.Model; +using Newtonsoft.Json; + +namespace Beef.Demo.Business.Data.Model +{ + /// + /// Represents the Power Source model. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public partial class PowerSource : ReferenceDataBaseGuid + { + /// + /// Gets or sets the Additional Info. + /// + [JsonProperty("additionalInfo", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? AdditionalInfo { get; set; } + } + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class PowerSourceCollection : List { } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/Demo/Beef.Demo.Business/Data/Model/Generated/Product.cs b/samples/Demo/Beef.Demo.Business/Data/Model/Generated/Product.cs index 952be33ea..d645091ac 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Model/Generated/Product.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Model/Generated/Product.cs @@ -8,8 +8,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Beef.Entities; using Newtonsoft.Json; @@ -17,7 +15,7 @@ namespace Beef.Demo.Business.Data.Model { /// - /// Represents the Product entity. + /// Represents the Product model. /// [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public partial class Product @@ -39,7 +37,7 @@ public partial class Product /// [JsonProperty("description", DefaultValueHandling = DefaultValueHandling.Ignore)] public string? Description { get; set; } - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Business/Data/Model/Generated/Robot.cs b/samples/Demo/Beef.Demo.Business/Data/Model/Generated/Robot.cs index 99e23216c..caa293500 100644 --- a/samples/Demo/Beef.Demo.Business/Data/Model/Generated/Robot.cs +++ b/samples/Demo/Beef.Demo.Business/Data/Model/Generated/Robot.cs @@ -8,8 +8,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Beef.Entities; using Newtonsoft.Json; @@ -17,7 +15,7 @@ namespace Beef.Demo.Business.Data.Model { /// - /// Represents the Robot entity. + /// Represents the Robot model. /// [JsonObject(MemberSerialization = MemberSerialization.OptIn)] public partial class Robot : IGuidIdentifier, IETag, IChangeLog @@ -59,14 +57,14 @@ public partial class Robot : IGuidIdentifier, IETag, IChangeLog public string? ETag { get; set; } /// - /// Gets or sets the Change Log (see ). + /// Gets or sets the Change Log (see ). /// [JsonProperty("changeLog", DefaultValueHandling = DefaultValueHandling.Ignore)] public ChangeLog? ChangeLog { get; set; } - } + } /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class RobotCollection : List { } diff --git a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ContactDataSvc.cs b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ContactDataSvc.cs index aa18b2703..b1b7b3fb8 100644 --- a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ContactDataSvc.cs +++ b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ContactDataSvc.cs @@ -21,7 +21,7 @@ namespace Beef.Demo.Business.DataSvc { /// - /// Provides the Contact data repository services. + /// Provides the data repository services. /// public partial class ContactDataSvc : IContactDataSvc { @@ -38,18 +38,15 @@ public partial class ContactDataSvc : IContactDataSvc public ContactDataSvc(IContactData data, IEventPublisher evtPub, IRequestCache cache) { _data = Check.NotNull(data, nameof(data)); _evtPub = Check.NotNull(evtPub, nameof(evtPub)); _cache = Check.NotNull(cache, nameof(cache)); ContactDataSvcCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void ContactDataSvcCtor(); + partial void ContactDataSvcCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . + /// The . public Task GetAllAsync() { - return DataSvcInvoker.Current.InvokeAsync(typeof(ContactDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.GetAllAsync().ConfigureAwait(false); return __result; @@ -57,32 +54,32 @@ public Task GetAllAsync() } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(Guid id) { - return DataSvcInvoker.Current.InvokeAsync(typeof(ContactDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __key = new UniqueKey(id); - if (_cache.TryGetValue(__key, out Contact __val)) + if (_cache.TryGetValue(__key, out Contact? __val)) return __val; var __result = await _data.GetAsync(id).ConfigureAwait(false); - _cache.SetValue(__key, __result!); + _cache.SetValue(__key, __result); return __result; }); } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(Contact value) { - return DataSvcInvoker.Current.InvokeAsync(typeof(ContactDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.CreateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); await _evtPub.PublishValueAsync(__result, $"Demo.Contact.{__result.Id}", "Create").ConfigureAwait(false); @@ -92,13 +89,13 @@ public Task CreateAsync(Contact value) } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . public Task UpdateAsync(Contact value) { - return DataSvcInvoker.Current.InvokeAsync(typeof(ContactDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.UpdateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); await _evtPub.PublishValueAsync(__result, $"Demo.Contact.{__result.Id}", "Update").ConfigureAwait(false); @@ -108,12 +105,12 @@ public Task UpdateAsync(Contact value) } /// - /// Deletes the object. + /// Deletes the specified . /// /// The identifier. public Task DeleteAsync(Guid id) { - return DataSvcInvoker.Current.InvokeAsync(typeof(ContactDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { await _data.DeleteAsync(id).ConfigureAwait(false); await _evtPub.PublishAsync($"Demo.Contact.{id}", "Delete", id).ConfigureAwait(false); diff --git a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/GenderDataSvc.cs b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/GenderDataSvc.cs index 5a17393e7..87d347da0 100644 --- a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/GenderDataSvc.cs +++ b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/GenderDataSvc.cs @@ -21,7 +21,7 @@ namespace Beef.Demo.Business.DataSvc { /// - /// Provides the Gender data repository services. + /// Provides the data repository services. /// public partial class GenderDataSvc : IGenderDataSvc { @@ -38,38 +38,35 @@ public partial class GenderDataSvc : IGenderDataSvc public GenderDataSvc(IGenderData data, IEventPublisher evtPub, IRequestCache cache) { _data = Check.NotNull(data, nameof(data)); _evtPub = Check.NotNull(evtPub, nameof(evtPub)); _cache = Check.NotNull(cache, nameof(cache)); GenderDataSvcCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void GenderDataSvcCtor(); + partial void GenderDataSvcCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(Guid id) { - return DataSvcInvoker.Current.InvokeAsync(typeof(GenderDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __key = new UniqueKey(id); - if (_cache.TryGetValue(__key, out Gender __val)) + if (_cache.TryGetValue(__key, out Gender? __val)) return __val; var __result = await _data.GetAsync(id).ConfigureAwait(false); - _cache.SetValue(__key, __result!); + _cache.SetValue(__key, __result); return __result; }); } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(Gender value) { - return DataSvcInvoker.Current.InvokeAsync(typeof(GenderDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.CreateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); await _evtPub.PublishValueAsync(__result, $"Demo.Gender.{__result.Id}", "Create").ConfigureAwait(false); @@ -79,13 +76,13 @@ public Task CreateAsync(Gender value) } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . public Task UpdateAsync(Gender value) { - return DataSvcInvoker.Current.InvokeAsync(typeof(GenderDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.UpdateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); await _evtPub.PublishValueAsync(__result, $"Demo.Gender.{__result.Id}", "Update").ConfigureAwait(false); diff --git a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IContactDataSvc.cs b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IContactDataSvc.cs index ecd6babb7..2db1ce6e5 100644 --- a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IContactDataSvc.cs +++ b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IContactDataSvc.cs @@ -17,39 +17,39 @@ namespace Beef.Demo.Business.DataSvc { /// - /// Defines the Contact data repository services. + /// Defines the data repository services. /// public partial interface IContactDataSvc { /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . + /// The . Task GetAllAsync(); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(Guid id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(Contact value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . Task UpdateAsync(Contact value); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. Task DeleteAsync(Guid id); diff --git a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IGenderDataSvc.cs b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IGenderDataSvc.cs index 69e27df42..9303d1b31 100644 --- a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IGenderDataSvc.cs +++ b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IGenderDataSvc.cs @@ -17,29 +17,29 @@ namespace Beef.Demo.Business.DataSvc { /// - /// Defines the Gender data repository services. + /// Defines the data repository services. /// public partial interface IGenderDataSvc { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(Guid id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(Gender value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . Task UpdateAsync(Gender value); } } diff --git a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IPersonDataSvc.cs b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IPersonDataSvc.cs index 9bba6f6fe..5a91e417c 100644 --- a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IPersonDataSvc.cs +++ b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IPersonDataSvc.cs @@ -17,64 +17,64 @@ namespace Beef.Demo.Business.DataSvc { /// - /// Defines the Person data repository services. + /// Defines the data repository services. /// public partial interface IPersonDataSvc { /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(Person value); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. Task DeleteAsync(Guid id); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(Guid id); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . Task UpdateAsync(Person value); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The . - /// A . + /// The . Task GetAllAsync(PagingArgs? paging); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . + /// The . Task GetAll2Async(); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetByArgsAsync(PersonArgs? args, PagingArgs? paging); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetDetailByArgsAsync(PersonArgs? args, PagingArgs? paging); /// @@ -93,28 +93,28 @@ public partial interface IPersonDataSvc /// /// Get at specified . /// - /// The Args (see ). + /// The Args (see ). /// A resultant . Task MapAsync(MapArgs? args); /// /// Get no arguments. /// - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetNoArgsAsync(); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetDetailAsync(Guid id); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . Task UpdateDetailAsync(PersonDetail value); /// @@ -127,15 +127,15 @@ public partial interface IPersonDataSvc /// Get Null. /// /// The Name. - /// A resultant . + /// A resultant . Task GetNullAsync(string? name); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetByArgsWithEfAsync(PersonArgs? args, PagingArgs? paging); /// @@ -144,28 +144,28 @@ public partial interface IPersonDataSvc Task ThrowErrorAsync(); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetWithEfAsync(Guid id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateWithEfAsync(Person value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . Task UpdateWithEfAsync(Person value); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. Task DeleteWithEfAsync(Guid id); diff --git a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IProductDataSvc.cs b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IProductDataSvc.cs index d0926adf9..63b30602b 100644 --- a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IProductDataSvc.cs +++ b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IProductDataSvc.cs @@ -17,23 +17,23 @@ namespace Beef.Demo.Business.DataSvc { /// - /// Defines the Product data repository services. + /// Defines the data repository services. /// public partial interface IProductDataSvc { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(int id); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetByArgsAsync(ProductArgs? args, PagingArgs? paging); } } diff --git a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IReferenceDataDataSvc.cs b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IReferenceDataDataSvc.cs index 14f0f8950..b2ec425bd 100644 --- a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IReferenceDataDataSvc.cs +++ b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IReferenceDataDataSvc.cs @@ -5,9 +5,8 @@ #nullable enable #pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options -using Beef.RefData; using System; - +using Beef.RefData; using RefDataNamespace = Beef.Demo.Common.Entities; namespace Beef.Demo.Business.DataSvc @@ -15,7 +14,7 @@ namespace Beef.Demo.Business.DataSvc /// /// Provides the ReferenceData data services. /// - public interface IReferenceDataDataSvc + public partial interface IReferenceDataDataSvc { /// /// Gets the for the associated . diff --git a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IRobotDataSvc.cs b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IRobotDataSvc.cs index 6e94bcfea..18d2664bc 100644 --- a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IRobotDataSvc.cs +++ b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/IRobotDataSvc.cs @@ -17,43 +17,43 @@ namespace Beef.Demo.Business.DataSvc { /// - /// Defines the Robot data repository services. + /// Defines the data repository services. /// public partial interface IRobotDataSvc { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(Guid id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(Robot value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . Task UpdateAsync(Robot value); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. Task DeleteAsync(Guid id); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetByArgsAsync(RobotArgs? args, PagingArgs? paging); } } diff --git a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ITripPersonDataSvc.cs b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ITripPersonDataSvc.cs index 89c3995d3..baf696953 100644 --- a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ITripPersonDataSvc.cs +++ b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ITripPersonDataSvc.cs @@ -17,33 +17,33 @@ namespace Beef.Demo.Business.DataSvc { /// - /// Defines the Trip Person data repository services. + /// Defines the data repository services. /// public partial interface ITripPersonDataSvc { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier (username). - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(string? id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(TripPerson value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . Task UpdateAsync(TripPerson value); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier (username). Task DeleteAsync(string? id); diff --git a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/PersonDataSvc.cs b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/PersonDataSvc.cs index ca0beeb82..1c41ffdd0 100644 --- a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/PersonDataSvc.cs +++ b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/PersonDataSvc.cs @@ -21,7 +21,7 @@ namespace Beef.Demo.Business.DataSvc { /// - /// Provides the Person data repository services. + /// Provides the data repository services. /// public partial class PersonDataSvc : IPersonDataSvc { @@ -66,19 +66,16 @@ public partial class PersonDataSvc : IPersonDataSvc public PersonDataSvc(IPersonData data, IEventPublisher evtPub, IRequestCache cache) { _data = Check.NotNull(data, nameof(data)); _evtPub = Check.NotNull(evtPub, nameof(evtPub)); _cache = Check.NotNull(cache, nameof(cache)); PersonDataSvcCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void PersonDataSvcCtor(); + partial void PersonDataSvcCtor(); // Enables additional functionality to be added to the constructor. /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(Person value) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.CreateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); await _evtPub.PublishValueAsync(__result, $"Demo.Person.{__result.Id}", "Create").ConfigureAwait(false); @@ -89,12 +86,12 @@ public Task CreateAsync(Person value) } /// - /// Deletes the object. + /// Deletes the specified . /// /// The identifier. public Task DeleteAsync(Guid id) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { await _data.DeleteAsync(id).ConfigureAwait(false); await _evtPub.PublishAsync($"Demo.Person.{id}", "Delete", id).ConfigureAwait(false); @@ -104,33 +101,33 @@ public Task DeleteAsync(Guid id) } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(Guid id) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __key = new UniqueKey(id); - if (_cache.TryGetValue(__key, out Person __val)) + if (_cache.TryGetValue(__key, out Person? __val)) return __val; var __result = await _data.GetAsync(id).ConfigureAwait(false); - _cache.SetValue(__key, __result!); + _cache.SetValue(__key, __result); if (_getOnAfterAsync != null) await _getOnAfterAsync(__result, id).ConfigureAwait(false); return __result; }); } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . public Task UpdateAsync(Person value) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.UpdateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); await _evtPub.PublishValueAsync(__result, $"Demo.Person.{__result.Id}", "Update").ConfigureAwait(false); @@ -141,13 +138,13 @@ public Task UpdateAsync(Person value) } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The . - /// A . + /// The . public Task GetAllAsync(PagingArgs? paging) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.GetAllAsync(paging).ConfigureAwait(false); if (_getAllOnAfterAsync != null) await _getAllOnAfterAsync(__result, paging).ConfigureAwait(false); @@ -156,12 +153,12 @@ public Task GetAllAsync(PagingArgs? paging) } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . + /// The . public Task GetAll2Async() { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.GetAll2Async().ConfigureAwait(false); if (_getAll2OnAfterAsync != null) await _getAll2OnAfterAsync(__result).ConfigureAwait(false); @@ -170,14 +167,14 @@ public Task GetAll2Async() } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetByArgsAsync(PersonArgs? args, PagingArgs? paging) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.GetByArgsAsync(args, paging).ConfigureAwait(false); if (_getByArgsOnAfterAsync != null) await _getByArgsOnAfterAsync(__result, args, paging).ConfigureAwait(false); @@ -186,14 +183,14 @@ public Task GetByArgsAsync(PersonArgs? args, PagingArgs? } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetDetailByArgsAsync(PersonArgs? args, PagingArgs? paging) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.GetDetailByArgsAsync(args, paging).ConfigureAwait(false); if (_getDetailByArgsOnAfterAsync != null) await _getDetailByArgsOnAfterAsync(__result, args, paging).ConfigureAwait(false); @@ -209,12 +206,13 @@ public Task GetDetailByArgsAsync(PersonArgs? args, /// A resultant . public Task MergeAsync(Guid fromId, Guid toId) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.MergeAsync(fromId, toId).ConfigureAwait(false); await _evtPub.PublishAsync( - _evtPub.CreateValueEvent(__result, $"Demo.Person.{fromId}", "Merge", fromId, toId)).ConfigureAwait(false); - _cache.SetValue(__result.UniqueKey, __result); + _evtPub.CreateValueEvent(__result, $"Demo.Person.{fromId}", "MergeFrom", fromId, toId), + _evtPub.CreateValueEvent(__result, $"Demo.Person.{toId}", "MergeTo", fromId, toId)).ConfigureAwait(false); + if (_mergeOnAfterAsync != null) await _mergeOnAfterAsync(__result, fromId, toId).ConfigureAwait(false); return __result; }); @@ -225,7 +223,7 @@ await _evtPub.PublishAsync( /// public Task MarkAsync() { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { await _data.MarkAsync().ConfigureAwait(false); if (_markOnAfterAsync != null) await _markOnAfterAsync().ConfigureAwait(false); @@ -235,11 +233,11 @@ public Task MarkAsync() /// /// Get at specified . /// - /// The Args (see ). + /// The Args (see ). /// A resultant . public Task MapAsync(MapArgs? args) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.MapAsync(args).ConfigureAwait(false); if (_mapOnAfterAsync != null) await _mapOnAfterAsync(__result, args).ConfigureAwait(false); @@ -250,50 +248,50 @@ public Task MapAsync(MapArgs? args) /// /// Get no arguments. /// - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetNoArgsAsync() { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __key = new UniqueKey(); - if (_cache.TryGetValue(__key, out Person __val)) + if (_cache.TryGetValue(__key, out Person? __val)) return __val; var __result = await _data.GetNoArgsAsync().ConfigureAwait(false); - _cache.SetValue(__key, __result!); + _cache.SetValue(__key, __result); if (_getNoArgsOnAfterAsync != null) await _getNoArgsOnAfterAsync(__result).ConfigureAwait(false); return __result; }); } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetDetailAsync(Guid id) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __key = new UniqueKey(id); - if (_cache.TryGetValue(__key, out PersonDetail __val)) + if (_cache.TryGetValue(__key, out PersonDetail? __val)) return __val; var __result = await _data.GetDetailAsync(id).ConfigureAwait(false); - _cache.SetValue(__key, __result!); + _cache.SetValue(__key, __result); if (_getDetailOnAfterAsync != null) await _getDetailOnAfterAsync(__result, id).ConfigureAwait(false); return __result; }); } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . public Task UpdateDetailAsync(PersonDetail value) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.UpdateDetailAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); await _evtPub.PublishValueAsync(__result, $"Demo.Person.{__result.Id}", "Update").ConfigureAwait(false); @@ -308,39 +306,32 @@ public Task UpdateDetailAsync(PersonDetail value) /// /// A resultant . public Task DataSvcCustomAsync() - { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => - { - var __result = await DataSvcCustomOnImplementationAsync().ConfigureAwait(false); - return __result; - }); - } + => DataSvcInvoker.Current.InvokeAsync(this, () => DataSvcCustomOnImplementationAsync()); /// /// Get Null. /// /// The Name. - /// A resultant . + /// A resultant . public Task GetNullAsync(string? name) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.GetNullAsync(name).ConfigureAwait(false); - _cache.SetValue(__result?.UniqueKey ?? UniqueKey.Empty, __result); if (_getNullOnAfterAsync != null) await _getNullOnAfterAsync(__result, name).ConfigureAwait(false); return __result; }); } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetByArgsWithEfAsync(PersonArgs? args, PagingArgs? paging) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.GetByArgsWithEfAsync(args, paging).ConfigureAwait(false); if (_getByArgsWithEfOnAfterAsync != null) await _getByArgsWithEfOnAfterAsync(__result, args, paging).ConfigureAwait(false); @@ -353,7 +344,7 @@ public Task GetByArgsWithEfAsync(PersonArgs? args, Pagin /// public Task ThrowErrorAsync() { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { await _data.ThrowErrorAsync().ConfigureAwait(false); if (_throwErrorOnAfterAsync != null) await _throwErrorOnAfterAsync().ConfigureAwait(false); @@ -361,33 +352,33 @@ public Task ThrowErrorAsync() } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetWithEfAsync(Guid id) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __key = new UniqueKey(id); - if (_cache.TryGetValue(__key, out Person __val)) + if (_cache.TryGetValue(__key, out Person? __val)) return __val; var __result = await _data.GetWithEfAsync(id).ConfigureAwait(false); - _cache.SetValue(__key, __result!); + _cache.SetValue(__key, __result); if (_getWithEfOnAfterAsync != null) await _getWithEfOnAfterAsync(__result, id).ConfigureAwait(false); return __result; }); } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateWithEfAsync(Person value) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.CreateWithEfAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); await _evtPub.PublishValueAsync(__result, $"Demo.Person.{__result.Id}", "Create").ConfigureAwait(false); @@ -398,13 +389,13 @@ public Task CreateWithEfAsync(Person value) } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . public Task UpdateWithEfAsync(Person value) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.UpdateWithEfAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); await _evtPub.PublishValueAsync(__result, $"Demo.Person.{__result.Id}", "Update").ConfigureAwait(false); @@ -415,16 +406,15 @@ public Task UpdateWithEfAsync(Person value) } /// - /// Deletes the object. + /// Deletes the specified . /// /// The identifier. public Task DeleteWithEfAsync(Guid id) { - return DataSvcInvoker.Current.InvokeAsync(typeof(PersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { await _data.DeleteWithEfAsync(id).ConfigureAwait(false); - await _evtPub.PublishAsync( - _evtPub.CreateEvent($"Demo.Person.{id}", "Delete", id)).ConfigureAwait(false); + await _evtPub.PublishAsync($"Demo.Person.{id}", "", id).ConfigureAwait(false); _cache.Remove(new UniqueKey(id)); if (_deleteWithEfOnAfterAsync != null) await _deleteWithEfOnAfterAsync(id).ConfigureAwait(false); }); diff --git a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ProductDataSvc.cs b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ProductDataSvc.cs index ec9cdf555..3a56eddff 100644 --- a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ProductDataSvc.cs +++ b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ProductDataSvc.cs @@ -20,7 +20,7 @@ namespace Beef.Demo.Business.DataSvc { /// - /// Provides the Product data repository services. + /// Provides the data repository services. /// public partial class ProductDataSvc : IProductDataSvc { @@ -35,39 +35,36 @@ public partial class ProductDataSvc : IProductDataSvc public ProductDataSvc(IProductData data, IRequestCache cache) { _data = Check.NotNull(data, nameof(data)); _cache = Check.NotNull(cache, nameof(cache)); ProductDataSvcCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void ProductDataSvcCtor(); + partial void ProductDataSvcCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(int id) { - return DataSvcInvoker.Current.InvokeAsync(typeof(ProductDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __key = new UniqueKey(id); - if (_cache.TryGetValue(__key, out Product __val)) + if (_cache.TryGetValue(__key, out Product? __val)) return __val; var __result = await _data.GetAsync(id).ConfigureAwait(false); - _cache.SetValue(__key, __result!); + _cache.SetValue(__key, __result); return __result; }); } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetByArgsAsync(ProductArgs? args, PagingArgs? paging) { - return DataSvcInvoker.Current.InvokeAsync(typeof(ProductDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.GetByArgsAsync(args, paging).ConfigureAwait(false); return __result; diff --git a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ReferenceDataDataSvc.cs b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ReferenceDataDataSvc.cs index 4cd5e2e35..a38b8c5e1 100644 --- a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ReferenceDataDataSvc.cs +++ b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/ReferenceDataDataSvc.cs @@ -23,7 +23,7 @@ namespace Beef.Demo.Business.DataSvc /// /// Provides the ReferenceData data services. /// - public class ReferenceDataDataSvc : IReferenceDataDataSvc + public partial class ReferenceDataDataSvc : IReferenceDataDataSvc { private readonly IServiceProvider _provider; private readonly Dictionary _cacheDict = new Dictionary(); @@ -41,8 +41,11 @@ public ReferenceDataDataSvc(IServiceProvider provider) _cacheDict.Add(typeof(RefDataNamespace.EyeColor), new ReferenceDataCache(() => DataSvcInvoker.Current.InvokeAsync(typeof(ReferenceDataDataSvc), () => GetDataAsync(data => data.EyeColorGetAllAsync())))); _cacheDict.Add(typeof(RefDataNamespace.PowerSource), new ReferenceDataCache(() => DataSvcInvoker.Current.InvokeAsync(typeof(ReferenceDataDataSvc), () => GetDataAsync(data => data.PowerSourceGetAllAsync())))); _cacheDict.Add(typeof(RefDataNamespace.Company), new ReferenceDataCache(() => DataSvcInvoker.Current.InvokeAsync(typeof(ReferenceDataDataSvc), () => GetDataAsync(data => data.CompanyGetAllAsync())))); + ReferenceDataDataSvcCtor(); } + partial void ReferenceDataDataSvcCtor(); // Enables the ReferenceDataDataSvc constructor to be extended. + /// /// Gets the data within a new scope; each reference data request needs to occur separately and independently. /// @@ -57,13 +60,9 @@ private async Task GetDataAsync(Func> func) /// /// The type associated /// A . - public IReferenceDataCollection GetCollection(Type type) - { - if (_cacheDict.TryGetValue(type ?? throw new ArgumentNullException(nameof(type)), out var rdc)) - return rdc.GetCollection(); - else + public IReferenceDataCollection GetCollection(Type type) => + _cacheDict.TryGetValue(type ?? throw new ArgumentNullException(nameof(type)), out var rdc) ? rdc.GetCollection() : throw new ArgumentException($"Type {type.Name} does not exist within the ReferenceDataDataSvc cache.", nameof(type)); - } } } diff --git a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/RobotDataSvc.cs b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/RobotDataSvc.cs index 61820ed22..eceafda5c 100644 --- a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/RobotDataSvc.cs +++ b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/RobotDataSvc.cs @@ -21,7 +21,7 @@ namespace Beef.Demo.Business.DataSvc { /// - /// Provides the Robot data repository services. + /// Provides the data repository services. /// public partial class RobotDataSvc : IRobotDataSvc { @@ -38,38 +38,35 @@ public partial class RobotDataSvc : IRobotDataSvc public RobotDataSvc(IRobotData data, IEventPublisher evtPub, IRequestCache cache) { _data = Check.NotNull(data, nameof(data)); _evtPub = Check.NotNull(evtPub, nameof(evtPub)); _cache = Check.NotNull(cache, nameof(cache)); RobotDataSvcCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void RobotDataSvcCtor(); + partial void RobotDataSvcCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(Guid id) { - return DataSvcInvoker.Current.InvokeAsync(typeof(RobotDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __key = new UniqueKey(id); - if (_cache.TryGetValue(__key, out Robot __val)) + if (_cache.TryGetValue(__key, out Robot? __val)) return __val; var __result = await _data.GetAsync(id).ConfigureAwait(false); - _cache.SetValue(__key, __result!); + _cache.SetValue(__key, __result); return __result; }); } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(Robot value) { - return DataSvcInvoker.Current.InvokeAsync(typeof(RobotDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.CreateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); await _evtPub.PublishValueAsync(__result, $"Demo.Robot.{__result.Id}", "Create").ConfigureAwait(false); @@ -79,13 +76,13 @@ public Task CreateAsync(Robot value) } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . public Task UpdateAsync(Robot value) { - return DataSvcInvoker.Current.InvokeAsync(typeof(RobotDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.UpdateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); await _evtPub.PublishValueAsync(__result, $"Demo.Robot.{__result.Id}", "Update").ConfigureAwait(false); @@ -95,12 +92,12 @@ public Task UpdateAsync(Robot value) } /// - /// Deletes the object. + /// Deletes the specified . /// /// The identifier. public Task DeleteAsync(Guid id) { - return DataSvcInvoker.Current.InvokeAsync(typeof(RobotDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { await _data.DeleteAsync(id).ConfigureAwait(false); await _evtPub.PublishAsync($"Demo.Robot.{id}", "Delete", id).ConfigureAwait(false); @@ -109,14 +106,14 @@ public Task DeleteAsync(Guid id) } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetByArgsAsync(RobotArgs? args, PagingArgs? paging) { - return DataSvcInvoker.Current.InvokeAsync(typeof(RobotDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.GetByArgsAsync(args, paging).ConfigureAwait(false); return __result; diff --git a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/TripPersonDataSvc.cs b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/TripPersonDataSvc.cs index ed7d7696f..b604c9534 100644 --- a/samples/Demo/Beef.Demo.Business/DataSvc/Generated/TripPersonDataSvc.cs +++ b/samples/Demo/Beef.Demo.Business/DataSvc/Generated/TripPersonDataSvc.cs @@ -21,7 +21,7 @@ namespace Beef.Demo.Business.DataSvc { /// - /// Provides the Trip Person data repository services. + /// Provides the data repository services. /// public partial class TripPersonDataSvc : ITripPersonDataSvc { @@ -38,38 +38,35 @@ public partial class TripPersonDataSvc : ITripPersonDataSvc public TripPersonDataSvc(ITripPersonData data, IEventPublisher evtPub, IRequestCache cache) { _data = Check.NotNull(data, nameof(data)); _evtPub = Check.NotNull(evtPub, nameof(evtPub)); _cache = Check.NotNull(cache, nameof(cache)); TripPersonDataSvcCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void TripPersonDataSvcCtor(); + partial void TripPersonDataSvcCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier (username). - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(string? id) { - return DataSvcInvoker.Current.InvokeAsync(typeof(TripPersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __key = new UniqueKey(id); - if (_cache.TryGetValue(__key, out TripPerson __val)) + if (_cache.TryGetValue(__key, out TripPerson? __val)) return __val; var __result = await _data.GetAsync(id).ConfigureAwait(false); - _cache.SetValue(__key, __result!); + _cache.SetValue(__key, __result); return __result; }); } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(TripPerson value) { - return DataSvcInvoker.Current.InvokeAsync(typeof(TripPersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.CreateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); await _evtPub.PublishValueAsync(__result, $"Demo.TripPerson.{__result.Id}", "Create").ConfigureAwait(false); @@ -79,13 +76,13 @@ public Task CreateAsync(TripPerson value) } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. - /// A refreshed object. + /// The . + /// The updated . public Task UpdateAsync(TripPerson value) { - return DataSvcInvoker.Current.InvokeAsync(typeof(TripPersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { var __result = await _data.UpdateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); await _evtPub.PublishValueAsync(__result, $"Demo.TripPerson.{__result.Id}", "Update").ConfigureAwait(false); @@ -95,12 +92,12 @@ public Task UpdateAsync(TripPerson value) } /// - /// Deletes the object. + /// Deletes the specified . /// /// The identifier (username). public Task DeleteAsync(string? id) { - return DataSvcInvoker.Current.InvokeAsync(typeof(TripPersonDataSvc), async () => + return DataSvcInvoker.Current.InvokeAsync(this, async () => { await _data.DeleteAsync(id).ConfigureAwait(false); await _evtPub.PublishAsync($"Demo.TripPerson.{id}", "Delete", id).ConfigureAwait(false); diff --git a/samples/Demo/Beef.Demo.Business/Generated/ConfigManager.cs b/samples/Demo/Beef.Demo.Business/Generated/ConfigManager.cs index 36296453f..2ca806f48 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/ConfigManager.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/ConfigManager.cs @@ -20,7 +20,7 @@ namespace Beef.Demo.Business { /// - /// Provides the Config business functionality. + /// Provides the Config business functionality. /// public partial class ConfigManager : IConfigManager { @@ -29,10 +29,7 @@ public partial class ConfigManager : IConfigManager /// public ConfigManager() => ConfigManagerCtor(); - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void ConfigManagerCtor(); + partial void ConfigManagerCtor(); // Enables additional functionality to be added to the constructor. /// /// Get Env Vars. @@ -43,8 +40,7 @@ public partial class ConfigManager : IConfigManager return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Unspecified; - var __result = await GetEnvVarsOnImplementationAsync().ConfigureAwait(false); - return Cleaner.Clean(__result); + return Cleaner.Clean(await GetEnvVarsOnImplementationAsync().ConfigureAwait(false)); }); } } diff --git a/samples/Demo/Beef.Demo.Business/Generated/ContactManager.cs b/samples/Demo/Beef.Demo.Business/Generated/ContactManager.cs index 05cb3635f..32ed03885 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/ContactManager.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/ContactManager.cs @@ -20,7 +20,7 @@ namespace Beef.Demo.Business { /// - /// Provides the Contact business functionality. + /// Provides the business functionality. /// public partial class ContactManager : IContactManager { @@ -30,17 +30,15 @@ public partial class ContactManager : IContactManager /// Initializes a new instance of the class. /// /// The . - public ContactManager(IContactDataSvc dataService) { _dataService = Check.NotNull(dataService, nameof(dataService)); ContactManagerCtor(); } + public ContactManager(IContactDataSvc dataService) + { _dataService = Check.NotNull(dataService, nameof(dataService)); ContactManagerCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void ContactManagerCtor(); + partial void ContactManagerCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . + /// The . public Task GetAllAsync() { return ManagerInvoker.Current.InvokeAsync(this, async () => @@ -51,29 +49,26 @@ public Task GetAllAsync() } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(Guid id) { return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Read; Cleaner.CleanUp(id); - MultiValidator.Create() - .Add(id.Validate(nameof(id)).Mandatory()) - .Run().ThrowOnError(); - + id.Validate(nameof(id)).Mandatory().Run().ThrowOnError(); return Cleaner.Clean(await _dataService.GetAsync(id).ConfigureAwait(false)); }); } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(Contact value) { value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); @@ -82,20 +77,16 @@ public Task CreateAsync(Contact value) { ExecutionContext.Current.OperationType = OperationType.Create; Cleaner.CleanUp(value); - MultiValidator.Create() - .Add(value.Validate(nameof(value))) - .Run().ThrowOnError(); - return Cleaner.Clean(await _dataService.CreateAsync(value).ConfigureAwait(false)); }); } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. - /// A refreshed object. + /// The updated . public Task UpdateAsync(Contact value, Guid id) { value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); @@ -105,16 +96,12 @@ public Task UpdateAsync(Contact value, Guid id) ExecutionContext.Current.OperationType = OperationType.Update; value.Id = id; Cleaner.CleanUp(value); - MultiValidator.Create() - .Add(value.Validate(nameof(value))) - .Run().ThrowOnError(); - return Cleaner.Clean(await _dataService.UpdateAsync(value).ConfigureAwait(false)); }); } /// - /// Deletes the object. + /// Deletes the specified . /// /// The identifier. public Task DeleteAsync(Guid id) @@ -123,10 +110,7 @@ public Task DeleteAsync(Guid id) { ExecutionContext.Current.OperationType = OperationType.Delete; Cleaner.CleanUp(id); - MultiValidator.Create() - .Add(id.Validate(nameof(id)).Mandatory()) - .Run().ThrowOnError(); - + id.Validate(nameof(id)).Mandatory().Run().ThrowOnError(); await _dataService.DeleteAsync(id).ConfigureAwait(false); }); } diff --git a/samples/Demo/Beef.Demo.Business/Generated/GenderManager.cs b/samples/Demo/Beef.Demo.Business/Generated/GenderManager.cs index f050f84f8..8c52f4093 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/GenderManager.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/GenderManager.cs @@ -20,7 +20,7 @@ namespace Beef.Demo.Business { /// - /// Provides the Gender business functionality. + /// Provides the business functionality. /// public partial class GenderManager : IGenderManager { @@ -30,37 +30,32 @@ public partial class GenderManager : IGenderManager /// Initializes a new instance of the class. /// /// The . - public GenderManager(IGenderDataSvc dataService) { _dataService = Check.NotNull(dataService, nameof(dataService)); GenderManagerCtor(); } + public GenderManager(IGenderDataSvc dataService) + { _dataService = Check.NotNull(dataService, nameof(dataService)); GenderManagerCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void GenderManagerCtor(); + partial void GenderManagerCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(Guid id) { return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Read; Cleaner.CleanUp(id); - MultiValidator.Create() - .Add(id.Validate(nameof(id)).Mandatory()) - .Run().ThrowOnError(); - + id.Validate(nameof(id)).Mandatory().Run().ThrowOnError(); return Cleaner.Clean(await _dataService.GetAsync(id).ConfigureAwait(false)); }); } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(Gender value) { value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); @@ -69,20 +64,16 @@ public Task CreateAsync(Gender value) { ExecutionContext.Current.OperationType = OperationType.Create; Cleaner.CleanUp(value); - MultiValidator.Create() - .Add(value.Validate(nameof(value)).Entity(new ReferenceDataValidator())) - .Run().ThrowOnError(); - return Cleaner.Clean(await _dataService.CreateAsync(value).ConfigureAwait(false)); }); } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. - /// A refreshed object. + /// The updated . public Task UpdateAsync(Gender value, Guid id) { value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); @@ -92,10 +83,6 @@ public Task UpdateAsync(Gender value, Guid id) ExecutionContext.Current.OperationType = OperationType.Update; value.Id = id; Cleaner.CleanUp(value); - MultiValidator.Create() - .Add(value.Validate(nameof(value)).Entity(new ReferenceDataValidator())) - .Run().ThrowOnError(); - return Cleaner.Clean(await _dataService.UpdateAsync(value).ConfigureAwait(false)); }); } diff --git a/samples/Demo/Beef.Demo.Business/Generated/IConfigManager.cs b/samples/Demo/Beef.Demo.Business/Generated/IConfigManager.cs index 56779db05..4fce53e82 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/IConfigManager.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/IConfigManager.cs @@ -17,7 +17,7 @@ namespace Beef.Demo.Business { /// - /// Defines the Config business functionality. + /// Defines the Config business functionality. /// public partial interface IConfigManager { diff --git a/samples/Demo/Beef.Demo.Business/Generated/IContactManager.cs b/samples/Demo/Beef.Demo.Business/Generated/IContactManager.cs index a7fd8c2e9..bfe17c272 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/IContactManager.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/IContactManager.cs @@ -17,40 +17,40 @@ namespace Beef.Demo.Business { /// - /// Defines the Contact business functionality. + /// Defines the business functionality. /// public partial interface IContactManager { /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . + /// The . Task GetAllAsync(); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(Guid id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(Contact value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. - /// A refreshed object. + /// The updated . Task UpdateAsync(Contact value, Guid id); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. Task DeleteAsync(Guid id); diff --git a/samples/Demo/Beef.Demo.Business/Generated/IGenderManager.cs b/samples/Demo/Beef.Demo.Business/Generated/IGenderManager.cs index 9431a7d0d..190977f04 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/IGenderManager.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/IGenderManager.cs @@ -17,30 +17,30 @@ namespace Beef.Demo.Business { /// - /// Defines the Gender business functionality. + /// Defines the business functionality. /// public partial interface IGenderManager { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(Guid id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(Gender value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. - /// A refreshed object. + /// The updated . Task UpdateAsync(Gender value, Guid id); } } diff --git a/samples/Demo/Beef.Demo.Business/Generated/IPersonManager.cs b/samples/Demo/Beef.Demo.Business/Generated/IPersonManager.cs index 2f4dce22c..28baef07f 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/IPersonManager.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/IPersonManager.cs @@ -17,65 +17,65 @@ namespace Beef.Demo.Business { /// - /// Defines the Person business functionality. + /// Defines the business functionality. /// public partial interface IPersonManager { /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(Person value); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. Task DeleteAsync(Guid id); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(Guid id); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. - /// A refreshed object. + /// The updated . Task UpdateAsync(Person value, Guid id); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The . - /// A . + /// The . Task GetAllAsync(PagingArgs? paging); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . + /// The . Task GetAll2Async(); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetByArgsAsync(PersonArgs? args, PagingArgs? paging); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetDetailByArgsAsync(PersonArgs? args, PagingArgs? paging); /// @@ -94,36 +94,36 @@ public partial interface IPersonManager /// /// Get at specified . /// - /// The Args (see ). + /// The Args (see ). /// A resultant . Task MapAsync(MapArgs? args); /// /// Get no arguments. /// - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetNoArgsAsync(); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetDetailAsync(Guid id); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. - /// A refreshed object. + /// The updated . Task UpdateDetailAsync(PersonDetail value, Guid id); /// /// Actually validating the FromBody parameter generation. /// - /// The Person (see ). - Task AddAsync(Person? person); + /// The Person (see ). + Task AddAsync(Person person); /// /// Validate a DataSvc Custom generation. @@ -134,22 +134,22 @@ public partial interface IPersonManager /// /// Validate a Manager Custom generation. /// - /// The selected object where found; otherwise, null. + /// The selected where found. Task ManagerCustomAsync(); /// /// Get Null. /// /// The Name. - /// A resultant . + /// A resultant . Task GetNullAsync(string? name); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetByArgsWithEfAsync(PersonArgs? args, PagingArgs? paging); /// @@ -158,29 +158,29 @@ public partial interface IPersonManager Task ThrowErrorAsync(); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetWithEfAsync(Guid id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateWithEfAsync(Person value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. - /// A refreshed object. + /// The updated . Task UpdateWithEfAsync(Person value, Guid id); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. Task DeleteWithEfAsync(Guid id); diff --git a/samples/Demo/Beef.Demo.Business/Generated/IProductManager.cs b/samples/Demo/Beef.Demo.Business/Generated/IProductManager.cs index 7b00138b9..2e11c8341 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/IProductManager.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/IProductManager.cs @@ -17,23 +17,23 @@ namespace Beef.Demo.Business { /// - /// Defines the Product business functionality. + /// Defines the business functionality. /// public partial interface IProductManager { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(int id); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetByArgsAsync(ProductArgs? args, PagingArgs? paging); } } diff --git a/samples/Demo/Beef.Demo.Business/Generated/IRobotManager.cs b/samples/Demo/Beef.Demo.Business/Generated/IRobotManager.cs index ee922ac88..f62efb2c4 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/IRobotManager.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/IRobotManager.cs @@ -17,44 +17,44 @@ namespace Beef.Demo.Business { /// - /// Defines the Robot business functionality. + /// Defines the business functionality. /// public partial interface IRobotManager { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(Guid id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(Robot value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. - /// A refreshed object. + /// The updated . Task UpdateAsync(Robot value, Guid id); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. Task DeleteAsync(Guid id); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . Task GetByArgsAsync(RobotArgs? args, PagingArgs? paging); /// diff --git a/samples/Demo/Beef.Demo.Business/Generated/ITripPersonManager.cs b/samples/Demo/Beef.Demo.Business/Generated/ITripPersonManager.cs index da0587b9d..bc57307ef 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/ITripPersonManager.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/ITripPersonManager.cs @@ -17,34 +17,34 @@ namespace Beef.Demo.Business { /// - /// Defines the Trip Person business functionality. + /// Defines the business functionality. /// public partial interface ITripPersonManager { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier (username). - /// The selected object where found; otherwise, null. + /// The selected where found. Task GetAsync(string? id); /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . Task CreateAsync(TripPerson value); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier (username). - /// A refreshed object. + /// The updated . Task UpdateAsync(TripPerson value, string? id); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier (username). Task DeleteAsync(string? id); diff --git a/samples/Demo/Beef.Demo.Business/Generated/PersonManager.cs b/samples/Demo/Beef.Demo.Business/Generated/PersonManager.cs index 8787c525a..8bb8e3a84 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/PersonManager.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/PersonManager.cs @@ -14,14 +14,14 @@ using Beef.Entities; using Beef.Validation; using Beef.Demo.Common.Entities; -using Beef.Demo.Business.Validation; using Beef.Demo.Business.DataSvc; +using Beef.Demo.Business.Validation; using RefDataNamespace = Beef.Demo.Common.Entities; namespace Beef.Demo.Business { /// - /// Provides the Person business functionality. + /// Provides the business functionality. /// public partial class PersonManager : IPersonManager { @@ -50,9 +50,13 @@ public partial class PersonManager : IPersonManager private Func? _updateOnBeforeAsync; private Func? _updateOnAfterAsync; + private Func? _getAllOnPreValidateAsync; + private Action? _getAllOnValidate; private Func? _getAllOnBeforeAsync; private Func? _getAllOnAfterAsync; + private Func? _getAll2OnPreValidateAsync; + private Action? _getAll2OnValidate; private Func? _getAll2OnBeforeAsync; private Func? _getAll2OnAfterAsync; @@ -71,6 +75,8 @@ public partial class PersonManager : IPersonManager private Func? _mergeOnBeforeAsync; private Func? _mergeOnAfterAsync; + private Func? _markOnPreValidateAsync; + private Action? _markOnValidate; private Func? _markOnBeforeAsync; private Func? _markOnAfterAsync; @@ -79,6 +85,8 @@ public partial class PersonManager : IPersonManager private Func? _mapOnBeforeAsync; private Func? _mapOnAfterAsync; + private Func? _getNoArgsOnPreValidateAsync; + private Action? _getNoArgsOnValidate; private Func? _getNoArgsOnBeforeAsync; private Func? _getNoArgsOnAfterAsync; @@ -92,6 +100,8 @@ public partial class PersonManager : IPersonManager private Func? _updateDetailOnBeforeAsync; private Func? _updateDetailOnAfterAsync; + private Func? _dataSvcCustomOnPreValidateAsync; + private Action? _dataSvcCustomOnValidate; private Func? _dataSvcCustomOnBeforeAsync; private Func? _dataSvcCustomOnAfterAsync; @@ -105,6 +115,8 @@ public partial class PersonManager : IPersonManager private Func? _getByArgsWithEfOnBeforeAsync; private Func? _getByArgsWithEfOnAfterAsync; + private Func? _throwErrorOnPreValidateAsync; + private Action? _throwErrorOnValidate; private Func? _throwErrorOnBeforeAsync; private Func? _throwErrorOnAfterAsync; @@ -135,18 +147,16 @@ public partial class PersonManager : IPersonManager /// Initializes a new instance of the class. /// /// The . - public PersonManager(IPersonDataSvc dataService) { _dataService = Check.NotNull(dataService, nameof(dataService)); PersonManagerCtor(); } + public PersonManager(IPersonDataSvc dataService) + { _dataService = Check.NotNull(dataService, nameof(dataService)); PersonManagerCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void PersonManagerCtor(); + partial void PersonManagerCtor(); // Enables additional functionality to be added to the constructor. /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(Person value) { value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); @@ -170,7 +180,7 @@ public Task CreateAsync(Person value) } /// - /// Deletes the object. + /// Deletes the specified . /// /// The identifier. public Task DeleteAsync(Guid id) @@ -193,10 +203,10 @@ public Task DeleteAsync(Guid id) } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(Guid id) { return ManagerInvoker.Current.InvokeAsync(this, async () => @@ -218,11 +228,11 @@ public Task DeleteAsync(Guid id) } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. - /// A refreshed object. + /// The updated . public Task UpdateAsync(Person value, Guid id) { value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); @@ -247,15 +257,21 @@ public Task UpdateAsync(Person value, Guid id) } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The . - /// A . + /// The . public Task GetAllAsync(PagingArgs? paging) { return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Read; + if (_getAllOnPreValidateAsync != null) await _getAllOnPreValidateAsync(paging).ConfigureAwait(false); + + MultiValidator.Create() + .Additional((__mv) => _getAllOnValidate?.Invoke(__mv, paging)) + .Run().ThrowOnError(); + if (_getAllOnBeforeAsync != null) await _getAllOnBeforeAsync(paging).ConfigureAwait(false); var __result = await _dataService.GetAllAsync(paging).ConfigureAwait(false); if (_getAllOnAfterAsync != null) await _getAllOnAfterAsync(__result, paging).ConfigureAwait(false); @@ -264,14 +280,20 @@ public Task GetAllAsync(PagingArgs? paging) } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// A . + /// The . public Task GetAll2Async() { return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Read; + if (_getAll2OnPreValidateAsync != null) await _getAll2OnPreValidateAsync().ConfigureAwait(false); + + MultiValidator.Create() + .Additional((__mv) => _getAll2OnValidate?.Invoke(__mv)) + .Run().ThrowOnError(); + if (_getAll2OnBeforeAsync != null) await _getAll2OnBeforeAsync().ConfigureAwait(false); var __result = await _dataService.GetAll2Async().ConfigureAwait(false); if (_getAll2OnAfterAsync != null) await _getAll2OnAfterAsync(__result).ConfigureAwait(false); @@ -280,11 +302,11 @@ public Task GetAll2Async() } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetByArgsAsync(PersonArgs? args, PagingArgs? paging) { return ManagerInvoker.Current.InvokeAsync(this, async () => @@ -306,11 +328,11 @@ public Task GetByArgsAsync(PersonArgs? args, PagingArgs? } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetDetailByArgsAsync(PersonArgs? args, PagingArgs? paging) { return ManagerInvoker.Current.InvokeAsync(this, async () => @@ -366,6 +388,12 @@ public Task MarkAsync() return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Update; + if (_markOnPreValidateAsync != null) await _markOnPreValidateAsync().ConfigureAwait(false); + + MultiValidator.Create() + .Additional((__mv) => _markOnValidate?.Invoke(__mv)) + .Run().ThrowOnError(); + if (_markOnBeforeAsync != null) await _markOnBeforeAsync().ConfigureAwait(false); await _dataService.MarkAsync().ConfigureAwait(false); if (_markOnAfterAsync != null) await _markOnAfterAsync().ConfigureAwait(false); @@ -375,7 +403,7 @@ public Task MarkAsync() /// /// Get at specified . /// - /// The Args (see ). + /// The Args (see ). /// A resultant . public Task MapAsync(MapArgs? args) { @@ -399,12 +427,18 @@ public Task MapAsync(MapArgs? args) /// /// Get no arguments. /// - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetNoArgsAsync() { return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Read; + if (_getNoArgsOnPreValidateAsync != null) await _getNoArgsOnPreValidateAsync().ConfigureAwait(false); + + MultiValidator.Create() + .Additional((__mv) => _getNoArgsOnValidate?.Invoke(__mv)) + .Run().ThrowOnError(); + if (_getNoArgsOnBeforeAsync != null) await _getNoArgsOnBeforeAsync().ConfigureAwait(false); var __result = await _dataService.GetNoArgsAsync().ConfigureAwait(false); if (_getNoArgsOnAfterAsync != null) await _getNoArgsOnAfterAsync(__result).ConfigureAwait(false); @@ -413,10 +447,10 @@ public Task MapAsync(MapArgs? args) } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetDetailAsync(Guid id) { return ManagerInvoker.Current.InvokeAsync(this, async () => @@ -438,11 +472,11 @@ public Task MapAsync(MapArgs? args) } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. - /// A refreshed object. + /// The updated . public Task UpdateDetailAsync(PersonDetail value, Guid id) { value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); @@ -469,8 +503,8 @@ public Task UpdateDetailAsync(PersonDetail value, Guid id) /// /// Actually validating the FromBody parameter generation. /// - /// The Person (see ). - public Task AddAsync(Person? person) + /// The Person (see ). + public Task AddAsync(Person person) { return ManagerInvoker.Current.InvokeAsync(this, async () => { @@ -488,6 +522,12 @@ public Task DataSvcCustomAsync() return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Unspecified; + if (_dataSvcCustomOnPreValidateAsync != null) await _dataSvcCustomOnPreValidateAsync().ConfigureAwait(false); + + MultiValidator.Create() + .Additional((__mv) => _dataSvcCustomOnValidate?.Invoke(__mv)) + .Run().ThrowOnError(); + if (_dataSvcCustomOnBeforeAsync != null) await _dataSvcCustomOnBeforeAsync().ConfigureAwait(false); var __result = await _dataService.DataSvcCustomAsync().ConfigureAwait(false); if (_dataSvcCustomOnAfterAsync != null) await _dataSvcCustomOnAfterAsync(__result).ConfigureAwait(false); @@ -498,14 +538,13 @@ public Task DataSvcCustomAsync() /// /// Validate a Manager Custom generation. /// - /// The selected object where found; otherwise, null. + /// The selected where found. public Task ManagerCustomAsync() { return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Read; - var __result = await ManagerCustomOnImplementationAsync().ConfigureAwait(false); - return Cleaner.Clean(__result); + return Cleaner.Clean(await ManagerCustomOnImplementationAsync().ConfigureAwait(false)); }); } @@ -513,7 +552,7 @@ public Task DataSvcCustomAsync() /// Get Null. /// /// The Name. - /// A resultant . + /// A resultant . public Task GetNullAsync(string? name) { return ManagerInvoker.Current.InvokeAsync(this, async () => @@ -534,11 +573,11 @@ public Task DataSvcCustomAsync() } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetByArgsWithEfAsync(PersonArgs? args, PagingArgs? paging) { return ManagerInvoker.Current.InvokeAsync(this, async () => @@ -567,6 +606,12 @@ public Task ThrowErrorAsync() return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Unspecified; + if (_throwErrorOnPreValidateAsync != null) await _throwErrorOnPreValidateAsync().ConfigureAwait(false); + + MultiValidator.Create() + .Additional((__mv) => _throwErrorOnValidate?.Invoke(__mv)) + .Run().ThrowOnError(); + if (_throwErrorOnBeforeAsync != null) await _throwErrorOnBeforeAsync().ConfigureAwait(false); await _dataService.ThrowErrorAsync().ConfigureAwait(false); if (_throwErrorOnAfterAsync != null) await _throwErrorOnAfterAsync().ConfigureAwait(false); @@ -574,10 +619,10 @@ public Task ThrowErrorAsync() } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetWithEfAsync(Guid id) { return ManagerInvoker.Current.InvokeAsync(this, async () => @@ -599,10 +644,10 @@ public Task ThrowErrorAsync() } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateWithEfAsync(Person value) { value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); @@ -626,11 +671,11 @@ public Task CreateWithEfAsync(Person value) } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. - /// A refreshed object. + /// The updated . public Task UpdateWithEfAsync(Person value, Guid id) { value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); @@ -655,7 +700,7 @@ public Task UpdateWithEfAsync(Person value, Guid id) } /// - /// Deletes the object. + /// Deletes the specified . /// /// The identifier. public Task DeleteWithEfAsync(Guid id) diff --git a/samples/Demo/Beef.Demo.Business/Generated/ProductManager.cs b/samples/Demo/Beef.Demo.Business/Generated/ProductManager.cs index 11839a239..1b2dda435 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/ProductManager.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/ProductManager.cs @@ -14,14 +14,14 @@ using Beef.Entities; using Beef.Validation; using Beef.Demo.Common.Entities; -using Beef.Demo.Business.Validation; using Beef.Demo.Business.DataSvc; +using Beef.Demo.Business.Validation; using RefDataNamespace = Beef.Demo.Common.Entities; namespace Beef.Demo.Business { /// - /// Provides the Product business functionality. + /// Provides the business functionality. /// public partial class ProductManager : IProductManager { @@ -31,48 +31,40 @@ public partial class ProductManager : IProductManager /// Initializes a new instance of the class. /// /// The . - public ProductManager(IProductDataSvc dataService) { _dataService = Check.NotNull(dataService, nameof(dataService)); ProductManagerCtor(); } + public ProductManager(IProductDataSvc dataService) + { _dataService = Check.NotNull(dataService, nameof(dataService)); ProductManagerCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void ProductManagerCtor(); + partial void ProductManagerCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(int id) { return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Read; Cleaner.CleanUp(id); - MultiValidator.Create() - .Add(id.Validate(nameof(id)).Mandatory()) - .Run().ThrowOnError(); - + id.Validate(nameof(id)).Mandatory().Run().ThrowOnError(); return Cleaner.Clean(await _dataService.GetAsync(id).ConfigureAwait(false)); }); } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetByArgsAsync(ProductArgs? args, PagingArgs? paging) { return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Read; Cleaner.CleanUp(args); - MultiValidator.Create() - .Add(args.Validate(nameof(args)).Entity(ProductArgsValidator.Default)) - .Run().ThrowOnError(); - + args.Validate(nameof(args)).Entity(ProductArgsValidator.Default).Run().ThrowOnError(); return Cleaner.Clean(await _dataService.GetByArgsAsync(args, paging).ConfigureAwait(false)); }); } diff --git a/samples/Demo/Beef.Demo.Business/Generated/ReferenceDataProvider.cs b/samples/Demo/Beef.Demo.Business/Generated/ReferenceDataProvider.cs index a1d57d81b..6cd0ae6c4 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/ReferenceDataProvider.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/ReferenceDataProvider.cs @@ -20,7 +20,7 @@ namespace Beef.Demo.Business /// /// Provides the implementation using the corresponding data services. /// - public class ReferenceDataProvider : RefDataNamespace.ReferenceData + public partial class ReferenceDataProvider : RefDataNamespace.ReferenceData { private readonly IReferenceDataDataSvc _dataService; @@ -28,8 +28,10 @@ public class ReferenceDataProvider : RefDataNamespace.ReferenceData /// Initializes a new instance of the class. /// /// The . - public ReferenceDataProvider(IReferenceDataDataSvc dataService) => _dataService = Check.NotNull(dataService, nameof(dataService)); - + public ReferenceDataProvider(IReferenceDataDataSvc dataService) { _dataService = Check.NotNull(dataService, nameof(dataService)); ReferenceDataProviderCtor(); } + + partial void ReferenceDataProviderCtor(); // Enables the ReferenceDataProvider constructor to be extended. + #region Collections /// @@ -63,7 +65,7 @@ public class ReferenceDataProvider : RefDataNamespace.ReferenceData public override RefDataNamespace.CompanyCollection Company => (RefDataNamespace.CompanyCollection)this[typeof(RefDataNamespace.Company)]; #endregion - + /// /// Gets the for the associated . /// diff --git a/samples/Demo/Beef.Demo.Business/Generated/RobotManager.cs b/samples/Demo/Beef.Demo.Business/Generated/RobotManager.cs index d2e37ff33..06c4a7f29 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/RobotManager.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/RobotManager.cs @@ -14,14 +14,14 @@ using Beef.Entities; using Beef.Validation; using Beef.Demo.Common.Entities; -using Beef.Demo.Business.Validation; using Beef.Demo.Business.DataSvc; +using Beef.Demo.Business.Validation; using RefDataNamespace = Beef.Demo.Common.Entities; namespace Beef.Demo.Business { /// - /// Provides the Robot business functionality. + /// Provides the business functionality. /// public partial class RobotManager : IRobotManager { @@ -31,37 +31,32 @@ public partial class RobotManager : IRobotManager /// Initializes a new instance of the class. /// /// The . - private RobotManager(IRobotDataSvc dataService) { _dataService = Check.NotNull(dataService, nameof(dataService)); RobotManagerCtor(); } + private RobotManager(IRobotDataSvc dataService) + { _dataService = Check.NotNull(dataService, nameof(dataService)); RobotManagerCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void RobotManagerCtor(); + partial void RobotManagerCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(Guid id) { return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Read; Cleaner.CleanUp(id); - MultiValidator.Create() - .Add(id.Validate(nameof(id)).Mandatory()) - .Run().ThrowOnError(); - + id.Validate(nameof(id)).Mandatory().Run().ThrowOnError(); return Cleaner.Clean(await _dataService.GetAsync(id).ConfigureAwait(false)); }); } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(Robot value) { value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); @@ -70,20 +65,17 @@ public Task CreateAsync(Robot value) { ExecutionContext.Current.OperationType = OperationType.Create; Cleaner.CleanUp(value); - MultiValidator.Create() - .Add(value.Validate(nameof(value)).Entity(RobotValidator.Default)) - .Run().ThrowOnError(); - + value.Validate(nameof(value)).Entity(RobotValidator.Default).Run().ThrowOnError(); return Cleaner.Clean(await _dataService.CreateAsync(value).ConfigureAwait(false)); }); } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. - /// A refreshed object. + /// The updated . public Task UpdateAsync(Robot value, Guid id) { value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); @@ -93,16 +85,13 @@ public Task UpdateAsync(Robot value, Guid id) ExecutionContext.Current.OperationType = OperationType.Update; value.Id = id; Cleaner.CleanUp(value); - MultiValidator.Create() - .Add(value.Validate(nameof(value)).Entity(RobotValidator.Default)) - .Run().ThrowOnError(); - + value.Validate(nameof(value)).Entity(RobotValidator.Default).Run().ThrowOnError(); return Cleaner.Clean(await _dataService.UpdateAsync(value).ConfigureAwait(false)); }); } /// - /// Deletes the object. + /// Deletes the specified . /// /// The identifier. public Task DeleteAsync(Guid id) @@ -111,30 +100,24 @@ public Task DeleteAsync(Guid id) { ExecutionContext.Current.OperationType = OperationType.Delete; Cleaner.CleanUp(id); - MultiValidator.Create() - .Add(id.Validate(nameof(id)).Mandatory()) - .Run().ThrowOnError(); - + id.Validate(nameof(id)).Mandatory().Run().ThrowOnError(); await _dataService.DeleteAsync(id).ConfigureAwait(false); }); } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . - /// A . + /// The . public Task GetByArgsAsync(RobotArgs? args, PagingArgs? paging) { return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Read; Cleaner.CleanUp(args); - MultiValidator.Create() - .Add(args.Validate(nameof(args)).Entity(RobotArgsValidator.Default)) - .Run().ThrowOnError(); - + args.Validate(nameof(args)).Entity(RobotArgsValidator.Default).Run().ThrowOnError(); return Cleaner.Clean(await _dataService.GetByArgsAsync(args, paging).ConfigureAwait(false)); }); } diff --git a/samples/Demo/Beef.Demo.Business/Generated/TripPersonManager.cs b/samples/Demo/Beef.Demo.Business/Generated/TripPersonManager.cs index 5f51d278f..0cf399236 100644 --- a/samples/Demo/Beef.Demo.Business/Generated/TripPersonManager.cs +++ b/samples/Demo/Beef.Demo.Business/Generated/TripPersonManager.cs @@ -20,7 +20,7 @@ namespace Beef.Demo.Business { /// - /// Provides the TripPerson business functionality. + /// Provides the business functionality. /// public partial class TripPersonManager : ITripPersonManager { @@ -30,37 +30,32 @@ public partial class TripPersonManager : ITripPersonManager /// Initializes a new instance of the class. /// /// The . - public TripPersonManager(ITripPersonDataSvc dataService) { _dataService = Check.NotNull(dataService, nameof(dataService)); TripPersonManagerCtor(); } + public TripPersonManager(ITripPersonDataSvc dataService) + { _dataService = Check.NotNull(dataService, nameof(dataService)); TripPersonManagerCtor(); } - /// - /// Enables additional functionality to be added to the constructor. - /// - partial void TripPersonManagerCtor(); + partial void TripPersonManagerCtor(); // Enables additional functionality to be added to the constructor. /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier (username). - /// The selected object where found; otherwise, null. + /// The selected where found. public Task GetAsync(string? id) { return ManagerInvoker.Current.InvokeAsync(this, async () => { ExecutionContext.Current.OperationType = OperationType.Read; Cleaner.CleanUp(id); - MultiValidator.Create() - .Add(id.Validate(nameof(id)).Mandatory()) - .Run().ThrowOnError(); - + id.Validate(nameof(id)).Mandatory().Run().ThrowOnError(); return Cleaner.Clean(await _dataService.GetAsync(id).ConfigureAwait(false)); }); } /// - /// Creates the object. + /// Creates a new . /// - /// The object. - /// A refreshed object. + /// The . + /// The created . public Task CreateAsync(TripPerson value) { value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); @@ -69,20 +64,16 @@ public Task CreateAsync(TripPerson value) { ExecutionContext.Current.OperationType = OperationType.Create; Cleaner.CleanUp(value); - MultiValidator.Create() - .Add(value.Validate(nameof(value))) - .Run().ThrowOnError(); - return Cleaner.Clean(await _dataService.CreateAsync(value).ConfigureAwait(false)); }); } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier (username). - /// A refreshed object. + /// The updated . public Task UpdateAsync(TripPerson value, string? id) { value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); @@ -92,16 +83,12 @@ public Task UpdateAsync(TripPerson value, string? id) ExecutionContext.Current.OperationType = OperationType.Update; value.Id = id; Cleaner.CleanUp(value); - MultiValidator.Create() - .Add(value.Validate(nameof(value))) - .Run().ThrowOnError(); - return Cleaner.Clean(await _dataService.UpdateAsync(value).ConfigureAwait(false)); }); } /// - /// Deletes the object. + /// Deletes the specified . /// /// The identifier (username). public Task DeleteAsync(string? id) @@ -110,10 +97,7 @@ public Task DeleteAsync(string? id) { ExecutionContext.Current.OperationType = OperationType.Delete; Cleaner.CleanUp(id); - MultiValidator.Create() - .Add(id.Validate(nameof(id)).Mandatory()) - .Run().ThrowOnError(); - + id.Validate(nameof(id)).Mandatory().Run().ThrowOnError(); await _dataService.DeleteAsync(id).ConfigureAwait(false); }); } diff --git a/samples/Demo/Beef.Demo.Business/PersonManager.cs b/samples/Demo/Beef.Demo.Business/PersonManager.cs index 2efe4fd39..bd9c9042d 100644 --- a/samples/Demo/Beef.Demo.Business/PersonManager.cs +++ b/samples/Demo/Beef.Demo.Business/PersonManager.cs @@ -1,6 +1,8 @@ using Beef.Demo.Common.Entities; using System.Threading.Tasks; +#nullable enable + namespace Beef.Demo.Business { public partial class PersonManager @@ -11,9 +13,11 @@ private Task AddOnImplementationAsync(Person value) return Task.CompletedTask; } - private Task ManagerCustomOnImplementationAsync() + private Task ManagerCustomOnImplementationAsync() { - return Task.FromResult(null); + return Task.FromResult(null); } } -} \ No newline at end of file +} + +#nullable restore \ No newline at end of file diff --git a/samples/Demo/Beef.Demo.Business/Validation/PersonValidator.cs b/samples/Demo/Beef.Demo.Business/Validation/PersonValidator.cs index f48fd0d2f..4a77b3f4a 100644 --- a/samples/Demo/Beef.Demo.Business/Validation/PersonValidator.cs +++ b/samples/Demo/Beef.Demo.Business/Validation/PersonValidator.cs @@ -24,7 +24,7 @@ public PersonValidator() Property(x => x.LastName).Mandatory().Common(CommonValidators.Text); Property(x => x.Gender).Mandatory().IsValid(); Property(x => x.EyeColor).IsValid(); - Property(x => x.Birthday).Mandatory().CompareValue(CompareOperator.LessThanEqual, DateTime.Now, "Today"); + Property(x => x.Birthday).Mandatory().CompareValue(CompareOperator.LessThanEqual, _ => DateTime.Now, _ => "Today"); Property(x => x.Address).Entity(_addressValidator); } } diff --git a/samples/Demo/Beef.Demo.CodeGen/Beef.Demo.xml b/samples/Demo/Beef.Demo.CodeGen/Beef.Demo.xml index 01032ff53..3adb60c43 100644 --- a/samples/Demo/Beef.Demo.CodeGen/Beef.Demo.xml +++ b/samples/Demo/Beef.Demo.CodeGen/Beef.Demo.xml @@ -26,7 +26,7 @@ - + @@ -39,10 +39,10 @@ - + - + @@ -145,7 +145,7 @@ - + diff --git a/samples/Demo/Beef.Demo.CodeGen/Beef.RefData.xml b/samples/Demo/Beef.Demo.CodeGen/Beef.RefData.xml index 17dea2dfb..f2a07a9a5 100644 --- a/samples/Demo/Beef.Demo.CodeGen/Beef.RefData.xml +++ b/samples/Demo/Beef.Demo.CodeGen/Beef.RefData.xml @@ -6,9 +6,11 @@ + + - + diff --git a/samples/Demo/Beef.Demo.CodeGen/Properties/launchSettings.json b/samples/Demo/Beef.Demo.CodeGen/Properties/launchSettings.json index 9415713f0..2dab624d7 100644 --- a/samples/Demo/Beef.Demo.CodeGen/Properties/launchSettings.json +++ b/samples/Demo/Beef.Demo.CodeGen/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "Beef.Demo.CodeGen": { "commandName": "Project", - "commandLineArgs": "entity --expectNoChanges", + "commandLineArgs": "entity", "workingDirectory": "C:\\Users\\eric.sibly\\source\\repos\\Avanade\\Beef\\samples\\Demo\\Beef.Demo.CodeGen" } } diff --git a/samples/Demo/Beef.Demo.Common/Agents/Generated/ConfigAgent.cs b/samples/Demo/Beef.Demo.Common/Agents/Generated/ConfigAgent.cs index 84a481542..c4310a87a 100644 --- a/samples/Demo/Beef.Demo.Common/Agents/Generated/ConfigAgent.cs +++ b/samples/Demo/Beef.Demo.Common/Agents/Generated/ConfigAgent.cs @@ -1,5 +1,5 @@ /* - * This file is automatically generated; any changes will be lost. + * This file is automatically generated; any changes will be lost. */ #nullable enable @@ -19,7 +19,7 @@ namespace Beef.Demo.Common.Agents { /// - /// Defines the Config Web API agent. + /// Defines the Config Web API agent. /// public partial interface IConfigAgent { @@ -32,7 +32,7 @@ public partial interface IConfigAgent } /// - /// Provides the Config Web API agent. + /// Provides the Config Web API agent. /// public partial class ConfigAgent : WebApiAgentBase, IConfigAgent { @@ -47,11 +47,9 @@ public ConfigAgent(IWebApiAgentArgs args) : base(args) { } /// /// The optional . /// A . - public Task> GetEnvVarsAsync(WebApiRequestOptions? requestOptions = null) - { - return PostAsync("api/v1/envvars", requestOptions: requestOptions, + public Task> GetEnvVarsAsync(WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/envvars", requestOptions: requestOptions, args: Array.Empty()); - } } } diff --git a/samples/Demo/Beef.Demo.Common/Agents/Generated/ContactAgent.cs b/samples/Demo/Beef.Demo.Common/Agents/Generated/ContactAgent.cs index 920a6bccc..11c5f55c1 100644 --- a/samples/Demo/Beef.Demo.Common/Agents/Generated/ContactAgent.cs +++ b/samples/Demo/Beef.Demo.Common/Agents/Generated/ContactAgent.cs @@ -1,5 +1,5 @@ /* - * This file is automatically generated; any changes will be lost. + * This file is automatically generated; any changes will be lost. */ #nullable enable @@ -19,44 +19,44 @@ namespace Beef.Demo.Common.Agents { /// - /// Defines the Contact Web API agent. + /// Defines the Web API agent. /// public partial interface IContactAgent { /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The optional . /// A . Task> GetAllAsync(WebApiRequestOptions? requestOptions = null); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . /// A . - Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null); + Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Creates the object. + /// Creates a new . /// - /// The object. + /// The . /// The optional . /// A . Task> CreateAsync(Contact value, WebApiRequestOptions? requestOptions = null); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. /// The optional . /// A . Task> UpdateAsync(Contact value, Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. /// The optional . @@ -65,7 +65,7 @@ public partial interface IContactAgent } /// - /// Provides the Contact Web API agent. + /// Provides the Web API agent. /// public partial class ContactAgent : WebApiAgentBase, IContactAgent { @@ -76,70 +76,54 @@ public partial class ContactAgent : WebApiAgentBase, IContactAgent public ContactAgent(IWebApiAgentArgs args) : base(args) { } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The optional . /// A . - public Task> GetAllAsync(WebApiRequestOptions? requestOptions = null) - { - return GetCollectionResultAsync("api/v1/contacts", requestOptions: requestOptions, + public Task> GetAllAsync(WebApiRequestOptions? requestOptions = null) => + GetCollectionResultAsync("api/v1/contacts", requestOptions: requestOptions, args: Array.Empty()); - } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . /// A . - public Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null) - { - return GetAsync("api/v1/contacts/{id}", requestOptions: requestOptions, + public Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/contacts/{id}", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Creates the object. + /// Creates a new . /// - /// The object. + /// The . /// The optional . /// A . - public Task> CreateAsync(Contact value, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PostAsync("api/v1/contacts", value, requestOptions: requestOptions, + public Task> CreateAsync(Contact value, WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/contacts", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: Array.Empty()); - } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. /// The optional . /// A . - public Task> UpdateAsync(Contact value, Guid id, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PutAsync("api/v1/contacts/{id}", value, requestOptions: requestOptions, + public Task> UpdateAsync(Contact value, Guid id, WebApiRequestOptions? requestOptions = null) => + PutAsync("api/v1/contacts/{id}", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. /// The optional . /// A . - public Task DeleteAsync(Guid id, WebApiRequestOptions? requestOptions = null) - { - return DeleteAsync("api/v1/contacts/{id}", requestOptions: requestOptions, + public Task DeleteAsync(Guid id, WebApiRequestOptions? requestOptions = null) => + DeleteAsync("api/v1/contacts/{id}", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } } } diff --git a/samples/Demo/Beef.Demo.Common/Agents/Generated/GenderAgent.cs b/samples/Demo/Beef.Demo.Common/Agents/Generated/GenderAgent.cs index 077c29c09..944cf8bb7 100644 --- a/samples/Demo/Beef.Demo.Common/Agents/Generated/GenderAgent.cs +++ b/samples/Demo/Beef.Demo.Common/Agents/Generated/GenderAgent.cs @@ -1,5 +1,5 @@ /* - * This file is automatically generated; any changes will be lost. + * This file is automatically generated; any changes will be lost. */ #nullable enable @@ -19,30 +19,30 @@ namespace Beef.Demo.Common.Agents { /// - /// Defines the Gender Web API agent. + /// Defines the Web API agent. /// public partial interface IGenderAgent { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . /// A . - Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null); + Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Creates the object. + /// Creates a new . /// - /// The object. + /// The . /// The optional . /// A . Task> CreateAsync(Gender value, WebApiRequestOptions? requestOptions = null); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. /// The optional . /// A . @@ -50,7 +50,7 @@ public partial interface IGenderAgent } /// - /// Provides the Gender Web API agent. + /// Provides the Web API agent. /// public partial class GenderAgent : WebApiAgentBase, IGenderAgent { @@ -61,47 +61,35 @@ public partial class GenderAgent : WebApiAgentBase, IGenderAgent public GenderAgent(IWebApiAgentArgs args) : base(args) { } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . /// A . - public Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null) - { - return GetAsync("api/v1/demo/ref/genders/{id}", requestOptions: requestOptions, + public Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/demo/ref/genders/{id}", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Creates the object. + /// Creates a new . /// - /// The object. + /// The . /// The optional . /// A . - public Task> CreateAsync(Gender value, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PostAsync("api/v1/demo/ref/genders", value, requestOptions: requestOptions, + public Task> CreateAsync(Gender value, WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/demo/ref/genders", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: Array.Empty()); - } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. /// The optional . /// A . - public Task> UpdateAsync(Gender value, Guid id, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PutAsync("api/v1/demo/ref/genders/{id}", value, requestOptions: requestOptions, + public Task> UpdateAsync(Gender value, Guid id, WebApiRequestOptions? requestOptions = null) => + PutAsync("api/v1/demo/ref/genders/{id}", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } } } diff --git a/samples/Demo/Beef.Demo.Common/Agents/Generated/PersonAgent.cs b/samples/Demo/Beef.Demo.Common/Agents/Generated/PersonAgent.cs index 6fdf6d227..b8f02456f 100644 --- a/samples/Demo/Beef.Demo.Common/Agents/Generated/PersonAgent.cs +++ b/samples/Demo/Beef.Demo.Common/Agents/Generated/PersonAgent.cs @@ -1,5 +1,5 @@ /* - * This file is automatically generated; any changes will be lost. + * This file is automatically generated; any changes will be lost. */ #nullable enable @@ -19,20 +19,20 @@ namespace Beef.Demo.Common.Agents { /// - /// Defines the Person Web API agent. + /// Defines the Web API agent. /// public partial interface IPersonAgent { /// - /// Creates the object. + /// Creates a new . /// - /// The object. + /// The . /// The optional . /// A . Task> CreateAsync(Person value, WebApiRequestOptions? requestOptions = null); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. /// The optional . @@ -40,34 +40,34 @@ public partial interface IPersonAgent Task DeleteAsync(Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . /// A . - Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null); + Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. /// The optional . /// A . Task> UpdateAsync(Person value, Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Patches the object. + /// Patches an existing . /// /// The . - /// The JSON patch value. + /// The that contains the patch content for the . /// The identifier. /// The optional . /// A . Task> PatchAsync(WebApiPatchOption patchOption, JToken value, Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The . /// The optional . @@ -75,25 +75,25 @@ public partial interface IPersonAgent Task> GetAllAsync(PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The optional . /// A . Task> GetAll2Async(WebApiRequestOptions? requestOptions = null); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . Task> GetByArgsAsync(PersonArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . @@ -118,7 +118,7 @@ public partial interface IPersonAgent /// /// Get at specified . /// - /// The Args (see ). + /// The Args (see ). /// The optional . /// A . Task> MapAsync(MapArgs? args, WebApiRequestOptions? requestOptions = null); @@ -128,30 +128,30 @@ public partial interface IPersonAgent /// /// The optional . /// A . - Task> GetNoArgsAsync(WebApiRequestOptions? requestOptions = null); + Task> GetNoArgsAsync(WebApiRequestOptions? requestOptions = null); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . /// A . - Task> GetDetailAsync(Guid id, WebApiRequestOptions? requestOptions = null); + Task> GetDetailAsync(Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. /// The optional . /// A . Task> UpdateDetailAsync(PersonDetail value, Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Patches the object. + /// Patches an existing . /// /// The . - /// The JSON patch value. + /// The that contains the patch content for the . /// The identifier. /// The optional . /// A . @@ -160,10 +160,10 @@ public partial interface IPersonAgent /// /// Actually validating the FromBody parameter generation. /// - /// The Person (see ). + /// The Person (see ). /// The optional . /// A . - Task AddAsync(Person? person, WebApiRequestOptions? requestOptions = null); + Task AddAsync(Person person, WebApiRequestOptions? requestOptions = null); /// /// Get Null. @@ -174,9 +174,9 @@ public partial interface IPersonAgent Task> GetNullAsync(string? name, WebApiRequestOptions? requestOptions = null); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . @@ -190,32 +190,32 @@ public partial interface IPersonAgent Task ThrowErrorAsync(WebApiRequestOptions? requestOptions = null); /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . /// A . - Task> GetWithEfAsync(Guid id, WebApiRequestOptions? requestOptions = null); + Task> GetWithEfAsync(Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Creates the object. + /// Creates a new . /// - /// The object. + /// The . /// The optional . /// A . Task> CreateWithEfAsync(Person value, WebApiRequestOptions? requestOptions = null); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. /// The optional . /// A . Task> UpdateWithEfAsync(Person value, Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. /// The optional . @@ -223,10 +223,10 @@ public partial interface IPersonAgent Task DeleteWithEfAsync(Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Patches the object. + /// Patches an existing . /// /// The . - /// The JSON patch value. + /// The that contains the patch content for the . /// The identifier. /// The optional . /// A . @@ -234,7 +234,7 @@ public partial interface IPersonAgent } /// - /// Provides the Person Web API agent. + /// Provides the Web API agent. /// public partial class PersonAgent : WebApiAgentBase, IPersonAgent { @@ -245,125 +245,98 @@ public partial class PersonAgent : WebApiAgentBase, IPersonAgent public PersonAgent(IWebApiAgentArgs args) : base(args) { } /// - /// Creates the object. + /// Creates a new . /// - /// The object. + /// The . /// The optional . /// A . - public Task> CreateAsync(Person value, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PostAsync("api/v1/persons", value, requestOptions: requestOptions, + public Task> CreateAsync(Person value, WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/persons", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: Array.Empty()); - } /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. /// The optional . /// A . - public Task DeleteAsync(Guid id, WebApiRequestOptions? requestOptions = null) - { - return DeleteAsync("api/v1/persons/{id}", requestOptions: requestOptions, + public Task DeleteAsync(Guid id, WebApiRequestOptions? requestOptions = null) => + DeleteAsync("api/v1/persons/{id}", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . /// A . - public Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null) - { - return GetAsync("api/v1/persons/{id}", requestOptions: requestOptions, + public Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/persons/{id}", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. /// The optional . /// A . - public Task> UpdateAsync(Person value, Guid id, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PutAsync("api/v1/persons/{id}", value, requestOptions: requestOptions, + public Task> UpdateAsync(Person value, Guid id, WebApiRequestOptions? requestOptions = null) => + PutAsync("api/v1/persons/{id}", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Patches the object. + /// Patches an existing . /// /// The . - /// The JSON patch value. + /// The that contains the patch content for the . /// The identifier. /// The optional . /// A . - public Task> PatchAsync(WebApiPatchOption patchOption, JToken value, Guid id, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PatchAsync("api/v1/persons/{id}", patchOption, value, requestOptions: requestOptions, + public Task> PatchAsync(WebApiPatchOption patchOption, JToken value, Guid id, WebApiRequestOptions? requestOptions = null) => + PatchAsync("api/v1/persons/{id}", patchOption, Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The . /// The optional . /// A . - public Task> GetAllAsync(PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) - { - return GetCollectionResultAsync("api/v1/persons/all", requestOptions: requestOptions, + public Task> GetAllAsync(PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) => + GetCollectionResultAsync("api/v1/persons/all", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiPagingArgsArg("paging", paging) }); - } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// /// The optional . /// A . - public Task> GetAll2Async(WebApiRequestOptions? requestOptions = null) - { - return GetCollectionResultAsync("api/v1/persons/allnopaging", requestOptions: requestOptions, + public Task> GetAll2Async(WebApiRequestOptions? requestOptions = null) => + GetCollectionResultAsync("api/v1/persons/allnopaging", requestOptions: requestOptions, args: Array.Empty()); - } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . - public Task> GetByArgsAsync(PersonArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) - { - return GetCollectionResultAsync("api/v1/persons", requestOptions: requestOptions, + public Task> GetByArgsAsync(PersonArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) => + GetCollectionResultAsync("api/v1/persons", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args, WebApiArgType.FromUriUseProperties), new WebApiPagingArgsArg("paging", paging) }); - } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . - public Task> GetDetailByArgsAsync(PersonArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) - { - return GetCollectionResultAsync("api/v1/persons/argsdetail", requestOptions: requestOptions, + public Task> GetDetailByArgsAsync(PersonArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) => + GetCollectionResultAsync("api/v1/persons/argsdetail", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args, WebApiArgType.FromUriUseProperties), new WebApiPagingArgsArg("paging", paging) }); - } /// /// Merge first into second. @@ -372,102 +345,80 @@ public Task> GetDetailByArgsAsyn /// The to identifier. /// The optional . /// A . - public Task> MergeAsync(Guid fromId, Guid toId, WebApiRequestOptions? requestOptions = null) - { - return PostAsync("api/v1/persons/merge", requestOptions: requestOptions, + public Task> MergeAsync(Guid fromId, Guid toId, WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/persons/merge", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("fromId", fromId), new WebApiArg("toId", toId) }); - } /// /// Mark . /// /// The optional . /// A . - public Task MarkAsync(WebApiRequestOptions? requestOptions = null) - { - return PostAsync("api/v1/persons/mark", requestOptions: requestOptions, + public Task MarkAsync(WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/persons/mark", requestOptions: requestOptions, args: Array.Empty()); - } /// /// Get at specified . /// - /// The Args (see ). + /// The Args (see ). /// The optional . /// A . - public Task> MapAsync(MapArgs? args, WebApiRequestOptions? requestOptions = null) - { - return PostAsync("api/v1/persons/map", requestOptions: requestOptions, + public Task> MapAsync(MapArgs? args, WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/persons/map", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args, WebApiArgType.FromUriUseProperties) }); - } /// /// Get no arguments. /// /// The optional . /// A . - public Task> GetNoArgsAsync(WebApiRequestOptions? requestOptions = null) - { - return GetAsync("api/v1/persons/noargsforme", requestOptions: requestOptions, + public Task> GetNoArgsAsync(WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/persons/noargsforme", requestOptions: requestOptions, args: Array.Empty()); - } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . /// A . - public Task> GetDetailAsync(Guid id, WebApiRequestOptions? requestOptions = null) - { - return GetAsync("api/v1/persons/{id}/detail", requestOptions: requestOptions, + public Task> GetDetailAsync(Guid id, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/persons/{id}/detail", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. /// The optional . /// A . - public Task> UpdateDetailAsync(PersonDetail value, Guid id, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PutAsync("api/v1/persons/{id}/detail", value, requestOptions: requestOptions, + public Task> UpdateDetailAsync(PersonDetail value, Guid id, WebApiRequestOptions? requestOptions = null) => + PutAsync("api/v1/persons/{id}/detail", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Patches the object. + /// Patches an existing . /// /// The . - /// The JSON patch value. + /// The that contains the patch content for the . /// The identifier. /// The optional . /// A . - public Task> PatchDetailAsync(WebApiPatchOption patchOption, JToken value, Guid id, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PatchAsync("api/v1/persons/{id}/detail", patchOption, value, requestOptions: requestOptions, + public Task> PatchDetailAsync(WebApiPatchOption patchOption, JToken value, Guid id, WebApiRequestOptions? requestOptions = null) => + PatchAsync("api/v1/persons/{id}/detail", patchOption, Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// /// Actually validating the FromBody parameter generation. /// - /// The Person (see ). + /// The Person (see ). /// The optional . /// A . - public Task AddAsync(Person? person, WebApiRequestOptions? requestOptions = null) - { - return PostAsync("api/v1/persons/fromBody", requestOptions: requestOptions, - args: new WebApiArg[] { new WebApiArg("person", person, WebApiArgType.FromBody) }); - } + public Task AddAsync(Person person, WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/persons/fromBody", requestOptions: requestOptions, + args: new WebApiArg[] { new WebApiArg("person", person, WebApiArgType.FromBody) }); /// /// Get Null. @@ -475,107 +426,82 @@ public Task AddAsync(Person? person, WebApiRequestOptions? re /// The Name. /// The optional . /// A . - public Task> GetNullAsync(string? name, WebApiRequestOptions? requestOptions = null) - { - return GetAsync("api/v1/persons/null", requestOptions: requestOptions, + public Task> GetNullAsync(string? name, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/persons/null", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("name", name) }); - } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . - public Task> GetByArgsWithEfAsync(PersonArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) - { - return GetCollectionResultAsync("api/v1/persons/args", requestOptions: requestOptions, + public Task> GetByArgsWithEfAsync(PersonArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) => + GetCollectionResultAsync("api/v1/persons/args", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args, WebApiArgType.FromUriUseProperties), new WebApiPagingArgsArg("paging", paging) }); - } /// /// Throw Error. /// /// The optional . /// A . - public Task ThrowErrorAsync(WebApiRequestOptions? requestOptions = null) - { - return PostAsync("api/v1/persons/error", requestOptions: requestOptions, + public Task ThrowErrorAsync(WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/persons/error", requestOptions: requestOptions, args: Array.Empty()); - } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . /// A . - public Task> GetWithEfAsync(Guid id, WebApiRequestOptions? requestOptions = null) - { - return GetAsync("api/v1/persons/ef/{id}", requestOptions: requestOptions, + public Task> GetWithEfAsync(Guid id, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/persons/ef/{id}", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Creates the object. + /// Creates a new . /// - /// The object. + /// The . /// The optional . /// A . - public Task> CreateWithEfAsync(Person value, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PostAsync("api/v1/persons/ef", value, requestOptions: requestOptions, + public Task> CreateWithEfAsync(Person value, WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/persons/ef", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: Array.Empty()); - } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. /// The optional . /// A . - public Task> UpdateWithEfAsync(Person value, Guid id, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PutAsync("api/v1/persons/ef/{id}", value, requestOptions: requestOptions, + public Task> UpdateWithEfAsync(Person value, Guid id, WebApiRequestOptions? requestOptions = null) => + PutAsync("api/v1/persons/ef/{id}", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. /// The optional . /// A . - public Task DeleteWithEfAsync(Guid id, WebApiRequestOptions? requestOptions = null) - { - return DeleteAsync("api/v1/persons/ef/{id}", requestOptions: requestOptions, + public Task DeleteWithEfAsync(Guid id, WebApiRequestOptions? requestOptions = null) => + DeleteAsync("api/v1/persons/ef/{id}", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Patches the object. + /// Patches an existing . /// /// The . - /// The JSON patch value. + /// The that contains the patch content for the . /// The identifier. /// The optional . /// A . - public Task> PatchWithEfAsync(WebApiPatchOption patchOption, JToken value, Guid id, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PatchAsync("api/v1/persons/ef/{id}", patchOption, value, requestOptions: requestOptions, + public Task> PatchWithEfAsync(WebApiPatchOption patchOption, JToken value, Guid id, WebApiRequestOptions? requestOptions = null) => + PatchAsync("api/v1/persons/ef/{id}", patchOption, Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } } } diff --git a/samples/Demo/Beef.Demo.Common/Agents/Generated/ProductAgent.cs b/samples/Demo/Beef.Demo.Common/Agents/Generated/ProductAgent.cs index 0b98345ea..fe9a637eb 100644 --- a/samples/Demo/Beef.Demo.Common/Agents/Generated/ProductAgent.cs +++ b/samples/Demo/Beef.Demo.Common/Agents/Generated/ProductAgent.cs @@ -1,5 +1,5 @@ /* - * This file is automatically generated; any changes will be lost. + * This file is automatically generated; any changes will be lost. */ #nullable enable @@ -19,22 +19,22 @@ namespace Beef.Demo.Common.Agents { /// - /// Defines the Product Web API agent. + /// Defines the Web API agent. /// public partial interface IProductAgent { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . /// A . - Task> GetAsync(int id, WebApiRequestOptions? requestOptions = null); + Task> GetAsync(int id, WebApiRequestOptions? requestOptions = null); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . @@ -42,7 +42,7 @@ public partial interface IProductAgent } /// - /// Provides the Product Web API agent. + /// Provides the Web API agent. /// public partial class ProductAgent : WebApiAgentBase, IProductAgent { @@ -53,29 +53,25 @@ public partial class ProductAgent : WebApiAgentBase, IProductAgent public ProductAgent(IWebApiAgentArgs args) : base(args) { } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . /// A . - public Task> GetAsync(int id, WebApiRequestOptions? requestOptions = null) - { - return GetAsync("api/v1/products/{id}", requestOptions: requestOptions, + public Task> GetAsync(int id, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/products/{id}", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . - public Task> GetByArgsAsync(ProductArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) - { - return GetCollectionResultAsync("api/v1/products", requestOptions: requestOptions, + public Task> GetByArgsAsync(ProductArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) => + GetCollectionResultAsync("api/v1/products", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args, WebApiArgType.FromUriUseProperties), new WebApiPagingArgsArg("paging", paging) }); - } } } diff --git a/samples/Demo/Beef.Demo.Common/Agents/Generated/ReferenceDataAgent.cs b/samples/Demo/Beef.Demo.Common/Agents/Generated/ReferenceDataAgent.cs index fc563f6b1..f343dd3a9 100644 --- a/samples/Demo/Beef.Demo.Common/Agents/Generated/ReferenceDataAgent.cs +++ b/samples/Demo/Beef.Demo.Common/Agents/Generated/ReferenceDataAgent.cs @@ -23,7 +23,7 @@ namespace Beef.Demo.Common.Agents public partial interface IReferenceDataAgent { /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -31,7 +31,7 @@ public partial interface IReferenceDataAgent Task> CountryGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -39,7 +39,7 @@ public partial interface IReferenceDataAgent Task> USStateGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -47,7 +47,7 @@ public partial interface IReferenceDataAgent Task> GenderGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -55,7 +55,7 @@ public partial interface IReferenceDataAgent Task> EyeColorGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -63,7 +63,7 @@ public partial interface IReferenceDataAgent Task> PowerSourceGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -92,7 +92,7 @@ public partial class ReferenceDataAgent : WebApiAgentBase, IReferenceDataAgent public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -101,7 +101,7 @@ public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } GetAsync("api/v1/demo/ref/countries", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -110,7 +110,7 @@ public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } GetAsync("api/v1/demo/ref/usStates", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -119,7 +119,7 @@ public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } GetAsync("api/v1/demo/ref/genders", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -128,7 +128,7 @@ public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } GetAsync("api/v1/demo/ref/eyeColors", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -137,7 +137,7 @@ public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } GetAsync("api/v1/demo/ref/powerSources", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); /// - /// Gets all of the objects that match the filter arguments. + /// Gets all of the items that match the filter arguments. /// /// The optional arguments. /// The optional . @@ -148,7 +148,7 @@ public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } /// /// Gets the reference data entries for the specified entities and codes from the query string; e.g: api/v1/demo/ref?entity=codeX,codeY&entity2=codeZ&entity3 /// - /// The list of reference data names. + /// The optional list of reference data names. /// The optional . /// A . /// The reference data objects will need to be manually extracted from the corresponding response content. diff --git a/samples/Demo/Beef.Demo.Common/Agents/Generated/ReferenceDataAgentProvider.cs b/samples/Demo/Beef.Demo.Common/Agents/Generated/ReferenceDataAgentProvider.cs index e0b314fd5..f5f112d1a 100644 --- a/samples/Demo/Beef.Demo.Common/Agents/Generated/ReferenceDataAgentProvider.cs +++ b/samples/Demo/Beef.Demo.Common/Agents/Generated/ReferenceDataAgentProvider.cs @@ -23,7 +23,7 @@ namespace Beef.Demo.Common.Agents /// /// Provides the implementation using the corresponding Web API agent. /// - public class ReferenceDataAgentProvider : RefDataNamespace.ReferenceData + public partial class ReferenceDataAgentProvider : RefDataNamespace.ReferenceData { private readonly Dictionary _nameDict = new Dictionary(); private readonly Dictionary _typeDict = new Dictionary(); @@ -64,50 +64,53 @@ public ReferenceDataAgentProvider(IReferenceDataAgent agent) _typeDict.Add(typeof(RefDataNamespace.Company), nameof(Company)); _cacheDict.Add(typeof(RefDataNamespace.Company), new ReferenceDataCache(() => _agent.CompanyGetAllAsync().ContinueWith((t) => t.Result.Value, TaskScheduler.Current))); + ReferenceDataAgentProviderCtor(); } + partial void ReferenceDataAgentProviderCtor(); // Enables the ReferenceDataAgentProvider constructor to be extended. + #endregion #region Collections - /// + /// /// Gets the . /// /// The . public override RefDataNamespace.CountryCollection Country => (RefDataNamespace.CountryCollection)this[typeof(RefDataNamespace.Country)]; - /// + /// /// Gets the . /// /// The . public override RefDataNamespace.USStateCollection USState => (RefDataNamespace.USStateCollection)this[typeof(RefDataNamespace.USState)]; - /// + /// /// Gets the . /// /// The . public override RefDataNamespace.GenderCollection Gender => (RefDataNamespace.GenderCollection)this[typeof(RefDataNamespace.Gender)]; - /// + /// /// Gets the . /// /// The . public override RefDataNamespace.EyeColorCollection EyeColor => (RefDataNamespace.EyeColorCollection)this[typeof(RefDataNamespace.EyeColor)]; - /// + /// /// Gets the . /// /// The . public override RefDataNamespace.PowerSourceCollection PowerSource => (RefDataNamespace.PowerSourceCollection)this[typeof(RefDataNamespace.PowerSource)]; - /// + /// /// Gets the . /// /// The . public override RefDataNamespace.CompanyCollection Company => (RefDataNamespace.CompanyCollection)this[typeof(RefDataNamespace.Company)]; #endregion - + #region This/GetCache/PrefetchAsync /// diff --git a/samples/Demo/Beef.Demo.Common/Agents/Generated/RobotAgent.cs b/samples/Demo/Beef.Demo.Common/Agents/Generated/RobotAgent.cs index 73ee1e224..0b5d68111 100644 --- a/samples/Demo/Beef.Demo.Common/Agents/Generated/RobotAgent.cs +++ b/samples/Demo/Beef.Demo.Common/Agents/Generated/RobotAgent.cs @@ -1,5 +1,5 @@ /* - * This file is automatically generated; any changes will be lost. + * This file is automatically generated; any changes will be lost. */ #nullable enable @@ -19,47 +19,47 @@ namespace Beef.Demo.Common.Agents { /// - /// Defines the Robot Web API agent. + /// Defines the Web API agent. /// public partial interface IRobotAgent { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . /// A . - Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null); + Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Creates the object. + /// Creates a new . /// - /// The object. + /// The . /// The optional . /// A . Task> CreateAsync(Robot value, WebApiRequestOptions? requestOptions = null); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. /// The optional . /// A . Task> UpdateAsync(Robot value, Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Patches the object. + /// Patches an existing . /// /// The . - /// The JSON patch value. + /// The that contains the patch content for the . /// The identifier. /// The optional . /// A . Task> PatchAsync(WebApiPatchOption patchOption, JToken value, Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. /// The optional . @@ -67,9 +67,9 @@ public partial interface IRobotAgent Task DeleteAsync(Guid id, WebApiRequestOptions? requestOptions = null); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . @@ -86,7 +86,7 @@ public partial interface IRobotAgent } /// - /// Provides the Robot Web API agent. + /// Provides the Web API agent. /// public partial class RobotAgent : WebApiAgentBase, IRobotAgent { @@ -97,89 +97,68 @@ public partial class RobotAgent : WebApiAgentBase, IRobotAgent public RobotAgent(IWebApiAgentArgs args) : base(args) { } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . /// A . - public Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null) - { - return GetAsync("api/v1/robots/{id}", requestOptions: requestOptions, + public Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/robots/{id}", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Creates the object. + /// Creates a new . /// - /// The object. + /// The . /// The optional . /// A . - public Task> CreateAsync(Robot value, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PostAsync("api/v1/robots", value, requestOptions: requestOptions, + public Task> CreateAsync(Robot value, WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/robots", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: Array.Empty()); - } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. /// The optional . /// A . - public Task> UpdateAsync(Robot value, Guid id, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PutAsync("api/v1/robots/{id}", value, requestOptions: requestOptions, + public Task> UpdateAsync(Robot value, Guid id, WebApiRequestOptions? requestOptions = null) => + PutAsync("api/v1/robots/{id}", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Patches the object. + /// Patches an existing . /// /// The . - /// The JSON patch value. + /// The that contains the patch content for the . /// The identifier. /// The optional . /// A . - public Task> PatchAsync(WebApiPatchOption patchOption, JToken value, Guid id, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PatchAsync("api/v1/robots/{id}", patchOption, value, requestOptions: requestOptions, + public Task> PatchAsync(WebApiPatchOption patchOption, JToken value, Guid id, WebApiRequestOptions? requestOptions = null) => + PatchAsync("api/v1/robots/{id}", patchOption, Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. /// The optional . /// A . - public Task DeleteAsync(Guid id, WebApiRequestOptions? requestOptions = null) - { - return DeleteAsync("api/v1/robots/{id}", requestOptions: requestOptions, + public Task DeleteAsync(Guid id, WebApiRequestOptions? requestOptions = null) => + DeleteAsync("api/v1/robots/{id}", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . - public Task> GetByArgsAsync(RobotArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) - { - return GetCollectionResultAsync("api/v1/robots", requestOptions: requestOptions, + public Task> GetByArgsAsync(RobotArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) => + GetCollectionResultAsync("api/v1/robots", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args, WebApiArgType.FromUriUseProperties), new WebApiPagingArgsArg("paging", paging) }); - } /// /// Raises a change event. @@ -188,11 +167,9 @@ public Task> GetByArgsAsync(RobotArgs? /// The Power Source (see ). /// The optional . /// A . - public Task RaisePowerSourceChangeAsync(Guid id, RefDataNamespace.PowerSource? powerSource, WebApiRequestOptions? requestOptions = null) - { - return PostAsync("api/v1/robots/{id}/powerSource/{powerSource}", requestOptions: requestOptions, + public Task RaisePowerSourceChangeAsync(Guid id, RefDataNamespace.PowerSource? powerSource, WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/robots/{id}/powerSource/{powerSource}", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id), new WebApiArg("powerSource", powerSource) }); - } } } diff --git a/samples/Demo/Beef.Demo.Common/Agents/Generated/TripPersonAgent.cs b/samples/Demo/Beef.Demo.Common/Agents/Generated/TripPersonAgent.cs index 4091b88a0..38c05f6d8 100644 --- a/samples/Demo/Beef.Demo.Common/Agents/Generated/TripPersonAgent.cs +++ b/samples/Demo/Beef.Demo.Common/Agents/Generated/TripPersonAgent.cs @@ -1,5 +1,5 @@ /* - * This file is automatically generated; any changes will be lost. + * This file is automatically generated; any changes will be lost. */ #nullable enable @@ -19,37 +19,37 @@ namespace Beef.Demo.Common.Agents { /// - /// Defines the Trip Person Web API agent. + /// Defines the Web API agent. /// public partial interface ITripPersonAgent { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier (username). /// The optional . /// A . - Task> GetAsync(string? id, WebApiRequestOptions? requestOptions = null); + Task> GetAsync(string? id, WebApiRequestOptions? requestOptions = null); /// - /// Creates the object. + /// Creates a new . /// - /// The object. + /// The . /// The optional . /// A . Task> CreateAsync(TripPerson value, WebApiRequestOptions? requestOptions = null); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier (username). /// The optional . /// A . Task> UpdateAsync(TripPerson value, string? id, WebApiRequestOptions? requestOptions = null); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier (username). /// The optional . @@ -58,7 +58,7 @@ public partial interface ITripPersonAgent } /// - /// Provides the Trip Person Web API agent. + /// Provides the Web API agent. /// public partial class TripPersonAgent : WebApiAgentBase, ITripPersonAgent { @@ -69,59 +69,45 @@ public partial class TripPersonAgent : WebApiAgentBase, ITripPersonAgent public TripPersonAgent(IWebApiAgentArgs args) : base(args) { } /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier (username). /// The optional . /// A . - public Task> GetAsync(string? id, WebApiRequestOptions? requestOptions = null) - { - return GetAsync("api/v1/tripPeople/{id}", requestOptions: requestOptions, + public Task> GetAsync(string? id, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/tripPeople/{id}", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Creates the object. + /// Creates a new . /// - /// The object. + /// The . /// The optional . /// A . - public Task> CreateAsync(TripPerson value, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PostAsync("api/v1/tripPeople", value, requestOptions: requestOptions, + public Task> CreateAsync(TripPerson value, WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/tripPeople", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: Array.Empty()); - } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier (username). /// The optional . /// A . - public Task> UpdateAsync(TripPerson value, string? id, WebApiRequestOptions? requestOptions = null) - { - if (value == null) - throw new ArgumentNullException(nameof(value)); - - return PutAsync("api/v1/tripPeople/{id}", value, requestOptions: requestOptions, + public Task> UpdateAsync(TripPerson value, string? id, WebApiRequestOptions? requestOptions = null) => + PutAsync("api/v1/tripPeople/{id}", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier (username). /// The optional . /// A . - public Task DeleteAsync(string? id, WebApiRequestOptions? requestOptions = null) - { - return DeleteAsync("api/v1/tripPeople/{id}", requestOptions: requestOptions, + public Task DeleteAsync(string? id, WebApiRequestOptions? requestOptions = null) => + DeleteAsync("api/v1/tripPeople/{id}", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("id", id) }); - } } } diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/Address.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/Address.cs index 7be930af4..1e57dd10f 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/Address.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/Address.cs @@ -41,7 +41,7 @@ public partial class Address : EntityBase, IEquatable
public string? Street { get => _street; - set => SetValue(ref _street, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Street)); + set => SetValue(ref _street, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Street)); } /// @@ -52,7 +52,7 @@ public string? Street public string? City { get => _city; - set => SetValue(ref _city, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(City)); + set => SetValue(ref _city, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(City)); } #endregion @@ -64,29 +64,23 @@ public string? City /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is Address val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is Address val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(Address? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(Address? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(Street, obj.Street) - && Equals(City, obj.City); + return base.Equals((object)value) + && Equals(Street, value.Street) + && Equals(City, value.City); } /// @@ -106,9 +100,9 @@ public bool Equals(Address? obj) public static bool operator != (Address? a, Address? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -118,7 +112,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -137,8 +131,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(Address from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); Street = from.Street; @@ -146,9 +140,9 @@ public void CopyFrom(Address from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -177,7 +171,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -200,7 +194,7 @@ public override bool IsInitial partial void OnAfterCopyFrom(Address from); #endregion - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/Company.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/Company.cs index 724c63e54..92b53e7bb 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/Company.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/Company.cs @@ -35,7 +35,7 @@ public partial class Company : ReferenceDataBaseGuid public string? ExternalCode { get => GetMapping(nameof(ExternalCode)); - set { var __externalCode = GetMapping(nameof(ExternalCode)) ?? default; SetValue(ref __externalCode, value, true, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ExternalCode)); SetMapping(nameof(ExternalCode), __externalCode!); } + set { var __externalCode = GetMapping(nameof(ExternalCode)) ?? default; SetValue(ref __externalCode, value, true, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ExternalCode)); SetMapping(nameof(ExternalCode), __externalCode!); } } #endregion @@ -48,10 +48,7 @@ public string? ExternalCode /// The Id. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator Company(Guid id) - { - return ConvertFromId(id); - } + public static implicit operator Company(Guid id) => ConvertFromId(id); /// /// An implicit cast from a Code to a . @@ -59,13 +56,10 @@ public static implicit operator Company(Guid id) /// The Code. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator Company(string? code) - { - return ConvertFromCode(code); - } + public static implicit operator Company(string? code) => ConvertFromCode(code); #endregion - + #region ICopyFrom /// @@ -84,17 +78,17 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(Company from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((ReferenceDataBaseGuid)from); ExternalCode = from.ExternalCode; OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -121,7 +115,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -133,7 +127,7 @@ public override bool IsInitial if (!base.IsInitial) return false; - return true; + return Cleaner.IsInitial(ExternalCode); } } @@ -146,29 +140,29 @@ public override bool IsInitial partial void OnAfterCopyFrom(Company from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class CompanyCollection : ReferenceDataCollectionBase { - #region Constructors - /// /// Initializes a new instance of the class. /// - public CompanyCollection(){ } + public CompanyCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public CompanyCollection(IEnumerable entities) => AddRange(entities); - - #endregion } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/Contact.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/Contact.cs index 4d845bf4a..5aba5ed83 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/Contact.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/Contact.cs @@ -37,12 +37,12 @@ public partial class Contact : EntityBase, IGuidIdentifier, IEquatable /// /// Gets or sets the identifier. /// - [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Include)] [Display(Name="Identifier")] public Guid Id { get => _id; - set => SetValue(ref _id, value, false, false, nameof(Id)); + set => SetValue(ref _id, value, false, false, nameof(Id)); } /// @@ -53,7 +53,7 @@ public Guid Id public string? FirstName { get => _firstName; - set => SetValue(ref _firstName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(FirstName)); + set => SetValue(ref _firstName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(FirstName)); } /// @@ -64,13 +64,13 @@ public string? FirstName public string? LastName { get => _lastName; - set => SetValue(ref _lastName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(LastName)); + set => SetValue(ref _lastName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(LastName)); } #endregion - #region UniqueKey - + #region IUniqueKey + /// /// Indicates whether the has a value. /// @@ -80,20 +80,17 @@ public string? LastName /// Gets the list of property names that represent the unique key. /// public override string[] UniqueKeyProperties => new string[] { nameof(Id) }; - + /// /// Creates the . /// /// The . /// The . public static UniqueKey CreateUniqueKey(Guid id) => new UniqueKey(id); - + /// - /// Gets the . + /// Gets the (consists of the following property(s): ). /// - /// - /// The UniqueKey key consists of the following property(s): . - /// public override UniqueKey UniqueKey => new UniqueKey(Id); #endregion @@ -105,30 +102,24 @@ public string? LastName /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is Contact val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is Contact val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(Contact? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(Contact? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(Id, obj.Id) - && Equals(FirstName, obj.FirstName) - && Equals(LastName, obj.LastName); + return base.Equals((object)value) + && Equals(Id, value.Id) + && Equals(FirstName, value.FirstName) + && Equals(LastName, value.LastName); } /// @@ -148,9 +139,9 @@ public bool Equals(Contact? obj) public static bool operator != (Contact? a, Contact? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -161,7 +152,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -180,8 +171,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(Contact from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); Id = from.Id; @@ -190,9 +181,9 @@ public void CopyFrom(Contact from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -222,7 +213,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -246,31 +237,27 @@ public override bool IsInitial partial void OnAfterCopyFrom(Contact from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class ContactCollection : EntityBaseCollection { - #region Constructors - /// /// Initializes a new instance of the class. /// - public ContactCollection(){ } + public ContactCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public ContactCollection(IEnumerable entities) => AddRange(entities); - #endregion - - #region ICloneable - /// /// Creates a deep copy of the . /// @@ -278,31 +265,29 @@ public ContactCollection(){ } public override object Clone() { var clone = new ContactCollection(); - foreach (Contact item in this) + foreach (var item in this) { clone.Add((Contact)item.Clone()); } return clone; } - - #endregion - - #region Operator /// - /// An implicit cast from a to a . + /// An implicit cast from the to a corresponding . /// /// The . /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] public static implicit operator ContactCollection(ContactCollectionResult result) => result?.Result!; - - #endregion } + #endregion + + #region CollectionResult + /// - /// Represents a collection result. + /// Represents the collection result. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public class ContactCollectionResult : EntityCollectionResult @@ -313,7 +298,7 @@ public class ContactCollectionResult : EntityCollectionResult - /// Initializes a new instance of the class with default . + /// Initializes a new instance of the class with . /// /// The . public ContactCollectionResult(PagingArgs? paging) : base(paging) { } @@ -336,6 +321,8 @@ public override object Clone() return clone; } } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/Country.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/Country.cs index b9074178c..686cead8b 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/Country.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/Country.cs @@ -33,10 +33,7 @@ public partial class Country : ReferenceDataBaseGuid /// The Id. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator Country(Guid id) - { - return ConvertFromId(id); - } + public static implicit operator Country(Guid id) => ConvertFromId(id); /// /// An implicit cast from a Code to a . @@ -44,13 +41,10 @@ public static implicit operator Country(Guid id) /// The Code. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator Country(string? code) - { - return ConvertFromCode(code); - } + public static implicit operator Country(string? code) => ConvertFromCode(code); #endregion - + #region ICopyFrom /// @@ -69,16 +63,16 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(Country from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((ReferenceDataBaseGuid)from); OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -105,7 +99,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -130,29 +124,29 @@ public override bool IsInitial partial void OnAfterCopyFrom(Country from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class CountryCollection : ReferenceDataCollectionBase { - #region Constructors - /// /// Initializes a new instance of the class. /// - public CountryCollection(){ } + public CountryCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public CountryCollection(IEnumerable entities) => AddRange(entities); - - #endregion } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/EyeColor.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/EyeColor.cs index 0e0ed731e..fb7f82b69 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/EyeColor.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/EyeColor.cs @@ -33,10 +33,7 @@ public partial class EyeColor : ReferenceDataBaseGuid /// The Id. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator EyeColor(Guid id) - { - return ConvertFromId(id); - } + public static implicit operator EyeColor(Guid id) => ConvertFromId(id); /// /// An implicit cast from a Code to a . @@ -44,13 +41,10 @@ public static implicit operator EyeColor(Guid id) /// The Code. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator EyeColor(string? code) - { - return ConvertFromCode(code); - } + public static implicit operator EyeColor(string? code) => ConvertFromCode(code); #endregion - + #region ICopyFrom /// @@ -69,16 +63,16 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(EyeColor from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((ReferenceDataBaseGuid)from); OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -105,7 +99,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -130,29 +124,29 @@ public override bool IsInitial partial void OnAfterCopyFrom(EyeColor from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class EyeColorCollection : ReferenceDataCollectionBase { - #region Constructors - /// /// Initializes a new instance of the class. /// - public EyeColorCollection(){ } + public EyeColorCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public EyeColorCollection(IEnumerable entities) => AddRange(entities); - - #endregion } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/Gender.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/Gender.cs index 68d873ccd..c3b5f1040 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/Gender.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/Gender.cs @@ -25,6 +25,19 @@ namespace Beef.Demo.Common.Entities [ReferenceDataInterface(typeof(IReferenceData))] public partial class Gender : ReferenceDataBaseGuid { + #region Constants + + /// + /// Represents a constant value. + /// + public const string Female = "F"; + /// + /// Represents a constant value. + /// + public const string Male = "M"; + + #endregion + #region Privates private string? _alternateName; @@ -41,7 +54,7 @@ public partial class Gender : ReferenceDataBaseGuid public string? AlternateName { get => _alternateName; - set => SetValue(ref _alternateName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(AlternateName)); + set => SetValue(ref _alternateName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(AlternateName)); } /// @@ -51,7 +64,7 @@ public string? AlternateName public string? TripCode { get => GetMapping(nameof(TripCode)); - set { var __tripCode = GetMapping(nameof(TripCode)) ?? default; SetValue(ref __tripCode, value, true, StringTrim.UseDefault, StringTransform.UseDefault, nameof(TripCode)); SetMapping(nameof(TripCode), __tripCode!); } + set { var __tripCode = GetMapping(nameof(TripCode)) ?? default; SetValue(ref __tripCode, value, true, StringTrim.UseDefault, StringTransform.UseDefault, nameof(TripCode)); SetMapping(nameof(TripCode), __tripCode!); } } #endregion @@ -64,10 +77,7 @@ public string? TripCode /// The Id. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator Gender(Guid id) - { - return ConvertFromId(id); - } + public static implicit operator Gender(Guid id) => ConvertFromId(id); /// /// An implicit cast from a Code to a . @@ -75,13 +85,10 @@ public static implicit operator Gender(Guid id) /// The Code. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator Gender(string? code) - { - return ConvertFromCode(code); - } + public static implicit operator Gender(string? code) => ConvertFromCode(code); #endregion - + #region ICopyFrom /// @@ -100,8 +107,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(Gender from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((ReferenceDataBaseGuid)from); AlternateName = from.AlternateName; @@ -109,9 +116,9 @@ public void CopyFrom(Gender from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -139,7 +146,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -151,7 +158,8 @@ public override bool IsInitial if (!base.IsInitial) return false; - return Cleaner.IsInitial(AlternateName); + return Cleaner.IsInitial(AlternateName) + && Cleaner.IsInitial(TripCode); } } @@ -164,29 +172,29 @@ public override bool IsInitial partial void OnAfterCopyFrom(Gender from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class GenderCollection : ReferenceDataCollectionBase { - #region Constructors - /// /// Initializes a new instance of the class. /// - public GenderCollection(){ } + public GenderCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public GenderCollection(IEnumerable entities) => AddRange(entities); - - #endregion } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/IReferenceData.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/IReferenceData.cs index 0c65e6e2f..ec1361cc1 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/IReferenceData.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/IReferenceData.cs @@ -14,7 +14,7 @@ namespace Beef.Demo.Common.Entities /// /// Provides for the required ReferenceData capabilities. /// - public interface IReferenceData : IReferenceDataProvider + public partial interface IReferenceData : IReferenceDataProvider { #region Collections diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/MapArgs.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/MapArgs.cs index a276f47d0..5003003f7 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/MapArgs.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/MapArgs.cs @@ -33,7 +33,7 @@ public partial class MapArgs : EntityBase, IEquatable #region Properties /// - /// Gets or sets the Coordinates (see ). + /// Gets or sets the Coordinates (see ). /// [JsonProperty("coordinates", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Coordinates")] @@ -41,40 +41,57 @@ public partial class MapArgs : EntityBase, IEquatable public MapCoordinates? Coordinates { get => _coordinates; - set => SetValue(ref _coordinates, value, false, false, nameof(Coordinates)); + set => SetValue(ref _coordinates, value, false, true, nameof(Coordinates)); } #endregion - #region IEquatable + #region IChangeTracking /// - /// Determines whether the specified object is equal to the current object by comparing the values of all the properties. + /// Resets the entity state to unchanged by accepting the changes (resets ). /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) + /// Ends and commits the entity changes (see ). + public override void AcceptChanges() { - if (!(obj is MapArgs val)) - return false; + Coordinates?.AcceptChanges(); + base.AcceptChanges(); + } - return Equals(val); + /// + /// Determines that until is invoked property changes are to be logged (see ). + /// + public override void TrackChanges() + { + Coordinates?.TrackChanges(); + base.TrackChanges(); } + #endregion + + #region IEquatable + /// - /// Determines whether the specified is equal to the current by comparing the values of all the properties. + /// Determines whether the specified object is equal to the current object by comparing the values of all the properties. /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(MapArgs? obj) + public override bool Equals(object? obj) => obj is MapArgs val && Equals(val); + + /// + /// Determines whether the specified is equal to the current by comparing the values of all the properties. + /// + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(MapArgs? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(Coordinates, obj.Coordinates); + return base.Equals((object)value) + && Equals(Coordinates, value.Coordinates); } /// @@ -94,9 +111,9 @@ public bool Equals(MapArgs? obj) public static bool operator != (MapArgs? a, MapArgs? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -105,7 +122,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -124,17 +141,17 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(MapArgs from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); - Coordinates = from.Coordinates; + Coordinates = CopyOrClone(from.Coordinates, Coordinates); OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -162,7 +179,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -184,7 +201,7 @@ public override bool IsInitial partial void OnAfterCopyFrom(MapArgs from); #endregion - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/MapCoordinates.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/MapCoordinates.cs index e40f78d0c..5e3a929de 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/MapCoordinates.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/MapCoordinates.cs @@ -41,7 +41,7 @@ public partial class MapCoordinates : EntityBase, IEquatable public decimal Latitude { get => _latitude; - set => SetValue(ref _latitude, value, false, false, nameof(Latitude)); + set => SetValue(ref _latitude, value, false, false, nameof(Latitude)); } /// @@ -52,7 +52,7 @@ public decimal Latitude public decimal Longitude { get => _longitude; - set => SetValue(ref _longitude, value, false, false, nameof(Longitude)); + set => SetValue(ref _longitude, value, false, false, nameof(Longitude)); } #endregion @@ -64,29 +64,23 @@ public decimal Longitude /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is MapCoordinates val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is MapCoordinates val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(MapCoordinates? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(MapCoordinates? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(Latitude, obj.Latitude) - && Equals(Longitude, obj.Longitude); + return base.Equals((object)value) + && Equals(Latitude, value.Latitude) + && Equals(Longitude, value.Longitude); } /// @@ -106,9 +100,9 @@ public bool Equals(MapCoordinates? obj) public static bool operator != (MapCoordinates? a, MapCoordinates? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -118,7 +112,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -137,8 +131,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(MapCoordinates from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); Latitude = from.Latitude; @@ -146,9 +140,9 @@ public void CopyFrom(MapCoordinates from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -177,7 +171,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -200,7 +194,7 @@ public override bool IsInitial partial void OnAfterCopyFrom(MapCoordinates from); #endregion - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/Person.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/Person.cs index c87546b5c..90c46e4e1 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/Person.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/Person.cs @@ -22,7 +22,7 @@ namespace Beef.Demo.Common.Entities /// Represents the Person entity. /// [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public partial class Person : EntityBase, IETag, IChangeLog, IGuidIdentifier, IEquatable + public partial class Person : EntityBase, IGuidIdentifier, IETag, IChangeLog, IEquatable { #region Privates @@ -46,12 +46,12 @@ public partial class Person : EntityBase, IETag, IChangeLog, IGuidIdentifier, IE /// /// Gets or sets the identifier. /// - [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Include)] [Display(Name="Identifier")] public Guid Id { get => _id; - set => SetValue(ref _id, value, false, false, nameof(Id)); + set => SetValue(ref _id, value, false, false, nameof(Id)); } /// @@ -62,7 +62,7 @@ public Guid Id public string? FirstName { get => _firstName; - set => SetValue(ref _firstName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(FirstName)); + set => SetValue(ref _firstName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(FirstName)); } /// @@ -73,7 +73,7 @@ public string? FirstName public string? LastName { get => _lastName; - set => SetValue(ref _lastName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(LastName)); + set => SetValue(ref _lastName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(LastName)); } /// @@ -84,7 +84,7 @@ public string? LastName public string? UniqueCode { get => _uniqueCode; - set => SetValue(ref _uniqueCode, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(UniqueCode)); + set => SetValue(ref _uniqueCode, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(UniqueCode)); } /// @@ -99,7 +99,7 @@ public string? GenderSid } /// - /// Gets the corresponding text (read-only where selected). + /// Gets the corresponding {{Gender}} text (read-only where selected). /// [JsonProperty("genderText", DefaultValueHandling = DefaultValueHandling.Ignore)] public string? GenderText { get => _genderText ?? GetRefDataText(() => Gender); set => _genderText = value; } @@ -127,7 +127,7 @@ public string? EyeColorSid } /// - /// Gets the corresponding text (read-only where selected). + /// Gets the corresponding {{EyeColor}} text (read-only where selected). /// [JsonProperty("eyeColorText", DefaultValueHandling = DefaultValueHandling.Ignore)] public string? EyeColorText { get => _eyeColorText ?? GetRefDataText(() => EyeColor); set => _eyeColorText = value; } @@ -148,22 +148,21 @@ public RefDataNamespace.EyeColor? EyeColor /// [JsonProperty("birthday", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Birthday")] - [DisplayFormat(DataFormatString = Beef.Entities.StringFormat.DateOnlyFormat)] public DateTime Birthday { get => _birthday; - set => SetValue(ref _birthday, value, false, DateTimeTransform.DateOnly, nameof(Birthday)); + set => SetValue(ref _birthday, value, false, DateTimeTransform.DateOnly, nameof(Birthday)); } /// - /// Gets or sets the Address (see ). + /// Gets or sets the Address (see ). /// [JsonProperty("address", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Address")] public Address? Address { get => _address; - set => SetValue(ref _address, value, false, true, nameof(Address)); + set => SetValue(ref _address, value, false, true, nameof(Address)); } /// @@ -174,24 +173,24 @@ public Address? Address public string? ETag { get => _eTag; - set => SetValue(ref _eTag, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ETag)); + set => SetValue(ref _eTag, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ETag)); } /// - /// Gets or sets the Change Log (see ). + /// Gets or sets the Change Log (see ). /// [JsonProperty("changeLog", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Change Log")] public ChangeLog? ChangeLog { get => _changeLog; - set => SetValue(ref _changeLog, value, false, true, nameof(ChangeLog)); + set => SetValue(ref _changeLog, value, false, true, nameof(ChangeLog)); } #endregion #region IChangeTracking - + /// /// Resets the entity state to unchanged by accepting the changes (resets ). /// @@ -215,8 +214,8 @@ public override void TrackChanges() #endregion - #region UniqueKey - + #region IUniqueKey + /// /// Indicates whether the has a value. /// @@ -226,20 +225,17 @@ public override void TrackChanges() /// Gets the list of property names that represent the unique key. /// public override string[] UniqueKeyProperties => new string[] { nameof(Id) }; - + /// /// Creates the . /// /// The . /// The . public static UniqueKey CreateUniqueKey(Guid id) => new UniqueKey(id); - + /// - /// Gets the . + /// Gets the (consists of the following property(s): ). /// - /// - /// The UniqueKey key consists of the following property(s): . - /// public override UniqueKey UniqueKey => new UniqueKey(Id); #endregion @@ -251,37 +247,31 @@ public override void TrackChanges() /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is Person val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is Person val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(Person? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(Person? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(Id, obj.Id) - && Equals(FirstName, obj.FirstName) - && Equals(LastName, obj.LastName) - && Equals(UniqueCode, obj.UniqueCode) - && Equals(GenderSid, obj.GenderSid) - && Equals(EyeColorSid, obj.EyeColorSid) - && Equals(Birthday, obj.Birthday) - && Equals(Address, obj.Address) - && Equals(ETag, obj.ETag) - && Equals(ChangeLog, obj.ChangeLog); + return base.Equals((object)value) + && Equals(Id, value.Id) + && Equals(FirstName, value.FirstName) + && Equals(LastName, value.LastName) + && Equals(UniqueCode, value.UniqueCode) + && Equals(GenderSid, value.GenderSid) + && Equals(EyeColorSid, value.EyeColorSid) + && Equals(Birthday, value.Birthday) + && Equals(Address, value.Address) + && Equals(ETag, value.ETag) + && Equals(ChangeLog, value.ChangeLog); } /// @@ -301,9 +291,9 @@ public bool Equals(Person? obj) public static bool operator != (Person? a, Person? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -321,7 +311,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -340,8 +330,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(Person from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); Id = from.Id; @@ -357,9 +347,9 @@ public void CopyFrom(Person from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -396,7 +386,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -427,31 +417,27 @@ public override bool IsInitial partial void OnAfterCopyFrom(Person from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class PersonCollection : EntityBaseCollection { - #region Constructors - /// /// Initializes a new instance of the class. /// - public PersonCollection(){ } + public PersonCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public PersonCollection(IEnumerable entities) => AddRange(entities); - #endregion - - #region ICloneable - /// /// Creates a deep copy of the . /// @@ -459,31 +445,29 @@ public PersonCollection(){ } public override object Clone() { var clone = new PersonCollection(); - foreach (Person item in this) + foreach (var item in this) { clone.Add((Person)item.Clone()); } return clone; } - - #endregion - - #region Operator /// - /// An implicit cast from a to a . + /// An implicit cast from the to a corresponding . /// /// The . /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] public static implicit operator PersonCollection(PersonCollectionResult result) => result?.Result!; - - #endregion } + #endregion + + #region CollectionResult + /// - /// Represents a collection result. + /// Represents the collection result. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public class PersonCollectionResult : EntityCollectionResult @@ -494,7 +478,7 @@ public class PersonCollectionResult : EntityCollectionResult - /// Initializes a new instance of the class with default . + /// Initializes a new instance of the class with . /// /// The . public PersonCollectionResult(PagingArgs? paging) : base(paging) { } @@ -517,6 +501,8 @@ public override object Clone() return clone; } } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/PersonArgs.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/PersonArgs.cs index 40ccc42fa..af37a399a 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/PersonArgs.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/PersonArgs.cs @@ -42,7 +42,7 @@ public partial class PersonArgs : EntityBase, IEquatable public string? FirstName { get => _firstName; - set => SetValue(ref _firstName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(FirstName)); + set => SetValue(ref _firstName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(FirstName)); } /// @@ -53,7 +53,7 @@ public string? FirstName public string? LastName { get => _lastName; - set => SetValue(ref _lastName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(LastName)); + set => SetValue(ref _lastName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(LastName)); } /// @@ -68,14 +68,14 @@ public List? GendersSids } /// - /// Gets or sets the Genders (see ). + /// Gets or sets the Genders (see ). /// [DebuggerBrowsable(DebuggerBrowsableState.Never)] [Display(Name="Genders")] - public ReferenceDataSidList? Genders + public ReferenceDataSidList? Genders { - get => new ReferenceDataSidList(ref _gendersSids); - set => SetValue(ref _gendersSids, value?.ToSidList(), false, false, nameof(Genders)); + get => new ReferenceDataSidList(ref _gendersSids); + set => SetValue(ref _gendersSids, value?.ToSidList(), false, false, nameof(Genders)); } #endregion @@ -87,30 +87,24 @@ public ReferenceDataSidList? Genders /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is PersonArgs val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is PersonArgs val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(PersonArgs? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(PersonArgs? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(FirstName, obj.FirstName) - && Equals(LastName, obj.LastName) - && Equals(GendersSids, obj.GendersSids); + return base.Equals((object)value) + && Equals(FirstName, value.FirstName) + && Equals(LastName, value.LastName) + && Equals(GendersSids, value.GendersSids); } /// @@ -130,9 +124,9 @@ public bool Equals(PersonArgs? obj) public static bool operator != (PersonArgs? a, PersonArgs? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -143,7 +137,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -162,8 +156,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(PersonArgs from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); FirstName = from.FirstName; @@ -172,9 +166,9 @@ public void CopyFrom(PersonArgs from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -204,7 +198,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -228,7 +222,7 @@ public override bool IsInitial partial void OnAfterCopyFrom(PersonArgs from); #endregion - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/PersonDetail.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/PersonDetail.cs index e16728569..d34289022 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/PersonDetail.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/PersonDetail.cs @@ -22,7 +22,7 @@ namespace Beef.Demo.Common.Entities /// Represents the detail entity. /// [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public partial class PersonDetail : Person + public partial class PersonDetail : Person, IEquatable { #region Privates @@ -33,20 +33,20 @@ public partial class PersonDetail : Person #region Properties /// - /// Gets or sets the History (see ). + /// Gets or sets the History. /// [JsonProperty("history", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="History")] public WorkHistoryCollection? History { get => _history; - set => SetValue(ref _history, value, false, true, nameof(History)); + set => SetValue(ref _history, value, false, true, nameof(History)); } #endregion #region IChangeTracking - + /// /// Resets the entity state to unchanged by accepting the changes (resets ). /// @@ -75,28 +75,22 @@ public override void TrackChanges() /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is PersonDetail val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is PersonDetail val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(PersonDetail? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(PersonDetail? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(History, obj.History); + return base.Equals((object)value) + && Equals(History, value.History); } /// @@ -116,9 +110,9 @@ public bool Equals(PersonDetail? obj) public static bool operator != (PersonDetail? a, PersonDetail? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -127,7 +121,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -146,17 +140,17 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(PersonDetail from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((Person)from); History = CopyOrClone(from.History, History); OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -184,7 +178,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -209,31 +203,27 @@ public override bool IsInitial partial void OnAfterCopyFrom(PersonDetail from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class PersonDetailCollection : EntityBaseCollection { - #region Constructors - /// /// Initializes a new instance of the class. /// - public PersonDetailCollection(){ } + public PersonDetailCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public PersonDetailCollection(IEnumerable entities) => AddRange(entities); - #endregion - - #region ICloneable - /// /// Creates a deep copy of the . /// @@ -241,31 +231,29 @@ public PersonDetailCollection(){ } public override object Clone() { var clone = new PersonDetailCollection(); - foreach (PersonDetail item in this) + foreach (var item in this) { clone.Add((PersonDetail)item.Clone()); } return clone; } - - #endregion - - #region Operator /// - /// An implicit cast from a to a . + /// An implicit cast from the to a corresponding . /// /// The . /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] public static implicit operator PersonDetailCollection(PersonDetailCollectionResult result) => result?.Result!; - - #endregion } + #endregion + + #region CollectionResult + /// - /// Represents a collection result. + /// Represents the collection result. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public class PersonDetailCollectionResult : EntityCollectionResult @@ -276,7 +264,7 @@ public class PersonDetailCollectionResult : EntityCollectionResult - /// Initializes a new instance of the class with default . + /// Initializes a new instance of the class with . /// /// The . public PersonDetailCollectionResult(PagingArgs? paging) : base(paging) { } @@ -299,6 +287,8 @@ public override object Clone() return clone; } } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/PersonOther.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/PersonOther.cs index 5d06e6f6f..62e538f94 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/PersonOther.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/PersonOther.cs @@ -8,8 +8,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Beef.Entities; using Newtonsoft.Json; @@ -20,7 +18,7 @@ namespace Beef.Demo.Common.Entities /// Represents the other without capabilities entity. /// [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public partial class PersonOther : IGuidIdentifier, IChangeLog, IETag + public partial class PersonOther : IGuidIdentifier, IETag, IChangeLog { /// /// Gets or sets the identifier. @@ -47,14 +45,14 @@ public partial class PersonOther : IGuidIdentifier, IChangeLog, IETag public string? ETag { get; set; } /// - /// Gets or sets the Change Log (see ). + /// Gets or sets the Change Log (see ). /// [JsonProperty("changeLog", DefaultValueHandling = DefaultValueHandling.Ignore)] public ChangeLog? ChangeLog { get; set; } - } + } /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class PersonOtherCollection : List { } diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/PowerSource.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/PowerSource.cs index 85386e7f5..3a1b2383e 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/PowerSource.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/PowerSource.cs @@ -41,7 +41,7 @@ public partial class PowerSource : ReferenceDataBaseGuid public string? AdditionalInfo { get => _additionalInfo; - set => SetValue(ref _additionalInfo, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(AdditionalInfo)); + set => SetValue(ref _additionalInfo, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(AdditionalInfo)); } #endregion @@ -54,10 +54,7 @@ public string? AdditionalInfo /// The Id. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator PowerSource(Guid id) - { - return ConvertFromId(id); - } + public static implicit operator PowerSource(Guid id) => ConvertFromId(id); /// /// An implicit cast from a Code to a . @@ -65,13 +62,10 @@ public static implicit operator PowerSource(Guid id) /// The Code. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator PowerSource(string? code) - { - return ConvertFromCode(code); - } + public static implicit operator PowerSource(string? code) => ConvertFromCode(code); #endregion - + #region ICopyFrom /// @@ -90,17 +84,17 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(PowerSource from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((ReferenceDataBaseGuid)from); AdditionalInfo = from.AdditionalInfo; OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -128,7 +122,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -153,29 +147,29 @@ public override bool IsInitial partial void OnAfterCopyFrom(PowerSource from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class PowerSourceCollection : ReferenceDataCollectionBase { - #region Constructors - /// /// Initializes a new instance of the class. /// - public PowerSourceCollection(){ } + public PowerSourceCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public PowerSourceCollection(IEnumerable entities) => AddRange(entities); - - #endregion } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/Product.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/Product.cs index f07e9e91c..358da689d 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/Product.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/Product.cs @@ -22,7 +22,7 @@ namespace Beef.Demo.Common.Entities /// Represents the Product entity. /// [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public partial class Product : EntityBase, IEquatable + public partial class Product : EntityBase, IIntIdentifier, IEquatable { #region Privates @@ -42,7 +42,7 @@ public partial class Product : EntityBase, IEquatable public int Id { get => _id; - set => SetValue(ref _id, value, true, false, nameof(Id)); + set => SetValue(ref _id, value, true, false, nameof(Id)); } /// @@ -53,7 +53,7 @@ public int Id public string? Name { get => _name; - set => SetValue(ref _name, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Name)); + set => SetValue(ref _name, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Name)); } /// @@ -64,13 +64,13 @@ public string? Name public string? Description { get => _description; - set => SetValue(ref _description, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Description)); + set => SetValue(ref _description, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Description)); } #endregion - #region UniqueKey - + #region IUniqueKey + /// /// Indicates whether the has a value. /// @@ -80,20 +80,17 @@ public string? Description /// Gets the list of property names that represent the unique key. /// public override string[] UniqueKeyProperties => new string[] { nameof(Id) }; - + /// /// Creates the . /// /// The . /// The . public static UniqueKey CreateUniqueKey(int id) => new UniqueKey(id); - + /// - /// Gets the . + /// Gets the (consists of the following property(s): ). /// - /// - /// The UniqueKey key consists of the following property(s): . - /// public override UniqueKey UniqueKey => new UniqueKey(Id); #endregion @@ -105,30 +102,24 @@ public string? Description /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is Product val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is Product val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(Product? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(Product? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(Id, obj.Id) - && Equals(Name, obj.Name) - && Equals(Description, obj.Description); + return base.Equals((object)value) + && Equals(Id, value.Id) + && Equals(Name, value.Name) + && Equals(Description, value.Description); } /// @@ -148,9 +139,9 @@ public bool Equals(Product? obj) public static bool operator != (Product? a, Product? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -161,7 +152,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -180,8 +171,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(Product from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); Id = from.Id; @@ -190,9 +181,9 @@ public void CopyFrom(Product from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -221,7 +212,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -230,7 +221,8 @@ public override bool IsInitial { get { - return Cleaner.IsInitial(Name) + return Cleaner.IsInitial(Id) + && Cleaner.IsInitial(Name) && Cleaner.IsInitial(Description); } } @@ -244,31 +236,27 @@ public override bool IsInitial partial void OnAfterCopyFrom(Product from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class ProductCollection : EntityBaseCollection { - #region Constructors - /// /// Initializes a new instance of the class. /// - public ProductCollection(){ } + public ProductCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public ProductCollection(IEnumerable entities) => AddRange(entities); - #endregion - - #region ICloneable - /// /// Creates a deep copy of the . /// @@ -276,31 +264,29 @@ public ProductCollection(){ } public override object Clone() { var clone = new ProductCollection(); - foreach (Product item in this) + foreach (var item in this) { clone.Add((Product)item.Clone()); } return clone; } - - #endregion - - #region Operator /// - /// An implicit cast from a to a . + /// An implicit cast from the to a corresponding . /// /// The . /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] public static implicit operator ProductCollection(ProductCollectionResult result) => result?.Result!; - - #endregion } + #endregion + + #region CollectionResult + /// - /// Represents a collection result. + /// Represents the collection result. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public class ProductCollectionResult : EntityCollectionResult @@ -311,7 +297,7 @@ public class ProductCollectionResult : EntityCollectionResult - /// Initializes a new instance of the class with default . + /// Initializes a new instance of the class with . /// /// The . public ProductCollectionResult(PagingArgs? paging) : base(paging) { } @@ -334,6 +320,8 @@ public override object Clone() return clone; } } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/ProductArgs.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/ProductArgs.cs index 06e691413..699f68806 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/ProductArgs.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/ProductArgs.cs @@ -41,7 +41,7 @@ public partial class ProductArgs : EntityBase, IEquatable public string? Name { get => _name; - set => SetValue(ref _name, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Name)); + set => SetValue(ref _name, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Name)); } /// @@ -52,7 +52,7 @@ public string? Name public string? Description { get => _description; - set => SetValue(ref _description, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Description)); + set => SetValue(ref _description, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Description)); } #endregion @@ -64,29 +64,23 @@ public string? Description /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is ProductArgs val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is ProductArgs val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(ProductArgs? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(ProductArgs? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(Name, obj.Name) - && Equals(Description, obj.Description); + return base.Equals((object)value) + && Equals(Name, value.Name) + && Equals(Description, value.Description); } /// @@ -106,9 +100,9 @@ public bool Equals(ProductArgs? obj) public static bool operator != (ProductArgs? a, ProductArgs? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -118,7 +112,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -137,8 +131,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(ProductArgs from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); Name = from.Name; @@ -146,9 +140,9 @@ public void CopyFrom(ProductArgs from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -177,7 +171,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -200,7 +194,7 @@ public override bool IsInitial partial void OnAfterCopyFrom(ProductArgs from); #endregion - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/ReferenceData.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/ReferenceData.cs index fb21ed536..b8015d634 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/ReferenceData.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/ReferenceData.cs @@ -35,7 +35,7 @@ public abstract partial class ReferenceData : IReferenceData typeof(PowerSource), typeof(Company) }; - + /// /// Gets the provider interface cref="Type"/> used for . The value is . /// diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/Robot.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/Robot.cs index cc5510e7d..41308e73a 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/Robot.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/Robot.cs @@ -43,12 +43,12 @@ public partial class Robot : EntityBase, IGuidIdentifier, IETag, IChangeLog, IEq /// /// Gets or sets the identifier. /// - [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Include)] [Display(Name="Identifier")] public Guid Id { get => _id; - set => SetValue(ref _id, value, false, false, nameof(Id)); + set => SetValue(ref _id, value, false, false, nameof(Id)); } /// @@ -59,7 +59,7 @@ public Guid Id public string? ModelNo { get => _modelNo; - set => SetValue(ref _modelNo, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ModelNo)); + set => SetValue(ref _modelNo, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ModelNo)); } /// @@ -70,7 +70,7 @@ public string? ModelNo public string? SerialNo { get => _serialNo; - set => SetValue(ref _serialNo, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(SerialNo)); + set => SetValue(ref _serialNo, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(SerialNo)); } /// @@ -85,7 +85,7 @@ public string? EyeColorSid } /// - /// Gets the corresponding text (read-only where selected). + /// Gets the corresponding {{EyeColor}} text (read-only where selected). /// [JsonProperty("eyeColorText", DefaultValueHandling = DefaultValueHandling.Ignore)] public string? EyeColorText { get => _eyeColorText ?? GetRefDataText(() => EyeColor); set => _eyeColorText = value; } @@ -113,7 +113,7 @@ public string? PowerSourceSid } /// - /// Gets the corresponding text (read-only where selected). + /// Gets the corresponding {{PowerSource}} text (read-only where selected). /// [JsonProperty("powerSourceText", DefaultValueHandling = DefaultValueHandling.Ignore)] public string? PowerSourceText { get => _powerSourceText ?? GetRefDataText(() => PowerSource); set => _powerSourceText = value; } @@ -137,24 +137,24 @@ public RefDataNamespace.PowerSource? PowerSource public string? ETag { get => _eTag; - set => SetValue(ref _eTag, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ETag)); + set => SetValue(ref _eTag, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ETag)); } /// - /// Gets or sets the Change Log (see ). + /// Gets or sets the Change Log (see ). /// [JsonProperty("changeLog", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Change Log")] public ChangeLog? ChangeLog { get => _changeLog; - set => SetValue(ref _changeLog, value, false, true, nameof(ChangeLog)); + set => SetValue(ref _changeLog, value, false, true, nameof(ChangeLog)); } #endregion #region IChangeTracking - + /// /// Resets the entity state to unchanged by accepting the changes (resets ). /// @@ -176,8 +176,8 @@ public override void TrackChanges() #endregion - #region UniqueKey - + #region IUniqueKey + /// /// Indicates whether the has a value. /// @@ -187,20 +187,17 @@ public override void TrackChanges() /// Gets the list of property names that represent the unique key. /// public override string[] UniqueKeyProperties => new string[] { nameof(Id) }; - + /// /// Creates the . /// /// The . /// The . public static UniqueKey CreateUniqueKey(Guid id) => new UniqueKey(id); - + /// - /// Gets the . + /// Gets the (consists of the following property(s): ). /// - /// - /// The UniqueKey key consists of the following property(s): . - /// public override UniqueKey UniqueKey => new UniqueKey(Id); #endregion @@ -212,34 +209,28 @@ public override void TrackChanges() /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is Robot val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is Robot val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(Robot? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(Robot? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(Id, obj.Id) - && Equals(ModelNo, obj.ModelNo) - && Equals(SerialNo, obj.SerialNo) - && Equals(EyeColorSid, obj.EyeColorSid) - && Equals(PowerSourceSid, obj.PowerSourceSid) - && Equals(ETag, obj.ETag) - && Equals(ChangeLog, obj.ChangeLog); + return base.Equals((object)value) + && Equals(Id, value.Id) + && Equals(ModelNo, value.ModelNo) + && Equals(SerialNo, value.SerialNo) + && Equals(EyeColorSid, value.EyeColorSid) + && Equals(PowerSourceSid, value.PowerSourceSid) + && Equals(ETag, value.ETag) + && Equals(ChangeLog, value.ChangeLog); } /// @@ -259,9 +250,9 @@ public bool Equals(Robot? obj) public static bool operator != (Robot? a, Robot? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -276,7 +267,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -295,8 +286,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(Robot from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); Id = from.Id; @@ -309,9 +300,9 @@ public void CopyFrom(Robot from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -345,7 +336,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -373,31 +364,27 @@ public override bool IsInitial partial void OnAfterCopyFrom(Robot from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class RobotCollection : EntityBaseCollection { - #region Constructors - /// /// Initializes a new instance of the class. /// - public RobotCollection(){ } + public RobotCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public RobotCollection(IEnumerable entities) => AddRange(entities); - #endregion - - #region ICloneable - /// /// Creates a deep copy of the . /// @@ -405,31 +392,29 @@ public RobotCollection(){ } public override object Clone() { var clone = new RobotCollection(); - foreach (Robot item in this) + foreach (var item in this) { clone.Add((Robot)item.Clone()); } return clone; } - - #endregion - - #region Operator /// - /// An implicit cast from a to a . + /// An implicit cast from the to a corresponding . /// /// The . /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] public static implicit operator RobotCollection(RobotCollectionResult result) => result?.Result!; - - #endregion } + #endregion + + #region CollectionResult + /// - /// Represents a collection result. + /// Represents the collection result. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public class RobotCollectionResult : EntityCollectionResult @@ -440,7 +425,7 @@ public class RobotCollectionResult : EntityCollectionResult - /// Initializes a new instance of the class with default . + /// Initializes a new instance of the class with . /// /// The . public RobotCollectionResult(PagingArgs? paging) : base(paging) { } @@ -463,6 +448,8 @@ public override object Clone() return clone; } } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/RobotArgs.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/RobotArgs.cs index b3e1eaf26..5031202e5 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/RobotArgs.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/RobotArgs.cs @@ -42,7 +42,7 @@ public partial class RobotArgs : EntityBase, IEquatable public string? ModelNo { get => _modelNo; - set => SetValue(ref _modelNo, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ModelNo)); + set => SetValue(ref _modelNo, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ModelNo)); } /// @@ -53,7 +53,7 @@ public string? ModelNo public string? SerialNo { get => _serialNo; - set => SetValue(ref _serialNo, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(SerialNo)); + set => SetValue(ref _serialNo, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(SerialNo)); } /// @@ -75,7 +75,7 @@ public List? PowerSourcesSids public ReferenceDataSidList? PowerSources { get => new ReferenceDataSidList(ref _powerSourcesSids); - set => SetValue(ref _powerSourcesSids, value?.ToSidList(), false, false, nameof(PowerSources)); + set => SetValue(ref _powerSourcesSids, value?.ToSidList(), false, false, nameof(PowerSources)); } #endregion @@ -87,30 +87,24 @@ public List? PowerSourcesSids /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is RobotArgs val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is RobotArgs val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(RobotArgs? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(RobotArgs? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(ModelNo, obj.ModelNo) - && Equals(SerialNo, obj.SerialNo) - && Equals(PowerSourcesSids, obj.PowerSourcesSids); + return base.Equals((object)value) + && Equals(ModelNo, value.ModelNo) + && Equals(SerialNo, value.SerialNo) + && Equals(PowerSourcesSids, value.PowerSourcesSids); } /// @@ -130,9 +124,9 @@ public bool Equals(RobotArgs? obj) public static bool operator != (RobotArgs? a, RobotArgs? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -143,7 +137,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -162,8 +156,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(RobotArgs from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); ModelNo = from.ModelNo; @@ -172,9 +166,9 @@ public void CopyFrom(RobotArgs from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -204,7 +198,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -228,7 +222,7 @@ public override bool IsInitial partial void OnAfterCopyFrom(RobotArgs from); #endregion - } + } } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/TripPerson.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/TripPerson.cs index e5eb47cd1..eb715e3ff 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/TripPerson.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/TripPerson.cs @@ -37,12 +37,12 @@ public partial class TripPerson : EntityBase, IStringIdentifier, IEquatable /// Gets or sets the identifier (username). /// - [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Include)] [Display(Name="Identifier")] public string? Id { get => _id; - set => SetValue(ref _id, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Id)); + set => SetValue(ref _id, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Id)); } /// @@ -53,7 +53,7 @@ public string? Id public string? FirstName { get => _firstName; - set => SetValue(ref _firstName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(FirstName)); + set => SetValue(ref _firstName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(FirstName)); } /// @@ -64,13 +64,13 @@ public string? FirstName public string? LastName { get => _lastName; - set => SetValue(ref _lastName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(LastName)); + set => SetValue(ref _lastName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(LastName)); } #endregion - #region UniqueKey - + #region IUniqueKey + /// /// Indicates whether the has a value. /// @@ -80,20 +80,17 @@ public string? LastName /// Gets the list of property names that represent the unique key. /// public override string[] UniqueKeyProperties => new string[] { nameof(Id) }; - + /// /// Creates the . /// /// The . /// The . - public static UniqueKey CreateUniqueKey(string id) => new UniqueKey(id); - + public static UniqueKey CreateUniqueKey(string? id) => new UniqueKey(id); + /// - /// Gets the . + /// Gets the (consists of the following property(s): ). /// - /// - /// The UniqueKey key consists of the following property(s): . - /// public override UniqueKey UniqueKey => new UniqueKey(Id); #endregion @@ -105,30 +102,24 @@ public string? LastName /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is TripPerson val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is TripPerson val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(TripPerson? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(TripPerson? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(Id, obj.Id) - && Equals(FirstName, obj.FirstName) - && Equals(LastName, obj.LastName); + return base.Equals((object)value) + && Equals(Id, value.Id) + && Equals(FirstName, value.FirstName) + && Equals(LastName, value.LastName); } /// @@ -148,9 +139,9 @@ public bool Equals(TripPerson? obj) public static bool operator != (TripPerson? a, TripPerson? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -161,7 +152,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -180,8 +171,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(TripPerson from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); Id = from.Id; @@ -190,9 +181,9 @@ public void CopyFrom(TripPerson from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -222,7 +213,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -246,31 +237,27 @@ public override bool IsInitial partial void OnAfterCopyFrom(TripPerson from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class TripPersonCollection : EntityBaseCollection { - #region Constructors - /// /// Initializes a new instance of the class. /// - public TripPersonCollection(){ } + public TripPersonCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public TripPersonCollection(IEnumerable entities) => AddRange(entities); - #endregion - - #region ICloneable - /// /// Creates a deep copy of the . /// @@ -278,31 +265,29 @@ public TripPersonCollection(){ } public override object Clone() { var clone = new TripPersonCollection(); - foreach (TripPerson item in this) + foreach (var item in this) { clone.Add((TripPerson)item.Clone()); } return clone; } - - #endregion - - #region Operator /// - /// An implicit cast from a to a . + /// An implicit cast from the to a corresponding . /// /// The . /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] public static implicit operator TripPersonCollection(TripPersonCollectionResult result) => result?.Result!; - - #endregion } + #endregion + + #region CollectionResult + /// - /// Represents a collection result. + /// Represents the collection result. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public class TripPersonCollectionResult : EntityCollectionResult @@ -313,7 +298,7 @@ public class TripPersonCollectionResult : EntityCollectionResult - /// Initializes a new instance of the class with default . + /// Initializes a new instance of the class with . /// /// The . public TripPersonCollectionResult(PagingArgs? paging) : base(paging) { } @@ -336,6 +321,8 @@ public override object Clone() return clone; } } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/USState.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/USState.cs index bcfe78df6..99e0244ca 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/USState.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/USState.cs @@ -33,10 +33,7 @@ public partial class USState : ReferenceDataBaseGuid /// The Id. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator USState(Guid id) - { - return ConvertFromId(id); - } + public static implicit operator USState(Guid id) => ConvertFromId(id); /// /// An implicit cast from a Code to a . @@ -44,13 +41,10 @@ public static implicit operator USState(Guid id) /// The Code. /// The corresponding . [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] - public static implicit operator USState(string? code) - { - return ConvertFromCode(code); - } + public static implicit operator USState(string? code) => ConvertFromCode(code); #endregion - + #region ICopyFrom /// @@ -69,16 +63,16 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(USState from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((ReferenceDataBaseGuid)from); OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -105,7 +99,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -130,29 +124,29 @@ public override bool IsInitial partial void OnAfterCopyFrom(USState from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class USStateCollection : ReferenceDataCollectionBase { - #region Constructors - /// /// Initializes a new instance of the class. /// - public USStateCollection(){ } + public USStateCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public USStateCollection(IEnumerable entities) => AddRange(entities); - - #endregion } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Entities/Generated/WorkHistory.cs b/samples/Demo/Beef.Demo.Common/Entities/Generated/WorkHistory.cs index 47c73427c..0e82da8b8 100644 --- a/samples/Demo/Beef.Demo.Common/Entities/Generated/WorkHistory.cs +++ b/samples/Demo/Beef.Demo.Common/Entities/Generated/WorkHistory.cs @@ -42,18 +42,18 @@ public partial class WorkHistory : EntityBase, IEquatable public Guid PersonId { get => _personId; - set => SetValue(ref _personId, value, true, false, nameof(PersonId)); + set => SetValue(ref _personId, value, true, false, nameof(PersonId)); } /// /// Gets or sets the Name. /// - [JsonProperty("name", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("name", DefaultValueHandling = DefaultValueHandling.Include)] [Display(Name="Name")] public string? Name { get => _name; - set => SetValue(ref _name, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Name)); + set => SetValue(ref _name, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Name)); } /// @@ -61,11 +61,10 @@ public string? Name /// [JsonProperty("startDate", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="Start Date")] - [DisplayFormat(DataFormatString = Beef.Entities.StringFormat.DateOnlyFormat)] public DateTime StartDate { get => _startDate; - set => SetValue(ref _startDate, value, false, DateTimeTransform.DateOnly, nameof(StartDate)); + set => SetValue(ref _startDate, value, false, DateTimeTransform.DateOnly, nameof(StartDate)); } /// @@ -73,17 +72,16 @@ public DateTime StartDate /// [JsonProperty("endDate", DefaultValueHandling = DefaultValueHandling.Ignore)] [Display(Name="End Date")] - [DisplayFormat(DataFormatString = Beef.Entities.StringFormat.DateOnlyFormat)] public DateTime? EndDate { get => _endDate; - set => SetValue(ref _endDate, value, false, DateTimeTransform.DateOnly, nameof(EndDate)); + set => SetValue(ref _endDate, value, false, DateTimeTransform.DateOnly, nameof(EndDate)); } #endregion - #region UniqueKey - + #region IUniqueKey + /// /// Indicates whether the has a value. /// @@ -93,20 +91,17 @@ public DateTime? EndDate /// Gets the list of property names that represent the unique key. /// public override string[] UniqueKeyProperties => new string[] { nameof(Name) }; - + /// /// Creates the . /// /// The . /// The . - public static UniqueKey CreateUniqueKey(string name) => new UniqueKey(name); - + public static UniqueKey CreateUniqueKey(string? name) => new UniqueKey(name); + /// - /// Gets the . + /// Gets the (consists of the following property(s): ). /// - /// - /// The UniqueKey key consists of the following property(s): . - /// public override UniqueKey UniqueKey => new UniqueKey(Name); #endregion @@ -118,31 +113,25 @@ public DateTime? EndDate /// /// The object to compare with the current object. /// true if the specified object is equal to the current object; otherwise, false. - public override bool Equals(object? obj) - { - if (!(obj is WorkHistory val)) - return false; - - return Equals(val); - } + public override bool Equals(object? obj) => obj is WorkHistory val && Equals(val); /// /// Determines whether the specified is equal to the current by comparing the values of all the properties. /// - /// The object to compare with the current object. - /// true if the specified object is equal to the current object; otherwise, false. - public bool Equals(WorkHistory? obj) + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(WorkHistory? value) { - if (obj == null) + if (value == null) return false; - else if (ReferenceEquals(obj, this)) + else if (ReferenceEquals(value, this)) return true; - return base.Equals((object)obj) - && Equals(PersonId, obj.PersonId) - && Equals(Name, obj.Name) - && Equals(StartDate, obj.StartDate) - && Equals(EndDate, obj.EndDate); + return base.Equals((object)value) + && Equals(PersonId, value.PersonId) + && Equals(Name, value.Name) + && Equals(StartDate, value.StartDate) + && Equals(EndDate, value.EndDate); } /// @@ -162,9 +151,9 @@ public bool Equals(WorkHistory? obj) public static bool operator != (WorkHistory? a, WorkHistory? b) => !Equals(a, b); /// - /// Returns a hash code for the . + /// Returns the hash code for the . /// - /// A hash code for the . + /// The hash code for the . public override int GetHashCode() { var hash = new HashCode(); @@ -176,7 +165,7 @@ public override int GetHashCode() } #endregion - + #region ICopyFrom /// @@ -195,8 +184,8 @@ public override void CopyFrom(object from) /// The to copy from. public void CopyFrom(WorkHistory from) { - if (from == null) - throw new ArgumentNullException(nameof(from)); + if (from == null) + throw new ArgumentNullException(nameof(from)); CopyFrom((EntityBase)from); PersonId = from.PersonId; @@ -206,9 +195,9 @@ public void CopyFrom(WorkHistory from) OnAfterCopyFrom(from); } - + #endregion - + #region ICloneable /// @@ -238,7 +227,7 @@ public override void CleanUp() OnAfterCleanUp(); } - + /// /// Indicates whether considered initial; i.e. all properties have their initial value. /// @@ -247,7 +236,8 @@ public override bool IsInitial { get { - return Cleaner.IsInitial(Name) + return Cleaner.IsInitial(PersonId) + && Cleaner.IsInitial(Name) && Cleaner.IsInitial(StartDate) && Cleaner.IsInitial(EndDate); } @@ -262,31 +252,27 @@ public override bool IsInitial partial void OnAfterCopyFrom(WorkHistory from); #endregion - } + } + + #region Collection /// - /// Represents a collection. + /// Represents the collection. /// [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] public partial class WorkHistoryCollection : EntityBaseCollection { - #region Constructors - /// /// Initializes a new instance of the class. /// - public WorkHistoryCollection(){ } + public WorkHistoryCollection() { } /// - /// Initializes a new instance of the class with an entity range. + /// Initializes a new instance of the class with an entities range. /// /// The entities. public WorkHistoryCollection(IEnumerable entities) => AddRange(entities); - #endregion - - #region ICloneable - /// /// Creates a deep copy of the . /// @@ -294,16 +280,16 @@ public WorkHistoryCollection(){ } public override object Clone() { var clone = new WorkHistoryCollection(); - foreach (WorkHistory item in this) + foreach (var item in this) { clone.Add((WorkHistory)item.Clone()); } return clone; } - - #endregion } + + #endregion } #pragma warning restore CA2227, CA1819 diff --git a/samples/Demo/Beef.Demo.Common/Grpc/Generated/RobotAgent.cs b/samples/Demo/Beef.Demo.Common/Grpc/Generated/RobotAgent.cs index 595eeb891..cccafb036 100644 --- a/samples/Demo/Beef.Demo.Common/Grpc/Generated/RobotAgent.cs +++ b/samples/Demo/Beef.Demo.Common/Grpc/Generated/RobotAgent.cs @@ -1,5 +1,5 @@ /* - * This file is automatically generated; any changes will be lost. + * This file is automatically generated; any changes will be lost. */ #nullable enable @@ -18,12 +18,12 @@ namespace Beef.Demo.Common.Grpc { /// - /// Defines the Robot gRPC agent. + /// Defines the gRPC agent. /// public partial interface IRobotAgent { /// - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . @@ -31,24 +31,24 @@ public partial interface IRobotAgent Task> GetAsync(Guid id, GrpcRequestOptions? requestOptions = null); /// - /// Creates the object. + /// Creates a new . /// - /// The object. + /// The . /// The optional . /// A . Task> CreateAsync(Robot value, GrpcRequestOptions? requestOptions = null); /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. /// The optional . /// A . Task> UpdateAsync(Robot value, Guid id, GrpcRequestOptions? requestOptions = null); /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. /// The optional . @@ -56,9 +56,9 @@ public partial interface IRobotAgent Task DeleteAsync(Guid id, GrpcRequestOptions? requestOptions = null); /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . @@ -66,7 +66,7 @@ public partial interface IRobotAgent } /// - /// Provides the Robot gRPC service agent. + /// Provides the gRPC agent. /// public partial class RobotAgent : GrpcAgentBase, IRobotAgent { @@ -77,7 +77,7 @@ public partial class RobotAgent : GrpcAgentBase - /// Gets the object that matches the selection criteria. + /// Gets the specified . /// /// The identifier. /// The optional . @@ -89,9 +89,9 @@ public Task> GetAsync(Guid id, GrpcRequestOptions? reques } /// - /// Creates the object. + /// Creates a new . /// - /// The object. + /// The . /// The optional . /// A . public Task> CreateAsync(Robot value, GrpcRequestOptions? requestOptions = null) @@ -101,9 +101,9 @@ public Task> CreateAsync(Robot value, GrpcRequestOptions? } /// - /// Updates the object. + /// Updates an existing . /// - /// The object. + /// The . /// The identifier. /// The optional . /// A . @@ -114,7 +114,7 @@ public Task> UpdateAsync(Robot value, Guid id, GrpcReques } /// - /// Deletes the object that matches the selection criteria. + /// Deletes the specified . /// /// The identifier. /// The optional . @@ -126,9 +126,9 @@ public Task DeleteAsync(Guid id, GrpcRequestOptions? requestOpt } /// - /// Gets the collection object that matches the selection criteria. + /// Gets the that contains the items that match the selection criteria. /// - /// The Args (see ). + /// The Args (see ). /// The . /// The optional . /// A . diff --git a/samples/Demo/Beef.Demo.Common/Grpc/Generated/Transformers.cs b/samples/Demo/Beef.Demo.Common/Grpc/Generated/Transformers.cs index 910bf0896..881287464 100644 --- a/samples/Demo/Beef.Demo.Common/Grpc/Generated/Transformers.cs +++ b/samples/Demo/Beef.Demo.Common/Grpc/Generated/Transformers.cs @@ -95,7 +95,7 @@ public static class Transformers #region Mappers /// - /// Gets the gRpc mapper. + /// Gets the gRPC mapper. /// public static EntityMapper ChangeLog => EntityMapper.Create() .HasProperty(e => e.CreatedBy, g => g.CreatedBy) @@ -104,7 +104,7 @@ public static class Transformers .HasProperty(e => e.UpdatedDate, g => g.UpdatedDate, p => p.SetConverter(NullableDateTimeToTimestamp)); /// - /// Gets the gRpc mapper. + /// Gets the Person gRPC mapper. /// public static EntityMapper Person => EntityMapper.Create() .HasProperty(e => e.Id, g => g.Id, p => p.SetConverter(GuidToStringConverter)) @@ -119,21 +119,21 @@ public static class Transformers .HasProperty(e => e.ChangeLog, g => g.ChangeLog, p => p.SetMapper(ChangeLog)); /// - /// Gets the gRpc mapper. + /// Gets the PersonCollectionResult gRPC mapper. /// public static EntityMapper PersonCollectionResult => EntityMapper.Create() .HasProperty(e => e.Result, g => g.Result, p => p.SetMapper(Person)) .HasProperty(e => e.Paging, g => g.Paging, p => p.SetConverter(PagingResultToPagingResultConverter)); /// - /// Gets the gRpc mapper. + /// Gets the Address gRPC mapper. /// public static EntityMapper Address => EntityMapper.Create() .HasProperty(e => e.Street, g => g.Street) .HasProperty(e => e.City, g => g.City); /// - /// Gets the gRpc mapper. + /// Gets the Robot gRPC mapper. /// public static EntityMapper Robot => EntityMapper.Create() .HasProperty(e => e.Id, g => g.Id, p => p.SetConverter(GuidToStringConverter)) @@ -145,22 +145,22 @@ public static class Transformers .HasProperty(e => e.ChangeLog, g => g.ChangeLog, p => p.SetMapper(ChangeLog)); /// - /// Gets the gRpc mapper. + /// Gets the RobotCollectionResult gRPC mapper. /// public static EntityMapper RobotCollectionResult => EntityMapper.Create() .HasProperty(e => e.Result, g => g.Result, p => p.SetMapper(Robot)) .HasProperty(e => e.Paging, g => g.Paging, p => p.SetConverter(PagingResultToPagingResultConverter)); /// - /// Gets the gRpc mapper. + /// Gets the RobotArgs gRPC mapper. /// public static EntityMapper RobotArgs => EntityMapper.Create() .HasProperty(e => e.ModelNo, g => g.ModelNo) .HasProperty(e => e.SerialNo, g => g.SerialNo) .HasProperty(e => e.PowerSourcesSids, g => g.PowerSources); + + #endregion } - - #endregion } #pragma warning restore IDE0005 diff --git a/samples/Demo/Beef.Demo.Common/Grpc/Generated/beef.demo.proto b/samples/Demo/Beef.Demo.Common/Grpc/Generated/beef.demo.proto index 3f80fd997..f666fb168 100644 --- a/samples/Demo/Beef.Demo.Common/Grpc/Generated/beef.demo.proto +++ b/samples/Demo/Beef.Demo.Common/Grpc/Generated/beef.demo.proto @@ -130,4 +130,4 @@ message RobotDeleteRequest { message RobotGetByArgsRequest { RobotArgs args = 1; PagingArgs Paging = 88; -} \ No newline at end of file +} diff --git a/samples/Demo/Beef.Demo.Functions/Startup.cs b/samples/Demo/Beef.Demo.Functions/Startup.cs index c5f9b7d78..f9585f414 100644 --- a/samples/Demo/Beef.Demo.Functions/Startup.cs +++ b/samples/Demo/Beef.Demo.Functions/Startup.cs @@ -3,6 +3,7 @@ using Beef.Demo.Business.Data; using Beef.Demo.Business.DataSvc; using Beef.Events; +using Beef.Events.Subscribe; using Microsoft.Azure.Functions.Extensions.DependencyInjection; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -25,7 +26,7 @@ public override void Configure(IFunctionsHostBuilder builder) .AddBeefBusinessServices(); // Add event subscriber host and auto-discovered subscribers. - builder.Services.AddBeefEventHubSubscriberHost(); + builder.Services.AddBeefEventHubSubscriberHost(EventSubscriberHostArgs.Create()); // Add the data sources as singletons for dependency injection requirements. var ccs = config.GetSection("CosmosDb"); diff --git a/samples/Demo/Beef.Demo.Test/FixtureSetup.cs b/samples/Demo/Beef.Demo.Test/FixtureSetup.cs index 15413a166..d761ca4c3 100644 --- a/samples/Demo/Beef.Demo.Test/FixtureSetup.cs +++ b/samples/Demo/Beef.Demo.Test/FixtureSetup.cs @@ -18,10 +18,9 @@ public void OneTimeSetUp() TestSetUp.SetDefaultLocalReferenceData(); TestSetUp.RegisterSetUp(async (count, data) => { - return await DatabaseExecutor.RunAsync( - count == 0 ? DatabaseExecutorCommand.ResetAndDatabase : DatabaseExecutorCommand.ResetAndData, - config["ConnectionStrings:BeefDemo"], useBeefDbo: true, - typeof(Database.Program).Assembly, Assembly.GetExecutingAssembly(), typeof(Beef.Demo.Abc.Database.Scripts).Assembly).ConfigureAwait(false) == 0; + return await DatabaseExecutor.RunAsync(new DatabaseExecutorArgs( + count == 0 ? DatabaseExecutorCommand.ResetAndDatabase : DatabaseExecutorCommand.ResetAndData, config["ConnectionStrings:BeefDemo"], + typeof(Database.Program).Assembly, Assembly.GetExecutingAssembly(), typeof(Beef.Demo.Abc.Database.Scripts).Assembly) { UseBeefDbo = true, RefDataSchemaName = "Ref" }).ConfigureAwait(false) == 0; }); } } diff --git a/samples/Demo/Beef.Demo.Test/PersonTest.cs b/samples/Demo/Beef.Demo.Test/PersonTest.cs index 2cd3f0694..a1369c625 100644 --- a/samples/Demo/Beef.Demo.Test/PersonTest.cs +++ b/samples/Demo/Beef.Demo.Test/PersonTest.cs @@ -1012,7 +1012,7 @@ public void I110_Add() .Run(a => a.AddAsync(new Person { FirstName = "Gary" })); // Make sure the content (body) is as expected. - Assert.AreEqual("{\"firstName\":\"Gary\"}", res.Request.Content.ReadAsStringAsync().Result); + Assert.AreEqual("{\"id\":\"00000000-0000-0000-0000-000000000000\",\"firstName\":\"Gary\"}", res.Request.Content.ReadAsStringAsync().Result); } [Test, TestSetUp] diff --git a/samples/My.Hr/My.Hr.Api/Controllers/Generated/EmployeeController.cs b/samples/My.Hr/My.Hr.Api/Controllers/Generated/EmployeeController.cs new file mode 100644 index 000000000..73e5ed48c --- /dev/null +++ b/samples/My.Hr/My.Hr.Api/Controllers/Generated/EmployeeController.cs @@ -0,0 +1,152 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Net; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json.Linq; +using Beef; +using Beef.AspNetCore.WebApi; +using Beef.Entities; +using My.Hr.Business; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Api.Controllers +{ + /// + /// Provides the Web API functionality. + /// + [AllowAnonymous] + [Route("api/v1/employees")] + public partial class EmployeeController : ControllerBase + { + private readonly IEmployeeManager _manager; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public EmployeeController(IEmployeeManager manager) + { _manager = Check.NotNull(manager, nameof(manager)); EmployeeControllerCtor(); } + + partial void EmployeeControllerCtor(); // Enables additional functionality to be added to the constructor. + + /// + /// Gets the specified . + /// + /// The identifier. + /// The selected where found. + [AllowAnonymous] + [HttpGet("{id}")] + [ProducesResponseType(typeof(Employee), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + public IActionResult Get(Guid id) + { + return new WebApiGet(this, () => _manager.GetAsync(id), + operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NotFound); + } + + /// + /// Creates a new . + /// + /// The . + /// The created . + [AllowAnonymous] + [HttpPost("")] + [ProducesResponseType(typeof(Employee), (int)HttpStatusCode.Created)] + public IActionResult Create([FromBody] Employee value) + { + return new WebApiPost(this, () => _manager.CreateAsync(WebApiActionBase.Value(value)), + operationType: OperationType.Create, statusCode: HttpStatusCode.Created, alternateStatusCode: null); + } + + /// + /// Updates an existing . + /// + /// The . + /// The identifier. + /// The updated . + [AllowAnonymous] + [HttpPut("{id}")] + [ProducesResponseType(typeof(Employee), (int)HttpStatusCode.OK)] + public IActionResult Update([FromBody] Employee value, Guid id) + { + return new WebApiPut(this, () => _manager.UpdateAsync(WebApiActionBase.Value(value), id), + operationType: OperationType.Update, statusCode: HttpStatusCode.OK, alternateStatusCode: null); + } + + /// + /// Patches an existing . + /// + /// The that contains the patch content for the . + /// The identifier. + /// The patched . + [AllowAnonymous] + [HttpPatch("{id}")] + [ProducesResponseType(typeof(Employee), (int)HttpStatusCode.OK)] + public IActionResult Patch([FromBody] JToken value, Guid id) + { + return new WebApiPatch(this, value, () => _manager.GetAsync(id), (__value) => _manager.UpdateAsync(__value, id), + operationType: OperationType.Update, statusCode: HttpStatusCode.OK, alternateStatusCode: null); + } + + /// + /// Deletes the specified . + /// + /// The Id. + [AllowAnonymous] + [HttpDelete("{id}")] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public IActionResult Delete(Guid id) + { + return new WebApiDelete(this, () => _manager.DeleteAsync(id), + operationType: OperationType.Delete, statusCode: HttpStatusCode.NoContent); + } + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The First Name. + /// The Last Name. + /// The Genders (see ). + /// The Start From. + /// The Start To. + /// Indicates whether Is Include Terminated. + /// The + [AllowAnonymous] + [HttpGet("")] + [ProducesResponseType(typeof(EmployeeBaseCollection), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public IActionResult GetByArgs(string? firstName = default, string? lastName = default, List? genders = default, DateTime? startFrom = default, DateTime? startTo = default, [FromQuery(Name = "includeTerminated")] bool? isIncludeTerminated = default) + { + var args = new EmployeeArgs { FirstName = firstName, LastName = lastName, GendersSids = genders, StartFrom = startFrom, StartTo = startTo, IsIncludeTerminated = isIncludeTerminated }; + return new WebApiGet(this, () => _manager.GetByArgsAsync(args, WebApiQueryString.CreatePagingArgs(this)), + operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); + } + + /// + /// Terminates an existing . + /// + /// The . + /// The identifier. + /// The updated . + [AllowAnonymous] + [HttpPost("{id}/terminate")] + [ProducesResponseType(typeof(Employee), (int)HttpStatusCode.OK)] + public IActionResult Terminate([FromBody] TerminationDetail value, Guid id) + { + return new WebApiPost(this, () => _manager.TerminateAsync(WebApiActionBase.Value(value), id), + operationType: OperationType.Update, statusCode: HttpStatusCode.OK, alternateStatusCode: null); + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Api/Controllers/Generated/PerformanceReviewController.cs b/samples/My.Hr/My.Hr.Api/Controllers/Generated/PerformanceReviewController.cs new file mode 100644 index 000000000..1f29761d7 --- /dev/null +++ b/samples/My.Hr/My.Hr.Api/Controllers/Generated/PerformanceReviewController.cs @@ -0,0 +1,132 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Net; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json.Linq; +using Beef; +using Beef.AspNetCore.WebApi; +using Beef.Entities; +using My.Hr.Business; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Api.Controllers +{ + /// + /// Provides the Web API functionality. + /// + [AllowAnonymous] + [Route("api/v1")] + public partial class PerformanceReviewController : ControllerBase + { + private readonly IPerformanceReviewManager _manager; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public PerformanceReviewController(IPerformanceReviewManager manager) + { _manager = Check.NotNull(manager, nameof(manager)); PerformanceReviewControllerCtor(); } + + partial void PerformanceReviewControllerCtor(); // Enables additional functionality to be added to the constructor. + + /// + /// Gets the specified . + /// + /// The identifier. + /// The selected where found. + [AllowAnonymous] + [HttpGet("reviews/{id}")] + [ProducesResponseType(typeof(PerformanceReview), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + public IActionResult Get(Guid id) + { + return new WebApiGet(this, () => _manager.GetAsync(id), + operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NotFound); + } + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The . + /// The + [AllowAnonymous] + [HttpGet("employees/{employeeId}/reviews")] + [ProducesResponseType(typeof(PerformanceReviewCollection), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public IActionResult GetByEmployeeId(Guid employeeId) + { + return new WebApiGet(this, () => _manager.GetByEmployeeIdAsync(employeeId, WebApiQueryString.CreatePagingArgs(this)), + operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); + } + + /// + /// Creates a new . + /// + /// The . + /// The . + /// The created . + [AllowAnonymous] + [HttpPost("employees/{employeeId}/reviews")] + [ProducesResponseType(typeof(PerformanceReview), (int)HttpStatusCode.Created)] + public IActionResult Create([FromBody] PerformanceReview value, Guid employeeId) + { + return new WebApiPost(this, () => _manager.CreateAsync(WebApiActionBase.Value(value), employeeId), + operationType: OperationType.Create, statusCode: HttpStatusCode.Created, alternateStatusCode: null); + } + + /// + /// Updates an existing . + /// + /// The . + /// The identifier. + /// The updated . + [AllowAnonymous] + [HttpPut("reviews/{id}")] + [ProducesResponseType(typeof(PerformanceReview), (int)HttpStatusCode.OK)] + public IActionResult Update([FromBody] PerformanceReview value, Guid id) + { + return new WebApiPut(this, () => _manager.UpdateAsync(WebApiActionBase.Value(value), id), + operationType: OperationType.Update, statusCode: HttpStatusCode.OK, alternateStatusCode: null); + } + + /// + /// Patches an existing . + /// + /// The that contains the patch content for the . + /// The identifier. + /// The patched . + [AllowAnonymous] + [HttpPatch("reviews/{id}")] + [ProducesResponseType(typeof(PerformanceReview), (int)HttpStatusCode.OK)] + public IActionResult Patch([FromBody] JToken value, Guid id) + { + return new WebApiPatch(this, value, () => _manager.GetAsync(id), (__value) => _manager.UpdateAsync(__value, id), + operationType: OperationType.Update, statusCode: HttpStatusCode.OK, alternateStatusCode: null); + } + + /// + /// Deletes the specified . + /// + /// The identifier. + [AllowAnonymous] + [HttpDelete("reviews/{id}")] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public IActionResult Delete(Guid id) + { + return new WebApiDelete(this, () => _manager.DeleteAsync(id), + operationType: OperationType.Delete, statusCode: HttpStatusCode.NoContent); + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Api/Controllers/Generated/ReferenceDataController.cs b/samples/My.Hr/My.Hr.Api/Controllers/Generated/ReferenceDataController.cs new file mode 100644 index 000000000..36c8a63f5 --- /dev/null +++ b/samples/My.Hr/My.Hr.Api/Controllers/Generated/ReferenceDataController.cs @@ -0,0 +1,144 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Beef; +using Beef.AspNetCore.WebApi; +using Beef.Entities; +using Beef.RefData; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Api.Controllers +{ + /// + /// Provides the ReferenceData Web API functionality. + /// + [AllowAnonymous] + public partial class ReferenceDataController : ControllerBase + { + /// + /// Gets all of the reference data items that match the specified criteria. + /// + /// The reference data code list. + /// The reference data text (including wildcards). + /// A RefDataNamespace.Gender collection. + [AllowAnonymous] + [HttpGet()] + [Route("api/v1/ref/genders")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public IActionResult GenderGetAll(List? codes = default, string? text = default) => new WebApiGet>(this, + () => Task.FromResult(ReferenceDataFilter.ApplyFilter(RefDataNamespace.ReferenceData.Current.Gender, codes, text, includeInactive: this.IncludeInactive())), + operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); + + /// + /// Gets all of the reference data items that match the specified criteria. + /// + /// The reference data code list. + /// The reference data text (including wildcards). + /// A RefDataNamespace.TerminationReason collection. + [AllowAnonymous] + [HttpGet()] + [Route("api/v1/ref/terminationReasons")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public IActionResult TerminationReasonGetAll(List? codes = default, string? text = default) => new WebApiGet>(this, + () => Task.FromResult(ReferenceDataFilter.ApplyFilter(RefDataNamespace.ReferenceData.Current.TerminationReason, codes, text, includeInactive: this.IncludeInactive())), + operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); + + /// + /// Gets all of the reference data items that match the specified criteria. + /// + /// The reference data code list. + /// The reference data text (including wildcards). + /// A RefDataNamespace.RelationshipType collection. + [AllowAnonymous] + [HttpGet()] + [Route("api/v1/ref/relationshipTypes")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public IActionResult RelationshipTypeGetAll(List? codes = default, string? text = default) => new WebApiGet>(this, + () => Task.FromResult(ReferenceDataFilter.ApplyFilter(RefDataNamespace.ReferenceData.Current.RelationshipType, codes, text, includeInactive: this.IncludeInactive())), + operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); + + /// + /// Gets all of the reference data items that match the specified criteria. + /// + /// The reference data code list. + /// The reference data text (including wildcards). + /// A RefDataNamespace.USState collection. + [AllowAnonymous] + [HttpGet()] + [Route("api/v1/ref/usStates")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public IActionResult USStateGetAll(List? codes = default, string? text = default) => new WebApiGet>(this, + () => Task.FromResult(ReferenceDataFilter.ApplyFilter(RefDataNamespace.ReferenceData.Current.USState, codes, text, includeInactive: this.IncludeInactive())), + operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); + + /// + /// Gets all of the reference data items that match the specified criteria. + /// + /// The reference data code list. + /// The reference data text (including wildcards). + /// A RefDataNamespace.PerformanceOutcome collection. + [AllowAnonymous] + [HttpGet()] + [Route("api/v1/ref/performanceOutcomes")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public IActionResult PerformanceOutcomeGetAll(List? codes = default, string? text = default) => new WebApiGet>(this, + () => Task.FromResult(ReferenceDataFilter.ApplyFilter(RefDataNamespace.ReferenceData.Current.PerformanceOutcome, codes, text, includeInactive: this.IncludeInactive())), + operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); + + /// + /// Gets the reference data entries for the specified entities and codes from the query string; e.g: ?entity=codeX,codeY&entity2=codeZ&entity3 + /// + /// A . + [AllowAnonymous] + [HttpGet()] + [Route("api/v1/ref")] + [ProducesResponseType(typeof(ReferenceDataMultiCollection), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public IActionResult GetNamed() + { + return new WebApiGet(this, async () => + { + var coll = new ReferenceDataMultiCollection(); + var inactive = this.IncludeInactive(); + var refSelection = this.ReferenceDataSelection(); + + var names = refSelection.Select(x => x.Key).ToArray(); + await RefDataNamespace.ReferenceData.Current.PrefetchAsync(names).ConfigureAwait(false); + + foreach (var q in refSelection) + { + switch (q.Key) + { + case var s when string.Compare(s, nameof(RefDataNamespace.Gender), StringComparison.InvariantCultureIgnoreCase) == 0: coll.Add(new ReferenceDataMultiItem(nameof(RefDataNamespace.Gender), ReferenceDataFilter.ApplyFilter(RefDataNamespace.ReferenceData.Current.Gender, q.Value, includeInactive: inactive))); break; + case var s when string.Compare(s, nameof(RefDataNamespace.TerminationReason), StringComparison.InvariantCultureIgnoreCase) == 0: coll.Add(new ReferenceDataMultiItem(nameof(RefDataNamespace.TerminationReason), ReferenceDataFilter.ApplyFilter(RefDataNamespace.ReferenceData.Current.TerminationReason, q.Value, includeInactive: inactive))); break; + case var s when string.Compare(s, nameof(RefDataNamespace.RelationshipType), StringComparison.InvariantCultureIgnoreCase) == 0: coll.Add(new ReferenceDataMultiItem(nameof(RefDataNamespace.RelationshipType), ReferenceDataFilter.ApplyFilter(RefDataNamespace.ReferenceData.Current.RelationshipType, q.Value, includeInactive: inactive))); break; + case var s when string.Compare(s, nameof(RefDataNamespace.USState), StringComparison.InvariantCultureIgnoreCase) == 0: coll.Add(new ReferenceDataMultiItem(nameof(RefDataNamespace.USState), ReferenceDataFilter.ApplyFilter(RefDataNamespace.ReferenceData.Current.USState, q.Value, includeInactive: inactive))); break; + case var s when string.Compare(s, nameof(RefDataNamespace.PerformanceOutcome), StringComparison.InvariantCultureIgnoreCase) == 0: coll.Add(new ReferenceDataMultiItem(nameof(RefDataNamespace.PerformanceOutcome), ReferenceDataFilter.ApplyFilter(RefDataNamespace.ReferenceData.Current.PerformanceOutcome, q.Value, includeInactive: inactive))); break; + } + } + + return coll; + }, operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Api/My.Hr.Api.csproj b/samples/My.Hr/My.Hr.Api/My.Hr.Api.csproj new file mode 100644 index 000000000..af2faeec7 --- /dev/null +++ b/samples/My.Hr/My.Hr.Api/My.Hr.Api.csproj @@ -0,0 +1,22 @@ + + + netcoreapp3.1 + enable + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Api/Program.cs b/samples/My.Hr/My.Hr.Api/Program.cs new file mode 100644 index 000000000..c70f5fffd --- /dev/null +++ b/samples/My.Hr/My.Hr.Api/Program.cs @@ -0,0 +1,24 @@ +using Beef.AspNetCore.WebApi; +using Microsoft.AspNetCore.Hosting; + +namespace My.Hr.Api +{ + /// + /// The Web API host/program. + /// + public static class Program + { + /// + /// Main startup. + /// + /// The startup arguments. + public static void Main(string[] args) => CreateHostBuilder(args).Build().Run(); + + /// + /// Creates the using the Beef capability to create the host with the underlying configuration probing. + /// + /// The startup arguments. + /// The . + public static IWebHostBuilder CreateHostBuilder(string[] args) => WebApiStartup.CreateWebHost(args, "Hr"); + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Api/Properties/launchSettings.json b/samples/My.Hr/My.Hr.Api/Properties/launchSettings.json new file mode 100644 index 000000000..3ce6a017c --- /dev/null +++ b/samples/My.Hr/My.Hr.Api/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:64784", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "My.Hr.Api": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Api/Startup.cs b/samples/My.Hr/My.Hr.Api/Startup.cs new file mode 100644 index 000000000..1f5da23e8 --- /dev/null +++ b/samples/My.Hr/My.Hr.Api/Startup.cs @@ -0,0 +1,128 @@ +using System; +using System.IO; +using System.Reflection; +using Beef; +using Beef.AspNetCore.WebApi; +using Beef.Caching.Policy; +using Beef.Data.Database; +using Beef.Data.EntityFrameworkCore; +using Beef.Entities; +using Beef.Events; +using Beef.Validation; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; +using My.Hr.Business; +using My.Hr.Business.Data; +using My.Hr.Business.DataSvc; + +namespace My.Hr.Api +{ + /// + /// Represents the startup class. + /// + public class Startup + { + private readonly IConfiguration _config; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public Startup(IConfiguration config) + { + _config = Check.NotNull(config, nameof(config)); + + // Use JSON property names in validation and default the page size. + ValidationArgs.DefaultUseJsonNames = true; + PagingArgs.DefaultTake = config.GetValue("BeefDefaultPageSize"); + } + + /// + /// The configure services method called by the runtime; use this method to add services to the container. + /// + /// The . + public void ConfigureServices(IServiceCollection services) + { + if (services == null) + throw new ArgumentNullException(nameof(services)); + + // Add the core beef services. + services.AddBeefExecutionContext() + .AddBeefRequestCache() + .AddBeefCachePolicyManager(_config.GetSection("BeefCaching").Get()) + .AddBeefWebApiServices() + .AddBeefBusinessServices(); + + // Add the beef database services (scoped per request/connection). + services.AddBeefDatabaseServices(() => new HrDb(WebApiStartup.GetConnectionString(_config, "Database"))); + + // Add the beef entity framework services (scoped per request/connection). + services.AddBeefEntityFrameworkServices(); + + // Add the generated reference data services. + services.AddGeneratedReferenceDataManagerServices() + .AddGeneratedReferenceDataDataSvcServices() + .AddGeneratedReferenceDataDataServices(); + + // Add the generated entity services. + services.AddGeneratedManagerServices() + .AddGeneratedDataSvcServices() + .AddGeneratedDataServices(); + + // Add event publishing services. + var ehcs = _config.GetValue("EventHubConnectionString"); + if (!string.IsNullOrEmpty(ehcs)) + services.AddBeefEventHubEventPublisher(ehcs); + else + services.AddBeefNullEventPublisher(); + + // Add additional services; note Beef requires NewtonsoftJson. + services.AddControllers().AddNewtonsoftJson(); + services.AddHealthChecks(); + services.AddHttpClient(); + + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "My.Hr API", Version = "v1" }); + + var xmlName = $"{Assembly.GetEntryAssembly()!.GetName().Name}.xml"; + var xmlFile = Path.Combine(AppContext.BaseDirectory, xmlName); + if (File.Exists(xmlFile)) + c.IncludeXmlComments(xmlFile); + }); + + services.AddSwaggerGenNewtonsoftSupport(); + } + + /// + /// The configure method called by the runtime; use this method to configure the HTTP request pipeline. + /// + /// The . + /// The . + public void Configure(IApplicationBuilder app, ILogger logger) + { + // Add exception handling to the pipeline. + app.UseWebApiExceptionHandler(logger, _config.GetValue("BeefIncludeExceptionInInternalServerError")); + + // Add Swagger as a JSON endpoint and to serve the swagger-ui to the pipeline. + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "My.Hr")); + + // Add execution context set up to the pipeline. + app.UseExecutionContext(); + + // Add health checks. + app.UseHealthChecks("/health"); + + // Use controllers. + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Api/appsettings.Development.json b/samples/My.Hr/My.Hr.Api/appsettings.Development.json new file mode 100644 index 000000000..e203e9407 --- /dev/null +++ b/samples/My.Hr/My.Hr.Api/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/samples/My.Hr/My.Hr.Api/appsettings.json b/samples/My.Hr/My.Hr.Api/appsettings.json new file mode 100644 index 000000000..da9a7f0aa --- /dev/null +++ b/samples/My.Hr/My.Hr.Api/appsettings.json @@ -0,0 +1,3 @@ +{ + // Used to override webapisettings.json +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Api/webapisettings.json b/samples/My.Hr/My.Hr.Api/webapisettings.json new file mode 100644 index 000000000..578c3bfbf --- /dev/null +++ b/samples/My.Hr/My.Hr.Api/webapisettings.json @@ -0,0 +1,38 @@ +{ + "UseUserSecrets": false, // Turns on: https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets (recommend setting via environment variable Hr_UseUserSecrets) + "KeyVaultName": null, // Turns on: https://docs.microsoft.com/en-us/aspnet/core/security/key-vault-configuration (recommend setting via environment variable Hr_KeyVaultName) + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "Database": "Data Source=.;Initial Catalog=My.Hr;Integrated Security=True" + }, + "BeefIncludeExceptionInInternalServerError": true, + "BeefDefaultPageSize": 25, + "BeefCaching": { + "Policies": [ + { + "Name": "30min sliding with 2hr max (default)", + "IsDefault": true, + "Policy": "Beef.Caching.Policy.SlidingCachePolicy, Beef.Core", + "Properties": [ + { + "Name": "Duration", + "Value": "00:30:00" + }, + { + "Name": "MaxDuration", + "Value": "02:00:00" + }, + { + "Name": "RandomizerOffset", + "Value": "05:00:00" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/Employee.cs b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/Employee.cs new file mode 100644 index 000000000..54eb55a7c --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/Employee.cs @@ -0,0 +1,106 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for an EF Model. + +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace My.Hr.Business.Data.EfModel +{ + /// + /// Represents the Entity Framework (EF) model for database object 'Hr.Employee'. + /// + public partial class Employee + { + /// + /// Gets or sets the 'EmployeeId' column value. + /// + public Guid EmployeeId { get; set; } + + /// + /// Gets or sets the 'Email' column value. + /// + public string? Email { get; set; } + + /// + /// Gets or sets the 'FirstName' column value. + /// + public string? FirstName { get; set; } + + /// + /// Gets or sets the 'LastName' column value. + /// + public string? LastName { get; set; } + + /// + /// Gets or sets the 'GenderCode' column value. + /// + public string? GenderCode { get; set; } + + /// + /// Gets or sets the 'Birthday' column value. + /// + public DateTime? Birthday { get; set; } + + /// + /// Gets or sets the 'StartDate' column value. + /// + public DateTime? StartDate { get; set; } + + /// + /// Gets or sets the 'TerminationDate' column value. + /// + public DateTime? TerminationDate { get; set; } + + /// + /// Gets or sets the 'TerminationReasonCode' column value. + /// + public string? TerminationReasonCode { get; set; } + + /// + /// Gets or sets the 'PhoneNo' column value. + /// + public string? PhoneNo { get; set; } + + /// + /// Adds the table/model configuration to the . + /// + /// The . + public static void AddToModel(ModelBuilder modelBuilder) + { + if (modelBuilder == null) + throw new ArgumentNullException(nameof(modelBuilder)); + + modelBuilder.Entity(entity => + { + entity.ToTable("Employee", "Hr"); + entity.HasKey("EmployeeId"); + entity.Property(p => p.EmployeeId).HasColumnType("UNIQUEIDENTIFIER"); + entity.Property(p => p.Email).HasColumnType("NVARCHAR(250)"); + entity.Property(p => p.FirstName).HasColumnType("NVARCHAR(100)"); + entity.Property(p => p.LastName).HasColumnType("NVARCHAR(100)"); + entity.Property(p => p.GenderCode).HasColumnType("NVARCHAR(50)"); + entity.Property(p => p.Birthday).HasColumnType("DATE"); + entity.Property(p => p.StartDate).HasColumnType("DATE"); + entity.Property(p => p.TerminationDate).HasColumnType("DATE"); + entity.Property(p => p.TerminationReasonCode).HasColumnType("NVARCHAR(50)"); + entity.Property(p => p.PhoneNo).HasColumnType("NVARCHAR(50)"); + AddToModel(entity); + }); + } + + /// + /// Enables further configuration of the underlying when configuring the . + /// + static partial void AddToModel(EntityTypeBuilder entity); + } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/Gender.cs b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/Gender.cs new file mode 100644 index 000000000..79efe8cec --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/Gender.cs @@ -0,0 +1,106 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for an EF Model. + +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace My.Hr.Business.Data.EfModel +{ + /// + /// Represents the Entity Framework (EF) model for database object 'Ref.Gender'. + /// + public partial class Gender + { + /// + /// Gets or sets the 'GenderId' column value. + /// + public Guid GenderId { get; set; } + + /// + /// Gets or sets the 'Code' column value. + /// + public string? Code { get; set; } + + /// + /// Gets or sets the 'Text' column value. + /// + public string? Text { get; set; } + + /// + /// Gets or sets the 'IsActive' column value. + /// + public bool? IsActive { get; set; } + + /// + /// Gets or sets the 'SortOrder' column value. + /// + public int? SortOrder { get; set; } + + /// + /// Gets or sets the 'RowVersion' column value. + /// + public byte[]? RowVersion { get; set; } + + /// + /// Gets or sets the 'CreatedBy' column value. + /// + public string? CreatedBy { get; set; } + + /// + /// Gets or sets the 'CreatedDate' column value. + /// + public DateTime? CreatedDate { get; set; } + + /// + /// Gets or sets the 'UpdatedBy' column value. + /// + public string? UpdatedBy { get; set; } + + /// + /// Gets or sets the 'UpdatedDate' column value. + /// + public DateTime? UpdatedDate { get; set; } + + /// + /// Adds the table/model configuration to the . + /// + /// The . + public static void AddToModel(ModelBuilder modelBuilder) + { + if (modelBuilder == null) + throw new ArgumentNullException(nameof(modelBuilder)); + + modelBuilder.Entity(entity => + { + entity.ToTable("Gender", "Ref"); + entity.HasKey("GenderId"); + entity.Property(p => p.GenderId).HasColumnType("UNIQUEIDENTIFIER"); + entity.Property(p => p.Code).HasColumnType("NVARCHAR(50)"); + entity.Property(p => p.Text).HasColumnType("NVARCHAR(250)"); + entity.Property(p => p.IsActive).HasColumnType("BIT"); + entity.Property(p => p.SortOrder).HasColumnType("INT"); + entity.Property(p => p.RowVersion).HasColumnType("TIMESTAMP").IsRowVersion(); + entity.Property(p => p.CreatedBy).HasColumnType("NVARCHAR(250)").ValueGeneratedOnUpdate(); + entity.Property(p => p.CreatedDate).HasColumnType("DATETIME2").ValueGeneratedOnUpdate(); + entity.Property(p => p.UpdatedBy).HasColumnType("NVARCHAR(250)").ValueGeneratedOnAdd(); + entity.Property(p => p.UpdatedDate).HasColumnType("DATETIME2").ValueGeneratedOnAdd(); + AddToModel(entity); + }); + } + + /// + /// Enables further configuration of the underlying when configuring the . + /// + static partial void AddToModel(EntityTypeBuilder entity); + } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/ModelBuilderExtensions.cs b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/ModelBuilderExtensions.cs new file mode 100644 index 000000000..be6bb46d5 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/ModelBuilderExtensions.cs @@ -0,0 +1,37 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable + +using Microsoft.EntityFrameworkCore; +using System; + +namespace My.Hr.Business.Data.EfModel +{ + /// + /// Represents extension methods for the . + /// + public static class ModelBuilderExtensions + { + /// + /// Adds all the generated models to the . + /// + /// The . + public static void AddGeneratedModels(this ModelBuilder modelBuilder) + { + if (modelBuilder == null) + throw new ArgumentNullException(nameof(modelBuilder)); + + Gender.AddToModel(modelBuilder); + TerminationReason.AddToModel(modelBuilder); + RelationshipType.AddToModel(modelBuilder); + USState.AddToModel(modelBuilder); + Employee.AddToModel(modelBuilder); + PerformanceReview.AddToModel(modelBuilder); + PerformanceOutcome.AddToModel(modelBuilder); + } + } +} + +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/PerformanceOutcome.cs b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/PerformanceOutcome.cs new file mode 100644 index 000000000..192cad575 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/PerformanceOutcome.cs @@ -0,0 +1,106 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for an EF Model. + +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace My.Hr.Business.Data.EfModel +{ + /// + /// Represents the Entity Framework (EF) model for database object 'Ref.PerformanceOutcome'. + /// + public partial class PerformanceOutcome + { + /// + /// Gets or sets the 'PerformanceOutcomeId' column value. + /// + public Guid PerformanceOutcomeId { get; set; } + + /// + /// Gets or sets the 'Code' column value. + /// + public string? Code { get; set; } + + /// + /// Gets or sets the 'Text' column value. + /// + public string? Text { get; set; } + + /// + /// Gets or sets the 'IsActive' column value. + /// + public bool? IsActive { get; set; } + + /// + /// Gets or sets the 'SortOrder' column value. + /// + public int? SortOrder { get; set; } + + /// + /// Gets or sets the 'RowVersion' column value. + /// + public byte[]? RowVersion { get; set; } + + /// + /// Gets or sets the 'CreatedBy' column value. + /// + public string? CreatedBy { get; set; } + + /// + /// Gets or sets the 'CreatedDate' column value. + /// + public DateTime? CreatedDate { get; set; } + + /// + /// Gets or sets the 'UpdatedBy' column value. + /// + public string? UpdatedBy { get; set; } + + /// + /// Gets or sets the 'UpdatedDate' column value. + /// + public DateTime? UpdatedDate { get; set; } + + /// + /// Adds the table/model configuration to the . + /// + /// The . + public static void AddToModel(ModelBuilder modelBuilder) + { + if (modelBuilder == null) + throw new ArgumentNullException(nameof(modelBuilder)); + + modelBuilder.Entity(entity => + { + entity.ToTable("PerformanceOutcome", "Ref"); + entity.HasKey("PerformanceOutcomeId"); + entity.Property(p => p.PerformanceOutcomeId).HasColumnType("UNIQUEIDENTIFIER"); + entity.Property(p => p.Code).HasColumnType("NVARCHAR(50)"); + entity.Property(p => p.Text).HasColumnType("NVARCHAR(250)"); + entity.Property(p => p.IsActive).HasColumnType("BIT"); + entity.Property(p => p.SortOrder).HasColumnType("INT"); + entity.Property(p => p.RowVersion).HasColumnType("TIMESTAMP").IsRowVersion(); + entity.Property(p => p.CreatedBy).HasColumnType("NVARCHAR(250)").ValueGeneratedOnUpdate(); + entity.Property(p => p.CreatedDate).HasColumnType("DATETIME2").ValueGeneratedOnUpdate(); + entity.Property(p => p.UpdatedBy).HasColumnType("NVARCHAR(250)").ValueGeneratedOnAdd(); + entity.Property(p => p.UpdatedDate).HasColumnType("DATETIME2").ValueGeneratedOnAdd(); + AddToModel(entity); + }); + } + + /// + /// Enables further configuration of the underlying when configuring the . + /// + static partial void AddToModel(EntityTypeBuilder entity); + } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/PerformanceReview.cs b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/PerformanceReview.cs new file mode 100644 index 000000000..d5b0f059b --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/PerformanceReview.cs @@ -0,0 +1,112 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for an EF Model. + +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace My.Hr.Business.Data.EfModel +{ + /// + /// Represents the Entity Framework (EF) model for database object 'Hr.PerformanceReview'. + /// + public partial class PerformanceReview + { + /// + /// Gets or sets the 'PerformanceReviewId' column value. + /// + public Guid PerformanceReviewId { get; set; } + + /// + /// Gets or sets the 'EmployeeId' column value. + /// + public Guid EmployeeId { get; set; } + + /// + /// Gets or sets the 'Date' column value. + /// + public DateTime? Date { get; set; } + + /// + /// Gets or sets the 'PerformanceOutcomeCode' column value. + /// + public string? PerformanceOutcomeCode { get; set; } + + /// + /// Gets or sets the 'Reviewer' column value. + /// + public string? Reviewer { get; set; } + + /// + /// Gets or sets the 'Notes' column value. + /// + public string? Notes { get; set; } + + /// + /// Gets or sets the 'RowVersion' column value. + /// + public byte[]? RowVersion { get; set; } + + /// + /// Gets or sets the 'CreatedBy' column value. + /// + public string? CreatedBy { get; set; } + + /// + /// Gets or sets the 'CreatedDate' column value. + /// + public DateTime? CreatedDate { get; set; } + + /// + /// Gets or sets the 'UpdatedBy' column value. + /// + public string? UpdatedBy { get; set; } + + /// + /// Gets or sets the 'UpdatedDate' column value. + /// + public DateTime? UpdatedDate { get; set; } + + /// + /// Adds the table/model configuration to the . + /// + /// The . + public static void AddToModel(ModelBuilder modelBuilder) + { + if (modelBuilder == null) + throw new ArgumentNullException(nameof(modelBuilder)); + + modelBuilder.Entity(entity => + { + entity.ToTable("PerformanceReview", "Hr"); + entity.HasKey("PerformanceReviewId"); + entity.Property(p => p.PerformanceReviewId).HasColumnType("UNIQUEIDENTIFIER"); + entity.Property(p => p.EmployeeId).HasColumnType("UNIQUEIDENTIFIER"); + entity.Property(p => p.Date).HasColumnType("DATETIME2"); + entity.Property(p => p.PerformanceOutcomeCode).HasColumnType("NVARCHAR(50)"); + entity.Property(p => p.Reviewer).HasColumnType("NVARCHAR(100)"); + entity.Property(p => p.Notes).HasColumnType("NVARCHAR(4000)"); + entity.Property(p => p.RowVersion).HasColumnType("TIMESTAMP").IsRowVersion(); + entity.Property(p => p.CreatedBy).HasColumnType("NVARCHAR(250)").ValueGeneratedOnUpdate(); + entity.Property(p => p.CreatedDate).HasColumnType("DATETIME2").ValueGeneratedOnUpdate(); + entity.Property(p => p.UpdatedBy).HasColumnType("NVARCHAR(250)").ValueGeneratedOnAdd(); + entity.Property(p => p.UpdatedDate).HasColumnType("DATETIME2").ValueGeneratedOnAdd(); + AddToModel(entity); + }); + } + + /// + /// Enables further configuration of the underlying when configuring the . + /// + static partial void AddToModel(EntityTypeBuilder entity); + } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/RelationshipType.cs b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/RelationshipType.cs new file mode 100644 index 000000000..96da0691c --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/RelationshipType.cs @@ -0,0 +1,106 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for an EF Model. + +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace My.Hr.Business.Data.EfModel +{ + /// + /// Represents the Entity Framework (EF) model for database object 'Ref.RelationshipType'. + /// + public partial class RelationshipType + { + /// + /// Gets or sets the 'RelationshipTypeId' column value. + /// + public Guid RelationshipTypeId { get; set; } + + /// + /// Gets or sets the 'Code' column value. + /// + public string? Code { get; set; } + + /// + /// Gets or sets the 'Text' column value. + /// + public string? Text { get; set; } + + /// + /// Gets or sets the 'IsActive' column value. + /// + public bool? IsActive { get; set; } + + /// + /// Gets or sets the 'SortOrder' column value. + /// + public int? SortOrder { get; set; } + + /// + /// Gets or sets the 'RowVersion' column value. + /// + public byte[]? RowVersion { get; set; } + + /// + /// Gets or sets the 'CreatedBy' column value. + /// + public string? CreatedBy { get; set; } + + /// + /// Gets or sets the 'CreatedDate' column value. + /// + public DateTime? CreatedDate { get; set; } + + /// + /// Gets or sets the 'UpdatedBy' column value. + /// + public string? UpdatedBy { get; set; } + + /// + /// Gets or sets the 'UpdatedDate' column value. + /// + public DateTime? UpdatedDate { get; set; } + + /// + /// Adds the table/model configuration to the . + /// + /// The . + public static void AddToModel(ModelBuilder modelBuilder) + { + if (modelBuilder == null) + throw new ArgumentNullException(nameof(modelBuilder)); + + modelBuilder.Entity(entity => + { + entity.ToTable("RelationshipType", "Ref"); + entity.HasKey("RelationshipTypeId"); + entity.Property(p => p.RelationshipTypeId).HasColumnType("UNIQUEIDENTIFIER"); + entity.Property(p => p.Code).HasColumnType("NVARCHAR(50)"); + entity.Property(p => p.Text).HasColumnType("NVARCHAR(250)"); + entity.Property(p => p.IsActive).HasColumnType("BIT"); + entity.Property(p => p.SortOrder).HasColumnType("INT"); + entity.Property(p => p.RowVersion).HasColumnType("TIMESTAMP").IsRowVersion(); + entity.Property(p => p.CreatedBy).HasColumnType("NVARCHAR(250)").ValueGeneratedOnUpdate(); + entity.Property(p => p.CreatedDate).HasColumnType("DATETIME2").ValueGeneratedOnUpdate(); + entity.Property(p => p.UpdatedBy).HasColumnType("NVARCHAR(250)").ValueGeneratedOnAdd(); + entity.Property(p => p.UpdatedDate).HasColumnType("DATETIME2").ValueGeneratedOnAdd(); + AddToModel(entity); + }); + } + + /// + /// Enables further configuration of the underlying when configuring the . + /// + static partial void AddToModel(EntityTypeBuilder entity); + } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/TerminationReason.cs b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/TerminationReason.cs new file mode 100644 index 000000000..5380021dc --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/TerminationReason.cs @@ -0,0 +1,106 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for an EF Model. + +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace My.Hr.Business.Data.EfModel +{ + /// + /// Represents the Entity Framework (EF) model for database object 'Ref.TerminationReason'. + /// + public partial class TerminationReason + { + /// + /// Gets or sets the 'TerminationReasonId' column value. + /// + public Guid TerminationReasonId { get; set; } + + /// + /// Gets or sets the 'Code' column value. + /// + public string? Code { get; set; } + + /// + /// Gets or sets the 'Text' column value. + /// + public string? Text { get; set; } + + /// + /// Gets or sets the 'IsActive' column value. + /// + public bool? IsActive { get; set; } + + /// + /// Gets or sets the 'SortOrder' column value. + /// + public int? SortOrder { get; set; } + + /// + /// Gets or sets the 'RowVersion' column value. + /// + public byte[]? RowVersion { get; set; } + + /// + /// Gets or sets the 'CreatedBy' column value. + /// + public string? CreatedBy { get; set; } + + /// + /// Gets or sets the 'CreatedDate' column value. + /// + public DateTime? CreatedDate { get; set; } + + /// + /// Gets or sets the 'UpdatedBy' column value. + /// + public string? UpdatedBy { get; set; } + + /// + /// Gets or sets the 'UpdatedDate' column value. + /// + public DateTime? UpdatedDate { get; set; } + + /// + /// Adds the table/model configuration to the . + /// + /// The . + public static void AddToModel(ModelBuilder modelBuilder) + { + if (modelBuilder == null) + throw new ArgumentNullException(nameof(modelBuilder)); + + modelBuilder.Entity(entity => + { + entity.ToTable("TerminationReason", "Ref"); + entity.HasKey("TerminationReasonId"); + entity.Property(p => p.TerminationReasonId).HasColumnType("UNIQUEIDENTIFIER"); + entity.Property(p => p.Code).HasColumnType("NVARCHAR(50)"); + entity.Property(p => p.Text).HasColumnType("NVARCHAR(250)"); + entity.Property(p => p.IsActive).HasColumnType("BIT"); + entity.Property(p => p.SortOrder).HasColumnType("INT"); + entity.Property(p => p.RowVersion).HasColumnType("TIMESTAMP").IsRowVersion(); + entity.Property(p => p.CreatedBy).HasColumnType("NVARCHAR(250)").ValueGeneratedOnUpdate(); + entity.Property(p => p.CreatedDate).HasColumnType("DATETIME2").ValueGeneratedOnUpdate(); + entity.Property(p => p.UpdatedBy).HasColumnType("NVARCHAR(250)").ValueGeneratedOnAdd(); + entity.Property(p => p.UpdatedDate).HasColumnType("DATETIME2").ValueGeneratedOnAdd(); + AddToModel(entity); + }); + } + + /// + /// Enables further configuration of the underlying when configuring the . + /// + static partial void AddToModel(EntityTypeBuilder entity); + } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/USState.cs b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/USState.cs new file mode 100644 index 000000000..bd7637b04 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/USState.cs @@ -0,0 +1,106 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for an EF Model. + +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace My.Hr.Business.Data.EfModel +{ + /// + /// Represents the Entity Framework (EF) model for database object 'Ref.USState'. + /// + public partial class USState + { + /// + /// Gets or sets the 'USStateId' column value. + /// + public Guid USStateId { get; set; } + + /// + /// Gets or sets the 'Code' column value. + /// + public string? Code { get; set; } + + /// + /// Gets or sets the 'Text' column value. + /// + public string? Text { get; set; } + + /// + /// Gets or sets the 'IsActive' column value. + /// + public bool? IsActive { get; set; } + + /// + /// Gets or sets the 'SortOrder' column value. + /// + public int? SortOrder { get; set; } + + /// + /// Gets or sets the 'RowVersion' column value. + /// + public byte[]? RowVersion { get; set; } + + /// + /// Gets or sets the 'CreatedBy' column value. + /// + public string? CreatedBy { get; set; } + + /// + /// Gets or sets the 'CreatedDate' column value. + /// + public DateTime? CreatedDate { get; set; } + + /// + /// Gets or sets the 'UpdatedBy' column value. + /// + public string? UpdatedBy { get; set; } + + /// + /// Gets or sets the 'UpdatedDate' column value. + /// + public DateTime? UpdatedDate { get; set; } + + /// + /// Adds the table/model configuration to the . + /// + /// The . + public static void AddToModel(ModelBuilder modelBuilder) + { + if (modelBuilder == null) + throw new ArgumentNullException(nameof(modelBuilder)); + + modelBuilder.Entity(entity => + { + entity.ToTable("USState", "Ref"); + entity.HasKey("USStateId"); + entity.Property(p => p.USStateId).HasColumnType("UNIQUEIDENTIFIER"); + entity.Property(p => p.Code).HasColumnType("NVARCHAR(50)"); + entity.Property(p => p.Text).HasColumnType("NVARCHAR(250)"); + entity.Property(p => p.IsActive).HasColumnType("BIT"); + entity.Property(p => p.SortOrder).HasColumnType("INT"); + entity.Property(p => p.RowVersion).HasColumnType("TIMESTAMP").IsRowVersion(); + entity.Property(p => p.CreatedBy).HasColumnType("NVARCHAR(250)").ValueGeneratedOnUpdate(); + entity.Property(p => p.CreatedDate).HasColumnType("DATETIME2").ValueGeneratedOnUpdate(); + entity.Property(p => p.UpdatedBy).HasColumnType("NVARCHAR(250)").ValueGeneratedOnAdd(); + entity.Property(p => p.UpdatedDate).HasColumnType("DATETIME2").ValueGeneratedOnAdd(); + AddToModel(entity); + }); + } + + /// + /// Enables further configuration of the underlying when configuring the . + /// + static partial void AddToModel(EntityTypeBuilder entity); + } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/EmployeeBaseData.cs b/samples/My.Hr/My.Hr.Business/Data/EmployeeBaseData.cs new file mode 100644 index 000000000..edc4674f4 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/EmployeeBaseData.cs @@ -0,0 +1,14 @@ +namespace My.Hr.Business.Data +{ + public partial class EmployeeBaseData + { + public partial class EfMapper + { + partial void EfMapperCtor() + { + // Adds a mapper between TerminationDetail and EfModel.Employee; this is a special case as it is not a direct property to property mapping within same type. + SrceProperty(s => s.Termination).SetMapper(TerminationDetailData.EfMapper.Default); + } + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/EmployeeData.cs b/samples/My.Hr/My.Hr.Business/Data/EmployeeData.cs new file mode 100644 index 000000000..e2b7215a1 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/EmployeeData.cs @@ -0,0 +1,100 @@ +using Beef; +using Beef.Data.Database; +using Microsoft.EntityFrameworkCore; +using My.Hr.Common.Entities; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace My.Hr.Business.Data +{ + public partial class EmployeeData + { + partial void EmployeeDataCtor() + { + // Implement the GetByArgs OnQuery search/filtering logic. + _getByArgsOnQuery = (q, args, _) => + { + _ef.WithWildcard(args?.FirstName, (w) => q = q.Where(x => EF.Functions.Like(x.FirstName, w))); + _ef.WithWildcard(args?.LastName, (w) => q = q.Where(x => EF.Functions.Like(x.LastName, w))); + _ef.With(args?.Genders, () => q = q.Where(x => args!.Genders!.ToCodeList().Contains(x.GenderCode))); + _ef.With(args?.StartFrom, () => q = q.Where(x => x.StartDate >= args!.StartFrom)); + _ef.With(args?.StartTo, () => q = q.Where(x => x.StartDate <= args!.StartTo)); + + if (args?.IsIncludeTerminated == null || !args.IsIncludeTerminated.Value) + q = q.Where(x => x.TerminationDate == null); + + return q.OrderBy(x => x.LastName).ThenBy(x => x.FirstName).ThenBy(x => x.StartDate); + }; + } + + /// + /// Executes the 'Get' stored procedure passing the identifier and returns the result. + /// + private Task GetOnImplementationAsync(Guid id) => + ExecuteStatement(_db.StoredProcedure("[Hr].[spEmployeeGet]").Param(DbMapper.Default.GetParamName(nameof(Employee.Id)), id)); + + /// + /// Executes the 'Create' stored procedure and returns the result. + /// + private Task CreateOnImplementationAsync(Employee value) => + ExecuteStatement("[Hr].[spEmployeeCreate]", value, Beef.Mapper.OperationTypes.Create); + + /// + /// Executes the 'Update' stored procedure and returns the result. + /// + private Task UpdateOnImplementationAsync(Employee value) => + ExecuteStatement("[Hr].[spEmployeeUpdate]", value, Beef.Mapper.OperationTypes.Update); + + /// + /// Executes the stored procedure, passing Employee parameters, and the EmergencyContacts as a table-valued parameter (TVP), the operation type to aid mapping, + /// and requests for the result to be reselected. + /// + private Task ExecuteStatement(string storedProcedureName, Employee value, Beef.Mapper.OperationTypes operationType) + { + var sp = _db.StoredProcedure(storedProcedureName) + .Params(p => DbMapper.Default.MapToDb(value, p, operationType)) + .TableValuedParam("@EmergencyContactList", EmergencyContactData.DbMapper.Default.CreateTableValuedParameter(value.EmergencyContacts!)) + .ReselectRecordParam(); + + return ExecuteStatement(sp)!; + } + + /// + /// Executes the underlying stored procedure and processes the result (used by Get, Create and Update). + /// + private async Task ExecuteStatement(DatabaseCommand db) + { + Employee? employee = null; + + // Execute the generated stored procedure, selecting (querying) two sets of data: + // 1. The selected Employee (single row), the row is not mandatory, and stop (do not goto second set) where null. Use the underlying DbMapper to map between columns and .NET Type. + // 2. Zero or more EmergencyContact rows. Use EmergencyContactData.DbMapper to map between columns and .NET Type. Update the Employee with result. + await db.SelectQueryMultiSetAsync( + new MultiSetSingleArgs(DbMapper.Default, r => employee = r, isMandatory: false, stopOnNull: true), + new MultiSetCollArgs(EmergencyContactData.DbMapper.Default, r => employee!.EmergencyContacts = r)); + + return employee; + } + + /// + /// Terminates an existing employee by updating their termination columns. + /// + private async Task TerminateOnImplementationAsync(TerminationDetail value, Guid id) + { + // Need to pre-query the data to, 1) check they exist, 2) check they are still employed, and 3) update. + var curr = await GetOnImplementationAsync(id); + if (curr == null) + throw new NotFoundException(); + + if (curr.Termination != null) + throw new ValidationException("An Employee can not be terminated more than once."); + + if (value.Date < curr.StartDate) + throw new ValidationException("An Employee can not be terminated prior to their start date."); + + curr.Termination = value; + return await UpdateOnImplementationAsync(curr); + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/Generated/EmergencyContactData.cs b/samples/My.Hr/My.Hr.Business/Data/Generated/EmergencyContactData.cs new file mode 100644 index 000000000..6062d3502 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/Generated/EmergencyContactData.cs @@ -0,0 +1,57 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Business; +using Beef.Data.Database; +using Beef.Entities; +using Beef.Mapper; +using Beef.Mapper.Converters; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business.Data +{ + /// + /// Provides the data access. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] + public partial class EmergencyContactData + { + + /// + /// Provides the property and database column mapping. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] + public partial class DbMapper : DatabaseMapper + { + /// + /// Initializes a new instance of the class. + /// + public DbMapper() + { + Property(s => s.Id, "EmergencyContactId").SetUniqueKey(false); + Property(s => s.FirstName); + Property(s => s.LastName); + Property(s => s.PhoneNo); + Property(s => s.RelationshipSid, "RelationshipTypeCode"); + AddStandardProperties(); + DbMapperCtor(); + } + + partial void DbMapperCtor(); // Enables the DbMapper constructor to be extended. + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/Generated/EmergencyContactDataTvp.cs b/samples/My.Hr/My.Hr.Business/Data/Generated/EmergencyContactDataTvp.cs new file mode 100644 index 000000000..672d2e8db --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/Generated/EmergencyContactDataTvp.cs @@ -0,0 +1,41 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Data; +using Beef.Data.Database; +using My.Hr.Common.Entities; + +namespace My.Hr.Business.Data +{ + public partial class EmergencyContactData + { + public partial class DbMapper + { + /// + /// Creates a for the . + /// + /// The entity list. + /// The Table-Valued Parameter. + public TableValuedParameter CreateTableValuedParameter(IEnumerable list) + { + var dt = new DataTable(); + dt.Columns.Add("EmergencyContactId", typeof(Guid)); + dt.Columns.Add("FirstName", typeof(string)); + dt.Columns.Add("LastName", typeof(string)); + dt.Columns.Add("PhoneNo", typeof(string)); + dt.Columns.Add("RelationshipTypeCode", typeof(string)); + + var tvp = new TableValuedParameter("[Hr].[udtEmergencyContactList]", dt); + AddToTableValuedParameter(tvp, list); + return tvp; + } + } + } +} + +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/Generated/EmployeeBaseData.cs b/samples/My.Hr/My.Hr.Business/Data/Generated/EmployeeBaseData.cs new file mode 100644 index 000000000..8ee9f5c09 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/Generated/EmployeeBaseData.cs @@ -0,0 +1,88 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Business; +using Beef.Data.Database; +using Beef.Data.EntityFrameworkCore; +using Beef.Entities; +using Beef.Mapper; +using Beef.Mapper.Converters; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business.Data +{ + /// + /// Provides the data access. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] + public partial class EmployeeBaseData + { + + /// + /// Provides the property and database column mapping. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] + public partial class DbMapper : DatabaseMapper + { + /// + /// Initializes a new instance of the class. + /// + public DbMapper() + { + Property(s => s.Id, "EmployeeId").SetUniqueKey(true); + Property(s => s.Email); + Property(s => s.FirstName); + Property(s => s.LastName); + Property(s => s.GenderSid, "GenderCode"); + Property(s => s.Birthday); + Property(s => s.StartDate); + Property(s => s.Termination).SetMapper(TerminationDetailData.DbMapper.Default!); + Property(s => s.PhoneNo); + AddStandardProperties(); + DbMapperCtor(); + } + + partial void DbMapperCtor(); // Enables the DbMapper constructor to be extended. + } + + /// + /// Provides the and Entity Framework property mapping. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] + public partial class EfMapper : EfDbMapper + { + /// + /// Initializes a new instance of the class. + /// + public EfMapper() + { + Property(s => s.Id, d => d.EmployeeId).SetUniqueKey(true); + Property(s => s.Email, d => d.Email); + Property(s => s.FirstName, d => d.FirstName); + Property(s => s.LastName, d => d.LastName); + Property(s => s.GenderSid, d => d.GenderCode); + Property(s => s.Birthday, d => d.Birthday); + Property(s => s.StartDate, d => d.StartDate); + Property(s => s.PhoneNo, d => d.PhoneNo); + AddStandardProperties(); + EfMapperCtor(); + } + + partial void EfMapperCtor(); // Enables the EfMapper constructor to be extended. + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/Generated/EmployeeData.cs b/samples/My.Hr/My.Hr.Business/Data/Generated/EmployeeData.cs new file mode 100644 index 000000000..f4fd94ef0 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/Generated/EmployeeData.cs @@ -0,0 +1,138 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Business; +using Beef.Data.Database; +using Beef.Data.EntityFrameworkCore; +using Beef.Entities; +using Beef.Mapper; +using Beef.Mapper.Converters; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business.Data +{ + /// + /// Provides the data access. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] + public partial class EmployeeData : IEmployeeData + { + private readonly IDatabase _db; + private readonly IEfDb _ef; + + #region Extensions + #pragma warning disable CS0649, IDE0044 // Defaults to null by design; can be overridden in constructor. + + private Func, EmployeeArgs?, IEfDbArgs, IQueryable>? _getByArgsOnQuery; + + #pragma warning restore CS0649, IDE0044 + #endregion + + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + public EmployeeData(IDatabase db, IEfDb ef) + { _db = Check.NotNull(db, nameof(db)); _ef = Check.NotNull(ef, nameof(ef)); EmployeeDataCtor(); } + + partial void EmployeeDataCtor(); // Enables additional functionality to be added to the constructor. + + /// + /// Gets the specified . + /// + /// The identifier. + /// The selected where found. + public Task GetAsync(Guid id) + => DataInvoker.Current.InvokeAsync(this, () => GetOnImplementationAsync(id)); + + /// + /// Creates a new . + /// + /// The . + /// The created . + public Task CreateAsync(Employee value) + => DataInvoker.Current.InvokeAsync(this, () => CreateOnImplementationAsync(Check.NotNull(value, nameof(value)))); + + /// + /// Updates an existing . + /// + /// The . + /// The updated . + public Task UpdateAsync(Employee value) + => DataInvoker.Current.InvokeAsync(this, () => UpdateOnImplementationAsync(Check.NotNull(value, nameof(value)))); + + /// + /// Deletes the specified . + /// + /// The Id. + public Task DeleteAsync(Guid id) + { + return DataInvoker.Current.InvokeAsync(this, async () => + { + var __dataArgs = DbMapper.Default.CreateArgs("[Hr].[spEmployeeDelete]"); + await _db.DeleteAsync(__dataArgs, id).ConfigureAwait(false); + }); + } + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The Args (see ). + /// The . + /// The . + public Task GetByArgsAsync(EmployeeArgs? args, PagingArgs? paging) + { + return DataInvoker.Current.InvokeAsync(this, async () => + { + EmployeeBaseCollectionResult __result = new EmployeeBaseCollectionResult(paging); + var __dataArgs = EmployeeBaseData.EfMapper.Default.CreateArgs(__result.Paging!); + __result.Result = _ef.Query(__dataArgs, q => _getByArgsOnQuery?.Invoke(q, args, __dataArgs) ?? q).SelectQuery(); + return await Task.FromResult(__result).ConfigureAwait(false); + }); + } + + /// + /// Terminates an existing . + /// + /// The . + /// The identifier. + /// The updated . + public Task TerminateAsync(TerminationDetail value, Guid id) + => DataInvoker.Current.InvokeAsync(this, () => TerminateOnImplementationAsync(Check.NotNull(value, nameof(value)), id)); + + /// + /// Provides the property and database column mapping. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] + public partial class DbMapper : DatabaseMapper + { + /// + /// Initializes a new instance of the class. + /// + public DbMapper() + { + InheritPropertiesFrom(EmployeeBaseData.DbMapper.Default); + Property(s => s.Address, "AddressJson").SetConverter(ObjectToJsonConverter
.Default!); + AddStandardProperties(); + DbMapperCtor(); + } + + partial void DbMapperCtor(); // Enables the DbMapper constructor to be extended. + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/Generated/IEmployeeData.cs b/samples/My.Hr/My.Hr.Business/Data/Generated/IEmployeeData.cs new file mode 100644 index 000000000..c56694f34 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/Generated/IEmployeeData.cs @@ -0,0 +1,70 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Entities; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business.Data +{ + /// + /// Defines the data access. + /// + public partial interface IEmployeeData + { + /// + /// Gets the specified . + /// + /// The identifier. + /// The selected where found. + Task GetAsync(Guid id); + + /// + /// Creates a new . + /// + /// The . + /// The created . + Task CreateAsync(Employee value); + + /// + /// Updates an existing . + /// + /// The . + /// The updated . + Task UpdateAsync(Employee value); + + /// + /// Deletes the specified . + /// + /// The Id. + Task DeleteAsync(Guid id); + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The Args (see ). + /// The . + /// The . + Task GetByArgsAsync(EmployeeArgs? args, PagingArgs? paging); + + /// + /// Terminates an existing . + /// + /// The . + /// The identifier. + /// The updated . + Task TerminateAsync(TerminationDetail value, Guid id); + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/Generated/IPerformanceReviewData.cs b/samples/My.Hr/My.Hr.Business/Data/Generated/IPerformanceReviewData.cs new file mode 100644 index 000000000..5bb464db7 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/Generated/IPerformanceReviewData.cs @@ -0,0 +1,62 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Entities; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business.Data +{ + /// + /// Defines the data access. + /// + public partial interface IPerformanceReviewData + { + /// + /// Gets the specified . + /// + /// The identifier. + /// The selected where found. + Task GetAsync(Guid id); + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The . + /// The . + /// The . + Task GetByEmployeeIdAsync(Guid employeeId, PagingArgs? paging); + + /// + /// Creates a new . + /// + /// The . + /// The created . + Task CreateAsync(PerformanceReview value); + + /// + /// Updates an existing . + /// + /// The . + /// The updated . + Task UpdateAsync(PerformanceReview value); + + /// + /// Deletes the specified . + /// + /// The identifier. + Task DeleteAsync(Guid id); + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/Generated/IReferenceDataData.cs b/samples/My.Hr/My.Hr.Business/Data/Generated/IReferenceDataData.cs new file mode 100644 index 000000000..7e209c140 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/Generated/IReferenceDataData.cs @@ -0,0 +1,55 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Data; +using System.Text; +using System.Threading.Tasks; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business.Data +{ + /// + /// Provides the ReferenceData data access. + /// + public partial interface IReferenceDataData + { + /// + /// Gets all the items. + /// + /// The . + Task GenderGetAllAsync(); + + /// + /// Gets all the items. + /// + /// The . + Task TerminationReasonGetAllAsync(); + + /// + /// Gets all the items. + /// + /// The . + Task RelationshipTypeGetAllAsync(); + + /// + /// Gets all the items. + /// + /// The . + Task USStateGetAllAsync(); + + /// + /// Gets all the items. + /// + /// The . + Task PerformanceOutcomeGetAllAsync(); + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/Generated/PerformanceReviewData.cs b/samples/My.Hr/My.Hr.Business/Data/Generated/PerformanceReviewData.cs new file mode 100644 index 000000000..507efbe23 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/Generated/PerformanceReviewData.cs @@ -0,0 +1,148 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Business; +using Beef.Data.EntityFrameworkCore; +using Beef.Entities; +using Beef.Mapper; +using Beef.Mapper.Converters; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business.Data +{ + /// + /// Provides the data access. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] + public partial class PerformanceReviewData : IPerformanceReviewData + { + private readonly IEfDb _ef; + + #region Extensions + #pragma warning disable CS0649, IDE0044 // Defaults to null by design; can be overridden in constructor. + + private Func, Guid, IEfDbArgs, IQueryable>? _getByEmployeeIdOnQuery; + + #pragma warning restore CS0649, IDE0044 + #endregion + + /// + /// Initializes a new instance of the class. + /// + /// The . + public PerformanceReviewData(IEfDb ef) + { _ef = Check.NotNull(ef, nameof(ef)); PerformanceReviewDataCtor(); } + + partial void PerformanceReviewDataCtor(); // Enables additional functionality to be added to the constructor. + + /// + /// Gets the specified . + /// + /// The identifier. + /// The selected where found. + public Task GetAsync(Guid id) + { + return DataInvoker.Current.InvokeAsync(this, async () => + { + var __dataArgs = EfMapper.Default.CreateArgs(); + return await _ef.GetAsync(__dataArgs, id).ConfigureAwait(false); + }); + } + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The . + /// The . + /// The . + public Task GetByEmployeeIdAsync(Guid employeeId, PagingArgs? paging) + { + return DataInvoker.Current.InvokeAsync(this, async () => + { + PerformanceReviewCollectionResult __result = new PerformanceReviewCollectionResult(paging); + var __dataArgs = EfMapper.Default.CreateArgs(__result.Paging!); + __result.Result = _ef.Query(__dataArgs, q => _getByEmployeeIdOnQuery?.Invoke(q, employeeId, __dataArgs) ?? q).SelectQuery(); + return await Task.FromResult(__result).ConfigureAwait(false); + }); + } + + /// + /// Creates a new . + /// + /// The . + /// The created . + public Task CreateAsync(PerformanceReview value) + { + return DataInvoker.Current.InvokeAsync(this, async () => + { + var __dataArgs = EfMapper.Default.CreateArgs(); + return await _ef.CreateAsync(__dataArgs, Check.NotNull(value, nameof(value))).ConfigureAwait(false); + }); + } + + /// + /// Updates an existing . + /// + /// The . + /// The updated . + public Task UpdateAsync(PerformanceReview value) + { + return DataInvoker.Current.InvokeAsync(this, async () => + { + var __dataArgs = EfMapper.Default.CreateArgs(); + return await _ef.UpdateAsync(__dataArgs, Check.NotNull(value, nameof(value))).ConfigureAwait(false); + }); + } + + /// + /// Deletes the specified . + /// + /// The identifier. + public Task DeleteAsync(Guid id) + { + return DataInvoker.Current.InvokeAsync(this, async () => + { + var __dataArgs = EfMapper.Default.CreateArgs(); + await _ef.DeleteAsync(__dataArgs, id).ConfigureAwait(false); + }); + } + + /// + /// Provides the and Entity Framework property mapping. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] + public partial class EfMapper : EfDbMapper + { + /// + /// Initializes a new instance of the class. + /// + public EfMapper() + { + Property(s => s.Id, d => d.PerformanceReviewId).SetUniqueKey(true); + Property(s => s.EmployeeId, d => d.EmployeeId).SetOperationTypes(OperationTypes.AnyExceptUpdate); + Property(s => s.Date, d => d.Date); + Property(s => s.OutcomeSid, d => d.PerformanceOutcomeCode); + Property(s => s.Reviewer, d => d.Reviewer); + Property(s => s.Notes, d => d.Notes); + AddStandardProperties(); + EfMapperCtor(); + } + + partial void EfMapperCtor(); // Enables the EfMapper constructor to be extended. + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/Generated/ReferenceDataData.cs b/samples/My.Hr/My.Hr.Business/Data/Generated/ReferenceDataData.cs new file mode 100644 index 000000000..9a4305be6 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/Generated/ReferenceDataData.cs @@ -0,0 +1,130 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Business; +using Beef.Data.EntityFrameworkCore; +using Beef.Mapper; +using Beef.Mapper.Converters; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business.Data +{ + /// + /// Provides the ReferenceData data access. + /// + public partial class ReferenceDataData : IReferenceDataData + { + private readonly IEfDb _ef; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public ReferenceDataData(IEfDb ef) + { _ef = Check.NotNull(ef, nameof(ef)); DataCtor(); } + + partial void DataCtor(); // Enables additional functionality to be added to the constructor. + + /// + /// Gets all the items. + /// + /// The . + public async Task GenderGetAllAsync() + { + var __coll = new RefDataNamespace.GenderCollection(); + await DataInvoker.Current.InvokeAsync(this, async () => { _ef.Query(GenderMapper.CreateArgs()).SelectQuery(__coll); await Task.CompletedTask.ConfigureAwait(false); }, BusinessInvokerArgs.TransactionSuppress).ConfigureAwait(false); + return __coll; + } + + /// + /// Gets all the items. + /// + /// The . + public async Task TerminationReasonGetAllAsync() + { + var __coll = new RefDataNamespace.TerminationReasonCollection(); + await DataInvoker.Current.InvokeAsync(this, async () => { _ef.Query(TerminationReasonMapper.CreateArgs()).SelectQuery(__coll); await Task.CompletedTask.ConfigureAwait(false); }, BusinessInvokerArgs.TransactionSuppress).ConfigureAwait(false); + return __coll; + } + + /// + /// Gets all the items. + /// + /// The . + public async Task RelationshipTypeGetAllAsync() + { + var __coll = new RefDataNamespace.RelationshipTypeCollection(); + await DataInvoker.Current.InvokeAsync(this, async () => { _ef.Query(RelationshipTypeMapper.CreateArgs()).SelectQuery(__coll); await Task.CompletedTask.ConfigureAwait(false); }, BusinessInvokerArgs.TransactionSuppress).ConfigureAwait(false); + return __coll; + } + + /// + /// Gets all the items. + /// + /// The . + public async Task USStateGetAllAsync() + { + var __coll = new RefDataNamespace.USStateCollection(); + await DataInvoker.Current.InvokeAsync(this, async () => { _ef.Query(USStateMapper.CreateArgs()).SelectQuery(__coll); await Task.CompletedTask.ConfigureAwait(false); }, BusinessInvokerArgs.TransactionSuppress).ConfigureAwait(false); + return __coll; + } + + /// + /// Gets all the items. + /// + /// The . + public async Task PerformanceOutcomeGetAllAsync() + { + var __coll = new RefDataNamespace.PerformanceOutcomeCollection(); + await DataInvoker.Current.InvokeAsync(this, async () => { _ef.Query(PerformanceOutcomeMapper.CreateArgs()).SelectQuery(__coll); await Task.CompletedTask.ConfigureAwait(false); }, BusinessInvokerArgs.TransactionSuppress).ConfigureAwait(false); + return __coll; + } + + /// + /// Provides the and Entity Framework property mapping. + /// + public static EfDbMapper GenderMapper => EfDbMapper.CreateAuto() + .HasProperty(s => s.Id, d => d.GenderId) + .AddStandardProperties(); + + /// + /// Provides the and Entity Framework property mapping. + /// + public static EfDbMapper TerminationReasonMapper => EfDbMapper.CreateAuto() + .HasProperty(s => s.Id, d => d.TerminationReasonId) + .AddStandardProperties(); + + /// + /// Provides the and Entity Framework property mapping. + /// + public static EfDbMapper RelationshipTypeMapper => EfDbMapper.CreateAuto() + .HasProperty(s => s.Id, d => d.RelationshipTypeId) + .AddStandardProperties(); + + /// + /// Provides the and Entity Framework property mapping. + /// + public static EfDbMapper USStateMapper => EfDbMapper.CreateAuto() + .HasProperty(s => s.Id, d => d.USStateId) + .AddStandardProperties(); + + /// + /// Provides the and Entity Framework property mapping. + /// + public static EfDbMapper PerformanceOutcomeMapper => EfDbMapper.CreateAuto() + .HasProperty(s => s.Id, d => d.PerformanceOutcomeId) + .AddStandardProperties(); + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/Generated/ReferenceDataServiceCollectionExtensions.cs b/samples/My.Hr/My.Hr.Business/Data/Generated/ReferenceDataServiceCollectionExtensions.cs new file mode 100644 index 000000000..106e5c631 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/Generated/ReferenceDataServiceCollectionExtensions.cs @@ -0,0 +1,30 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using Microsoft.Extensions.DependencyInjection; + +namespace My.Hr.Business.Data +{ + /// + /// Provides the generated Data-layer services. + /// + public static class ReferenceDataServiceCollectionsExtension + { + /// + /// Adds the generated Data-layer services. + /// + /// The . + /// The . + public static IServiceCollection AddGeneratedReferenceDataDataServices(this IServiceCollection services) + { + return services.AddTransient(); + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/Generated/ServiceCollectionExtensions.cs b/samples/My.Hr/My.Hr.Business/Data/Generated/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..b9ccdd09c --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/Generated/ServiceCollectionExtensions.cs @@ -0,0 +1,31 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using Microsoft.Extensions.DependencyInjection; + +namespace My.Hr.Business.Data +{ + /// + /// Provides the generated Data-layer services. + /// + public static class ServiceCollectionsExtension + { + /// + /// Adds the generated Data-layer services. + /// + /// The . + /// The . + public static IServiceCollection AddGeneratedDataServices(this IServiceCollection services) + { + return services.AddScoped() + .AddScoped(); + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/Generated/TerminationDetailData.cs b/samples/My.Hr/My.Hr.Business/Data/Generated/TerminationDetailData.cs new file mode 100644 index 000000000..4f6fc54f4 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/Generated/TerminationDetailData.cs @@ -0,0 +1,75 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Business; +using Beef.Data.Database; +using Beef.Data.EntityFrameworkCore; +using Beef.Entities; +using Beef.Mapper; +using Beef.Mapper.Converters; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business.Data +{ + /// + /// Provides the data access. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] + public partial class TerminationDetailData + { + + /// + /// Provides the property and database column mapping. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] + public partial class DbMapper : DatabaseMapper + { + /// + /// Initializes a new instance of the class. + /// + public DbMapper() + { + Property(s => s.Date, "TerminationDate"); + Property(s => s.ReasonSid, "TerminationReasonCode"); + AddStandardProperties(); + DbMapperCtor(); + } + + partial void DbMapperCtor(); // Enables the DbMapper constructor to be extended. + } + + /// + /// Provides the and Entity Framework property mapping. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] + public partial class EfMapper : EfDbMapper + { + /// + /// Initializes a new instance of the class. + /// + public EfMapper() + { + Property(s => s.Date, d => d.TerminationDate); + Property(s => s.ReasonSid, d => d.TerminationReasonCode); + AddStandardProperties(); + EfMapperCtor(); + } + + partial void EfMapperCtor(); // Enables the EfMapper constructor to be extended. + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/HrDb.cs b/samples/My.Hr/My.Hr.Business/Data/HrDb.cs new file mode 100644 index 000000000..4eceddaf1 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/HrDb.cs @@ -0,0 +1,24 @@ +using Beef.Data.Database; +using System.Data.Common; + +namespace My.Hr.Business.Data +{ + /// + /// Represents the My.Hr database. + /// + public class HrDb : DatabaseBase + { + /// + /// Initializes a new instance of the class. + /// + /// The connection string. + /// The optional data provider. + public HrDb(string connectionString, DbProviderFactory? provider = null) : base(connectionString, provider, new SqlRetryDatabaseInvoker()) { } + + /// + /// Set the SQL Session Context when the connection is opened. + /// + /// The . + public override void OnConnectionOpen(DbConnection dbConnection) => SetSqlSessionContext(dbConnection); + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/HrEfDb.cs b/samples/My.Hr/My.Hr.Business/Data/HrEfDb.cs new file mode 100644 index 000000000..ba119ae6e --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/HrEfDb.cs @@ -0,0 +1,16 @@ +using Beef.Data.EntityFrameworkCore; + +namespace My.Hr.Business.Data +{ + /// + /// Represents the My.Hr database using Entity Framework. + /// + public class HrEfDb : EfDbBase + { + /// + /// Initializes a new instance of the class. + /// + /// The entity framework database context. + public HrEfDb(HrEfDbContext dbContext) : base(dbContext) { } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/HrEfDbContext.cs b/samples/My.Hr/My.Hr.Business/Data/HrEfDbContext.cs new file mode 100644 index 000000000..6dcb3dbf7 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/HrEfDbContext.cs @@ -0,0 +1,46 @@ +using Beef.Data.Database; +using My.Hr.Business.Data.EfModel; +using Microsoft.EntityFrameworkCore; + +namespace My.Hr.Business.Data +{ + /// + /// Represents the Entity Framework . + /// + public class HrEfDbContext : DbContext + { + private readonly IDatabase _db; + + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The corresponding . + public HrEfDbContext(DbContextOptions options, IDatabase db) : base(options) => _db = db; + + /// + /// Overrides the to leverage the connection management. + /// + /// The . + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + + // Uses the DB connection management from the database class - ensures DB connection pooling and required DB session context setting. + if (!optionsBuilder.IsConfigured) + optionsBuilder.UseSqlServer(_db.GetConnection()); + } + + /// + /// Overrides the . + /// + /// The . + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + // Add the generated models to the model builder. + modelBuilder.AddGeneratedModels(); + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Data/PerformanceReviewData.cs b/samples/My.Hr/My.Hr.Business/Data/PerformanceReviewData.cs new file mode 100644 index 000000000..f16474ff1 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Data/PerformanceReviewData.cs @@ -0,0 +1,12 @@ +using System.Linq; + +namespace My.Hr.Business.Data +{ + public partial class PerformanceReviewData + { + partial void PerformanceReviewDataCtor() + { + _getByEmployeeIdOnQuery = (q_, employeeId, _) => q_.Where(x => x.EmployeeId == employeeId).OrderByDescending(x => x.Date); + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/DataSvc/Generated/EmployeeDataSvc.cs b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/EmployeeDataSvc.cs new file mode 100644 index 000000000..d6d1d33c8 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/EmployeeDataSvc.cs @@ -0,0 +1,143 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005, IDE0044 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Business; +using Beef.Caching; +using Beef.Entities; +using Beef.Events; +using My.Hr.Business.Data; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business.DataSvc +{ + /// + /// Provides the data repository services. + /// + public partial class EmployeeDataSvc : IEmployeeDataSvc + { + private readonly IEmployeeData _data; + private readonly IEventPublisher _evtPub; + private readonly IRequestCache _cache; + + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + /// The . + public EmployeeDataSvc(IEmployeeData data, IEventPublisher evtPub, IRequestCache cache) + { _data = Check.NotNull(data, nameof(data)); _evtPub = Check.NotNull(evtPub, nameof(evtPub)); _cache = Check.NotNull(cache, nameof(cache)); EmployeeDataSvcCtor(); } + + partial void EmployeeDataSvcCtor(); // Enables additional functionality to be added to the constructor. + + /// + /// Gets the specified . + /// + /// The identifier. + /// The selected where found. + public Task GetAsync(Guid id) + { + return DataSvcInvoker.Current.InvokeAsync(this, async () => + { + var __key = new UniqueKey(id); + if (_cache.TryGetValue(__key, out Employee? __val)) + return __val; + + var __result = await _data.GetAsync(id).ConfigureAwait(false); + _cache.SetValue(__key, __result); + return __result; + }); + } + + /// + /// Creates a new . + /// + /// The . + /// The created . + public Task CreateAsync(Employee value) + { + return DataSvcInvoker.Current.InvokeAsync(this, async () => + { + var __result = await _data.CreateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); + await _evtPub.PublishValueAsync(__result, $"My.Hr.Employee.{__result.Id}", "Created").ConfigureAwait(false); + _cache.SetValue(__result.UniqueKey, __result); + return __result; + }); + } + + /// + /// Updates an existing . + /// + /// The . + /// The updated . + public Task UpdateAsync(Employee value) + { + return DataSvcInvoker.Current.InvokeAsync(this, async () => + { + var __result = await _data.UpdateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); + await _evtPub.PublishValueAsync(__result, $"My.Hr.Employee.{__result.Id}", "Updated").ConfigureAwait(false); + _cache.SetValue(__result.UniqueKey, __result); + return __result; + }); + } + + /// + /// Deletes the specified . + /// + /// The Id. + public Task DeleteAsync(Guid id) + { + return DataSvcInvoker.Current.InvokeAsync(this, async () => + { + await _data.DeleteAsync(id).ConfigureAwait(false); + await _evtPub.PublishAsync($"My.Hr.Employee.{id}", "Deleted", id).ConfigureAwait(false); + _cache.Remove(new UniqueKey(id)); + }); + } + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The Args (see ). + /// The . + /// The . + public Task GetByArgsAsync(EmployeeArgs? args, PagingArgs? paging) + { + return DataSvcInvoker.Current.InvokeAsync(this, async () => + { + var __result = await _data.GetByArgsAsync(args, paging).ConfigureAwait(false); + return __result; + }); + } + + /// + /// Terminates an existing . + /// + /// The . + /// The identifier. + /// The updated . + public Task TerminateAsync(TerminationDetail value, Guid id) + { + return DataSvcInvoker.Current.InvokeAsync(this, async () => + { + var __result = await _data.TerminateAsync(Check.NotNull(value, nameof(value)), id).ConfigureAwait(false); + await _evtPub.PublishValueAsync(__result, $"My.Hr.Employee.{id}", "Terminated", id).ConfigureAwait(false); + _cache.SetValue(__result.UniqueKey, __result); + return __result; + }); + } + } +} + +#pragma warning restore IDE0005, IDE0044 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/DataSvc/Generated/IEmployeeDataSvc.cs b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/IEmployeeDataSvc.cs new file mode 100644 index 000000000..024a4f483 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/IEmployeeDataSvc.cs @@ -0,0 +1,70 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Entities; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business.DataSvc +{ + /// + /// Defines the data repository services. + /// + public partial interface IEmployeeDataSvc + { + /// + /// Gets the specified . + /// + /// The identifier. + /// The selected where found. + Task GetAsync(Guid id); + + /// + /// Creates a new . + /// + /// The . + /// The created . + Task CreateAsync(Employee value); + + /// + /// Updates an existing . + /// + /// The . + /// The updated . + Task UpdateAsync(Employee value); + + /// + /// Deletes the specified . + /// + /// The Id. + Task DeleteAsync(Guid id); + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The Args (see ). + /// The . + /// The . + Task GetByArgsAsync(EmployeeArgs? args, PagingArgs? paging); + + /// + /// Terminates an existing . + /// + /// The . + /// The identifier. + /// The updated . + Task TerminateAsync(TerminationDetail value, Guid id); + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/DataSvc/Generated/IPerformanceReviewDataSvc.cs b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/IPerformanceReviewDataSvc.cs new file mode 100644 index 000000000..d6304b657 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/IPerformanceReviewDataSvc.cs @@ -0,0 +1,62 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Entities; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business.DataSvc +{ + /// + /// Defines the data repository services. + /// + public partial interface IPerformanceReviewDataSvc + { + /// + /// Gets the specified . + /// + /// The identifier. + /// The selected where found. + Task GetAsync(Guid id); + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The . + /// The . + /// The . + Task GetByEmployeeIdAsync(Guid employeeId, PagingArgs? paging); + + /// + /// Creates a new . + /// + /// The . + /// The created . + Task CreateAsync(PerformanceReview value); + + /// + /// Updates an existing . + /// + /// The . + /// The updated . + Task UpdateAsync(PerformanceReview value); + + /// + /// Deletes the specified . + /// + /// The identifier. + Task DeleteAsync(Guid id); + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/IReferenceDataDataSvc_cs.xml b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/IReferenceDataDataSvc.cs similarity index 54% rename from tools/Beef.CodeGen.Core/Templates/IReferenceDataDataSvc_cs.xml rename to samples/My.Hr/My.Hr.Business/DataSvc/Generated/IReferenceDataDataSvc.cs index 1ae3d7df6..e09590c6e 100644 --- a/tools/Beef.CodeGen.Core/Templates/IReferenceDataDataSvc_cs.xml +++ b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/IReferenceDataDataSvc.cs @@ -1,32 +1,20 @@ - - - \ No newline at end of file +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/DataSvc/Generated/PerformanceReviewDataSvc.cs b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/PerformanceReviewDataSvc.cs new file mode 100644 index 000000000..1b7d01098 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/PerformanceReviewDataSvc.cs @@ -0,0 +1,126 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005, IDE0044 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Business; +using Beef.Caching; +using Beef.Entities; +using Beef.Events; +using My.Hr.Business.Data; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business.DataSvc +{ + /// + /// Provides the data repository services. + /// + public partial class PerformanceReviewDataSvc : IPerformanceReviewDataSvc + { + private readonly IPerformanceReviewData _data; + private readonly IEventPublisher _evtPub; + private readonly IRequestCache _cache; + + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + /// The . + public PerformanceReviewDataSvc(IPerformanceReviewData data, IEventPublisher evtPub, IRequestCache cache) + { _data = Check.NotNull(data, nameof(data)); _evtPub = Check.NotNull(evtPub, nameof(evtPub)); _cache = Check.NotNull(cache, nameof(cache)); PerformanceReviewDataSvcCtor(); } + + partial void PerformanceReviewDataSvcCtor(); // Enables additional functionality to be added to the constructor. + + /// + /// Gets the specified . + /// + /// The identifier. + /// The selected where found. + public Task GetAsync(Guid id) + { + return DataSvcInvoker.Current.InvokeAsync(this, async () => + { + var __key = new UniqueKey(id); + if (_cache.TryGetValue(__key, out PerformanceReview? __val)) + return __val; + + var __result = await _data.GetAsync(id).ConfigureAwait(false); + _cache.SetValue(__key, __result); + return __result; + }); + } + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The . + /// The . + /// The . + public Task GetByEmployeeIdAsync(Guid employeeId, PagingArgs? paging) + { + return DataSvcInvoker.Current.InvokeAsync(this, async () => + { + var __result = await _data.GetByEmployeeIdAsync(employeeId, paging).ConfigureAwait(false); + return __result; + }); + } + + /// + /// Creates a new . + /// + /// The . + /// The created . + public Task CreateAsync(PerformanceReview value) + { + return DataSvcInvoker.Current.InvokeAsync(this, async () => + { + var __result = await _data.CreateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); + await _evtPub.PublishValueAsync(__result, $"My.Hr.PerformanceReview.{__result.Id}", "Created").ConfigureAwait(false); + _cache.SetValue(__result.UniqueKey, __result); + return __result; + }); + } + + /// + /// Updates an existing . + /// + /// The . + /// The updated . + public Task UpdateAsync(PerformanceReview value) + { + return DataSvcInvoker.Current.InvokeAsync(this, async () => + { + var __result = await _data.UpdateAsync(Check.NotNull(value, nameof(value))).ConfigureAwait(false); + await _evtPub.PublishValueAsync(__result, $"My.Hr.PerformanceReview.{__result.Id}", "Updated").ConfigureAwait(false); + _cache.SetValue(__result.UniqueKey, __result); + return __result; + }); + } + + /// + /// Deletes the specified . + /// + /// The identifier. + public Task DeleteAsync(Guid id) + { + return DataSvcInvoker.Current.InvokeAsync(this, async () => + { + await _data.DeleteAsync(id).ConfigureAwait(false); + await _evtPub.PublishAsync($"My.Hr.PerformanceReview.{id}", "Deleted", id).ConfigureAwait(false); + _cache.Remove(new UniqueKey(id)); + }); + } + } +} + +#pragma warning restore IDE0005, IDE0044 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/DataSvc/Generated/ReferenceDataDataSvc.cs b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/ReferenceDataDataSvc.cs new file mode 100644 index 000000000..6bd5dd66b --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/ReferenceDataDataSvc.cs @@ -0,0 +1,69 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Data; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Beef; +using Beef.Business; +using Beef.RefData; +using Beef.RefData.Caching; +using My.Hr.Business.Data; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business.DataSvc +{ + /// + /// Provides the ReferenceData data services. + /// + public partial class ReferenceDataDataSvc : IReferenceDataDataSvc + { + private readonly IServiceProvider _provider; + private readonly Dictionary _cacheDict = new Dictionary(); + + /// + /// Initializes a new instance of the class. + /// + /// The . + public ReferenceDataDataSvc(IServiceProvider provider) + { + _provider = Check.NotNull(provider, nameof(provider)); + _cacheDict.Add(typeof(RefDataNamespace.Gender), new ReferenceDataCache(() => DataSvcInvoker.Current.InvokeAsync(typeof(ReferenceDataDataSvc), () => GetDataAsync(data => data.GenderGetAllAsync())))); + _cacheDict.Add(typeof(RefDataNamespace.TerminationReason), new ReferenceDataCache(() => DataSvcInvoker.Current.InvokeAsync(typeof(ReferenceDataDataSvc), () => GetDataAsync(data => data.TerminationReasonGetAllAsync())))); + _cacheDict.Add(typeof(RefDataNamespace.RelationshipType), new ReferenceDataCache(() => DataSvcInvoker.Current.InvokeAsync(typeof(ReferenceDataDataSvc), () => GetDataAsync(data => data.RelationshipTypeGetAllAsync())))); + _cacheDict.Add(typeof(RefDataNamespace.USState), new ReferenceDataCache(() => DataSvcInvoker.Current.InvokeAsync(typeof(ReferenceDataDataSvc), () => GetDataAsync(data => data.USStateGetAllAsync())))); + _cacheDict.Add(typeof(RefDataNamespace.PerformanceOutcome), new ReferenceDataCache(() => DataSvcInvoker.Current.InvokeAsync(typeof(ReferenceDataDataSvc), () => GetDataAsync(data => data.PerformanceOutcomeGetAllAsync())))); + ReferenceDataDataSvcCtor(); + } + + partial void ReferenceDataDataSvcCtor(); // Enables the ReferenceDataDataSvc constructor to be extended. + + /// + /// Gets the data within a new scope; each reference data request needs to occur separately and independently. + /// + private async Task GetDataAsync(Func> func) + { + using var scope = _provider.CreateScope(); + return await func(scope.ServiceProvider.GetService()).ConfigureAwait(false); + } + + /// + /// Gets the for the associated . + /// + /// The type associated + /// A . + public IReferenceDataCollection GetCollection(Type type) => + _cacheDict.TryGetValue(type ?? throw new ArgumentNullException(nameof(type)), out var rdc) ? rdc.GetCollection() : + throw new ArgumentException($"Type {type.Name} does not exist within the ReferenceDataDataSvc cache.", nameof(type)); + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/DataSvc/Generated/ReferenceDataServiceCollectionExtensions.cs b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/ReferenceDataServiceCollectionExtensions.cs new file mode 100644 index 000000000..948ebe9db --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/ReferenceDataServiceCollectionExtensions.cs @@ -0,0 +1,30 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using Microsoft.Extensions.DependencyInjection; + +namespace My.Hr.Business.DataSvc +{ + /// + /// Provides the generated DataSvc-layer services. + /// + public static class ReferenceDataServiceCollectionsExtension + { + /// + /// Adds the generated DataSvc-layer services. + /// + /// The . + /// The . + public static IServiceCollection AddGeneratedReferenceDataDataSvcServices(this IServiceCollection services) + { + return services.AddSingleton(); + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/DataSvc/Generated/ServiceCollectionExtensions.cs b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..dbfb34a6d --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/DataSvc/Generated/ServiceCollectionExtensions.cs @@ -0,0 +1,31 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using Microsoft.Extensions.DependencyInjection; + +namespace My.Hr.Business.DataSvc +{ + /// + /// Provides the generated DataSvc-layer services. + /// + public static class ServiceCollectionsExtension + { + /// + /// Adds the generated DataSvc-layer services. + /// + /// The . + /// The . + public static IServiceCollection AddGeneratedDataSvcServices(this IServiceCollection services) + { + return services.AddScoped() + .AddScoped(); + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Generated/EmployeeManager.cs b/samples/My.Hr/My.Hr.Business/Generated/EmployeeManager.cs new file mode 100644 index 000000000..a08a16bb3 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Generated/EmployeeManager.cs @@ -0,0 +1,147 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Business; +using Beef.Entities; +using Beef.Validation; +using My.Hr.Common.Entities; +using My.Hr.Business.DataSvc; +using My.Hr.Business.Validation; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business +{ + /// + /// Provides the business functionality. + /// + public partial class EmployeeManager : IEmployeeManager + { + private readonly IEmployeeDataSvc _dataService; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public EmployeeManager(IEmployeeDataSvc dataService) + { _dataService = Check.NotNull(dataService, nameof(dataService)); EmployeeManagerCtor(); } + + partial void EmployeeManagerCtor(); // Enables additional functionality to be added to the constructor. + + /// + /// Gets the specified . + /// + /// The identifier. + /// The selected where found. + public Task GetAsync(Guid id) + { + return ManagerInvoker.Current.InvokeAsync(this, async () => + { + ExecutionContext.Current.OperationType = OperationType.Read; + Cleaner.CleanUp(id); + id.Validate(nameof(id)).Mandatory().Run().ThrowOnError(); + return Cleaner.Clean(await _dataService.GetAsync(id).ConfigureAwait(false)); + }); + } + + /// + /// Creates a new . + /// + /// The . + /// The created . + public Task CreateAsync(Employee value) + { + value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); + + return ManagerInvoker.Current.InvokeAsync(this, async () => + { + ExecutionContext.Current.OperationType = OperationType.Create; + Cleaner.CleanUp(value); + value.Validate(nameof(value)).Entity(EmployeeValidator.Default).Run().ThrowOnError(); + return Cleaner.Clean(await _dataService.CreateAsync(value).ConfigureAwait(false)); + }); + } + + /// + /// Updates an existing . + /// + /// The . + /// The identifier. + /// The updated . + public Task UpdateAsync(Employee value, Guid id) + { + value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); + + return ManagerInvoker.Current.InvokeAsync(this, async () => + { + ExecutionContext.Current.OperationType = OperationType.Update; + value.Id = id; + Cleaner.CleanUp(value); + value.Validate(nameof(value)).Entity(EmployeeValidator.Default).Run().ThrowOnError(); + return Cleaner.Clean(await _dataService.UpdateAsync(value).ConfigureAwait(false)); + }); + } + + /// + /// Deletes the specified . + /// + /// The Id. + public Task DeleteAsync(Guid id) + { + return ManagerInvoker.Current.InvokeAsync(this, async () => + { + ExecutionContext.Current.OperationType = OperationType.Delete; + Cleaner.CleanUp(id); + id.Validate(nameof(id)).Mandatory().Common(EmployeeValidator.CanDelete).Run().ThrowOnError(); + await _dataService.DeleteAsync(id).ConfigureAwait(false); + }); + } + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The Args (see ). + /// The . + /// The . + public Task GetByArgsAsync(EmployeeArgs? args, PagingArgs? paging) + { + return ManagerInvoker.Current.InvokeAsync(this, async () => + { + ExecutionContext.Current.OperationType = OperationType.Read; + Cleaner.CleanUp(args); + args.Validate(nameof(args)).Entity(EmployeeArgsValidator.Default).Run().ThrowOnError(); + return Cleaner.Clean(await _dataService.GetByArgsAsync(args, paging).ConfigureAwait(false)); + }); + } + + /// + /// Terminates an existing . + /// + /// The . + /// The identifier. + /// The updated . + public Task TerminateAsync(TerminationDetail value, Guid id) + { + value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); + + return ManagerInvoker.Current.InvokeAsync(this, async () => + { + ExecutionContext.Current.OperationType = OperationType.Update; + Cleaner.CleanUp(value, id); + value.Validate(nameof(value)).Entity(TerminationDetailValidator.Default).Run().ThrowOnError(); + return Cleaner.Clean(await _dataService.TerminateAsync(value, id).ConfigureAwait(false)); + }); + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Generated/IEmployeeManager.cs b/samples/My.Hr/My.Hr.Business/Generated/IEmployeeManager.cs new file mode 100644 index 000000000..d7685be4e --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Generated/IEmployeeManager.cs @@ -0,0 +1,71 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Entities; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business +{ + /// + /// Defines the business functionality. + /// + public partial interface IEmployeeManager + { + /// + /// Gets the specified . + /// + /// The identifier. + /// The selected where found. + Task GetAsync(Guid id); + + /// + /// Creates a new . + /// + /// The . + /// The created . + Task CreateAsync(Employee value); + + /// + /// Updates an existing . + /// + /// The . + /// The identifier. + /// The updated . + Task UpdateAsync(Employee value, Guid id); + + /// + /// Deletes the specified . + /// + /// The Id. + Task DeleteAsync(Guid id); + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The Args (see ). + /// The . + /// The . + Task GetByArgsAsync(EmployeeArgs? args, PagingArgs? paging); + + /// + /// Terminates an existing . + /// + /// The . + /// The identifier. + /// The updated . + Task TerminateAsync(TerminationDetail value, Guid id); + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Generated/IPerformanceReviewManager.cs b/samples/My.Hr/My.Hr.Business/Generated/IPerformanceReviewManager.cs new file mode 100644 index 000000000..fd132584a --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Generated/IPerformanceReviewManager.cs @@ -0,0 +1,64 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Entities; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business +{ + /// + /// Defines the business functionality. + /// + public partial interface IPerformanceReviewManager + { + /// + /// Gets the specified . + /// + /// The identifier. + /// The selected where found. + Task GetAsync(Guid id); + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The . + /// The . + /// The . + Task GetByEmployeeIdAsync(Guid employeeId, PagingArgs? paging); + + /// + /// Creates a new . + /// + /// The . + /// The . + /// The created . + Task CreateAsync(PerformanceReview value, Guid employeeId); + + /// + /// Updates an existing . + /// + /// The . + /// The identifier. + /// The updated . + Task UpdateAsync(PerformanceReview value, Guid id); + + /// + /// Deletes the specified . + /// + /// The identifier. + Task DeleteAsync(Guid id); + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Generated/PerformanceReviewManager.cs b/samples/My.Hr/My.Hr.Business/Generated/PerformanceReviewManager.cs new file mode 100644 index 000000000..c890b9a76 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Generated/PerformanceReviewManager.cs @@ -0,0 +1,129 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Business; +using Beef.Entities; +using Beef.Validation; +using My.Hr.Common.Entities; +using My.Hr.Business.DataSvc; +using My.Hr.Business.Validation; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business +{ + /// + /// Provides the business functionality. + /// + public partial class PerformanceReviewManager : IPerformanceReviewManager + { + private readonly IPerformanceReviewDataSvc _dataService; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public PerformanceReviewManager(IPerformanceReviewDataSvc dataService) + { _dataService = Check.NotNull(dataService, nameof(dataService)); PerformanceReviewManagerCtor(); } + + partial void PerformanceReviewManagerCtor(); // Enables additional functionality to be added to the constructor. + + /// + /// Gets the specified . + /// + /// The identifier. + /// The selected where found. + public Task GetAsync(Guid id) + { + return ManagerInvoker.Current.InvokeAsync(this, async () => + { + ExecutionContext.Current.OperationType = OperationType.Read; + Cleaner.CleanUp(id); + id.Validate(nameof(id)).Mandatory().Run().ThrowOnError(); + return Cleaner.Clean(await _dataService.GetAsync(id).ConfigureAwait(false)); + }); + } + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The . + /// The . + /// The . + public Task GetByEmployeeIdAsync(Guid employeeId, PagingArgs? paging) + { + return ManagerInvoker.Current.InvokeAsync(this, async () => + { + ExecutionContext.Current.OperationType = OperationType.Read; + Cleaner.CleanUp(employeeId); + return Cleaner.Clean(await _dataService.GetByEmployeeIdAsync(employeeId, paging).ConfigureAwait(false)); + }); + } + + /// + /// Creates a new . + /// + /// The . + /// The . + /// The created . + public Task CreateAsync(PerformanceReview value, Guid employeeId) + { + value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); + + return ManagerInvoker.Current.InvokeAsync(this, async () => + { + ExecutionContext.Current.OperationType = OperationType.Create; + value.EmployeeId = employeeId; + Cleaner.CleanUp(value); + value.Validate(nameof(value)).Entity(PerformanceReviewValidator.Default).Run().ThrowOnError(); + return Cleaner.Clean(await _dataService.CreateAsync(value).ConfigureAwait(false)); + }); + } + + /// + /// Updates an existing . + /// + /// The . + /// The identifier. + /// The updated . + public Task UpdateAsync(PerformanceReview value, Guid id) + { + value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); + + return ManagerInvoker.Current.InvokeAsync(this, async () => + { + ExecutionContext.Current.OperationType = OperationType.Update; + value.Id = id; + Cleaner.CleanUp(value); + value.Validate(nameof(value)).Entity(PerformanceReviewValidator.Default).Run().ThrowOnError(); + return Cleaner.Clean(await _dataService.UpdateAsync(value).ConfigureAwait(false)); + }); + } + + /// + /// Deletes the specified . + /// + /// The identifier. + public Task DeleteAsync(Guid id) + { + return ManagerInvoker.Current.InvokeAsync(this, async () => + { + ExecutionContext.Current.OperationType = OperationType.Delete; + Cleaner.CleanUp(id); + id.Validate(nameof(id)).Mandatory().Run().ThrowOnError(); + await _dataService.DeleteAsync(id).ConfigureAwait(false); + }); + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Generated/ReferenceDataProvider.cs b/samples/My.Hr/My.Hr.Business/Generated/ReferenceDataProvider.cs new file mode 100644 index 000000000..d0f16a742 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Generated/ReferenceDataProvider.cs @@ -0,0 +1,104 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Beef; +using Beef.RefData; +using My.Hr.Business.DataSvc; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Business +{ + /// + /// Provides the implementation using the corresponding data services. + /// + public partial class ReferenceDataProvider : RefDataNamespace.ReferenceData + { + private readonly IReferenceDataDataSvc _dataService; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public ReferenceDataProvider(IReferenceDataDataSvc dataService) { _dataService = Check.NotNull(dataService, nameof(dataService)); ReferenceDataProviderCtor(); } + + partial void ReferenceDataProviderCtor(); // Enables the ReferenceDataProvider constructor to be extended. + + #region Collections + + /// + /// Gets the . + /// + public override RefDataNamespace.GenderCollection Gender => (RefDataNamespace.GenderCollection)this[typeof(RefDataNamespace.Gender)]; + + /// + /// Gets the . + /// + public override RefDataNamespace.TerminationReasonCollection TerminationReason => (RefDataNamespace.TerminationReasonCollection)this[typeof(RefDataNamespace.TerminationReason)]; + + /// + /// Gets the . + /// + public override RefDataNamespace.RelationshipTypeCollection RelationshipType => (RefDataNamespace.RelationshipTypeCollection)this[typeof(RefDataNamespace.RelationshipType)]; + + /// + /// Gets the . + /// + public override RefDataNamespace.USStateCollection USState => (RefDataNamespace.USStateCollection)this[typeof(RefDataNamespace.USState)]; + + /// + /// Gets the . + /// + public override RefDataNamespace.PerformanceOutcomeCollection PerformanceOutcome => (RefDataNamespace.PerformanceOutcomeCollection)this[typeof(RefDataNamespace.PerformanceOutcome)]; + + #endregion + + /// + /// Gets the for the associated . + /// + /// The . + /// A . + public override IReferenceDataCollection this[Type type] => _dataService.GetCollection(type); + + /// + /// Prefetches all, or the list of objects, where not already cached or expired. + /// + /// The list of names; otherwise, null for all. + public override Task PrefetchAsync(params string[] names) + { + var types = new List(); + if (names == null) + { + types.AddRange(GetAllTypes()); + } + else + { + foreach (string name in names.Distinct()) + { + switch (name) + { + case var n when string.Compare(n, nameof(RefDataNamespace.Gender), StringComparison.InvariantCultureIgnoreCase) == 0: types.Add(typeof(RefDataNamespace.Gender)); break; + case var n when string.Compare(n, nameof(RefDataNamespace.TerminationReason), StringComparison.InvariantCultureIgnoreCase) == 0: types.Add(typeof(RefDataNamespace.TerminationReason)); break; + case var n when string.Compare(n, nameof(RefDataNamespace.RelationshipType), StringComparison.InvariantCultureIgnoreCase) == 0: types.Add(typeof(RefDataNamespace.RelationshipType)); break; + case var n when string.Compare(n, nameof(RefDataNamespace.USState), StringComparison.InvariantCultureIgnoreCase) == 0: types.Add(typeof(RefDataNamespace.USState)); break; + case var n when string.Compare(n, nameof(RefDataNamespace.PerformanceOutcome), StringComparison.InvariantCultureIgnoreCase) == 0: types.Add(typeof(RefDataNamespace.PerformanceOutcome)); break; + } + } + } + + Parallel.ForEach(types, (type, _) => { var __ = this[type]; }); + return Task.CompletedTask; + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Generated/ReferenceDataServiceCollectionExtensions.cs b/samples/My.Hr/My.Hr.Business/Generated/ReferenceDataServiceCollectionExtensions.cs new file mode 100644 index 000000000..f2971bd6f --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Generated/ReferenceDataServiceCollectionExtensions.cs @@ -0,0 +1,31 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using Microsoft.Extensions.DependencyInjection; +using My.Hr.Common.Entities; + +namespace My.Hr.Business +{ + /// + /// Provides the generated Manager-layer services. + /// + public static class ReferenceDataServiceCollectionsExtension + { + /// + /// Adds the generated Manager-layer services. + /// + /// The . + /// The . + public static IServiceCollection AddGeneratedReferenceDataManagerServices(this IServiceCollection services) + { + return services.AddSingleton(); + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Generated/ServiceCollectionExtensions.cs b/samples/My.Hr/My.Hr.Business/Generated/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..e99df837c --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Generated/ServiceCollectionExtensions.cs @@ -0,0 +1,31 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using Microsoft.Extensions.DependencyInjection; + +namespace My.Hr.Business +{ + /// + /// Provides the generated Manager-layer services. + /// + public static class ServiceCollectionsExtension + { + /// + /// Adds the generated Manager-layer services. + /// + /// The . + /// The . + public static IServiceCollection AddGeneratedManagerServices(this IServiceCollection services) + { + return services.AddScoped() + .AddScoped(); + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/My.Hr.Business.csproj b/samples/My.Hr/My.Hr.Business/My.Hr.Business.csproj new file mode 100644 index 000000000..9c4338df6 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/My.Hr.Business.csproj @@ -0,0 +1,14 @@ + + + netcoreapp3.1 + enable + + + + + + + + + + \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Validation/CommonValidators.cs b/samples/My.Hr/My.Hr.Business/Validation/CommonValidators.cs new file mode 100644 index 000000000..08f78dff3 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Validation/CommonValidators.cs @@ -0,0 +1,33 @@ +using Beef.Validation; +using System.ComponentModel.DataAnnotations; + +namespace My.Hr.Business.Validation +{ + /// + /// Provides common validator capabilities. + /// + public static class CommonValidators + { + private static readonly EmailAddressAttribute _emailValidator = new EmailAddressAttribute(); + + /// + /// Provides a common person's name validator, ensure max length is 100. + /// + public static CommonValidator PersonName = CommonValidator.Create(cv => cv.String(100)); + + /// + /// Provides a common address's street validator, ensure max length is 100. + /// + public static CommonValidator Street = CommonValidator.Create(cv => cv.String(100)); + + /// + /// Provides a common email validator, ensure max length is 250, is all lowercase, and use validator. + /// + public static CommonValidator Email = CommonValidator.Create(cv => cv.String(250).Override(v => v.Value!.ToLowerInvariant()).Must(v => _emailValidator.IsValid(v.Value))); + + /// + /// Provides a common phone number validator, just length, but could be regex or other. + /// + public static CommonValidator PhoneNo = CommonValidator.Create(cv => cv.String(50)); + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Validation/EmployeeArgsValidator.cs b/samples/My.Hr/My.Hr.Business/Validation/EmployeeArgsValidator.cs new file mode 100644 index 000000000..1003fa064 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Validation/EmployeeArgsValidator.cs @@ -0,0 +1,22 @@ +using Beef.Validation; +using My.Hr.Common.Entities; + +namespace My.Hr.Business.Validation +{ + /// + /// Represents a validator. + /// + public class EmployeeArgsValidator : Validator + { + /// + /// Initializes a new instance of the class. + /// + public EmployeeArgsValidator() + { + Property(x => x.FirstName).Common(CommonValidators.PersonName).Wildcard(); + Property(x => x.LastName).Common(CommonValidators.PersonName).Wildcard(); + Property(x => x.Genders).AreValid(); + Property(x => x.StartFrom).CompareProperty(CompareOperator.LessThanEqual, x => x.StartTo); + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Validation/EmployeeValidator.cs b/samples/My.Hr/My.Hr.Business/Validation/EmployeeValidator.cs new file mode 100644 index 000000000..6be47ab70 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Validation/EmployeeValidator.cs @@ -0,0 +1,87 @@ +using Beef; +using Beef.Validation; +using Beef.Validation.Rules; +using My.Hr.Business.DataSvc; +using My.Hr.Common.Entities; +using System; +using System.Text.RegularExpressions; + +namespace My.Hr.Business.Validation +{ + /// + /// Represents a validator. + /// + public class EmployeeValidator : Validator + { + // Address validator implemented using fluent-style method-chaining. + private static readonly Validator
_addressValidator = Validator.Create
() + .HasProperty(x => x.Street1, p => p.Mandatory().Common(CommonValidators.Street)) + .HasProperty(x => x.Street2, p => p.Common(CommonValidators.Street)) + .HasProperty(x => x.City, p => p.Mandatory().String(50)) + .HasProperty(x => x.State, p => p.Mandatory().IsValid()) + .HasProperty(x => x.PostCode, p => p.Mandatory().String(new Regex(@"^\d{5}(?:[-\s]\d{4})?$"))); + + // Emergency Contact validator implemented using fluent-style method-chaining. + public static readonly Validator _emergencyContactValidator = Validator.Create() + .HasProperty(x => x.FirstName, p => p.Mandatory().Common(CommonValidators.PersonName)) + .HasProperty(x => x.LastName, p => p.Mandatory().Common(CommonValidators.PersonName)) + .HasProperty(x => x.PhoneNo, p => p.Mandatory().Common(CommonValidators.PhoneNo)) + .HasProperty(x => x.Relationship, p => p.Mandatory().IsValid()); + + /// + /// Initializes a new instance of the class. + /// + public EmployeeValidator() + { + Property(x => x.Email).Mandatory().Common(CommonValidators.Email); + Property(x => x.FirstName).Mandatory().Common(CommonValidators.PersonName); + Property(x => x.LastName).Mandatory().Common(CommonValidators.PersonName); + Property(x => x.Gender).Mandatory().IsValid(); + Property(x => x.Birthday).Mandatory().CompareValue(CompareOperator.LessThanEqual, _ => DateTime.UtcNow.AddYears(-18), errorText: "Birthday is invalid as the Employee must be at least 18 years of age."); + Property(x => x.StartDate).Mandatory().CompareValue(CompareOperator.GreaterThanEqual, new DateTime(1999, 01, 01, 0, 0, 0, DateTimeKind.Utc), "January 1, 1999"); + Property(x => x.PhoneNo).Mandatory().Common(CommonValidators.PhoneNo); + Property(x => x.Address).Entity(_addressValidator); + Property(x => x.EmergencyContacts).Collection(maxCount: 5, item: new CollectionRuleItem(_emergencyContactValidator)); + } + + /// + /// Add further validation logic non-property bound. + /// + protected override void OnValidate(ValidationContext context) + { + base.OnValidate(context); + + // Ensure that the termination data is always null on an update; unless already terminated then it can no longer be updated. + switch (ExecutionContext.Current.OperationType) + { + case OperationType.Create: + context.Value.Termination = null; + break; + + case OperationType.Update: + var existing = context.GetService().GetAsync(context.Value.Id).GetAwaiter().GetResult(); + if (existing == null) + throw new NotFoundException(); + + if (existing.Termination != null) + throw new ValidationException("Once an Employee has been Terminated the data can no longer be updated."); + + context.Value.Termination = null; + break; + } + } + + /// + /// Common validator that will be referenced by the Delete operation to ensure that the employee can indeed be deleted. + /// + public static CommonValidator CanDelete = CommonValidator.Create(cv => cv.Custom(context => + { + var existing = context.GetService().GetAsync(context.Value).GetAwaiter().GetResult(); + if (existing == null) + throw new NotFoundException(); + + if (existing.StartDate <= DateTime.Now) + throw new ValidationException("An employee cannot be deleted after they have started their employment."); + })); + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Validation/PerformanceReviewValidator.cs b/samples/My.Hr/My.Hr.Business/Validation/PerformanceReviewValidator.cs new file mode 100644 index 000000000..7767fcf35 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Validation/PerformanceReviewValidator.cs @@ -0,0 +1,66 @@ +using Beef; +using Beef.Entities; +using Beef.Validation; +using My.Hr.Common.Entities; +using System; + +namespace My.Hr.Business.Validation +{ + /// + /// Represents a validator. + /// + public class PerformanceReviewValidator : Validator + { + /// + /// Initializes a new instance of the class. + /// + public PerformanceReviewValidator() + { + Property(x => x.EmployeeId).Mandatory(); + Property(x => x.Date).Mandatory().CompareValue(CompareOperator.LessThanEqual, _ => Cleaner.Clean(DateTime.Now), _ => "today"); + Property(x => x.Notes).String(4000); + Property(x => x.Reviewer).Mandatory().String(256); + Property(x => x.Outcome).Mandatory().IsValid(); + } + + /// + /// Add further validation logic. + /// + protected override void OnValidate(ValidationContext context) + { + if (!context.HasError(x => x.EmployeeId)) + { + // Ensure that the EmployeeId has not be changed (change back) as it is immutable. + if (ExecutionContext.Current.OperationType == OperationType.Update) + { + var prm = (IPerformanceReviewManager)context.ServiceProvider.GetService(typeof(IPerformanceReviewManager)); + var prv = prm.GetAsync(context.Value.Id).GetAwaiter().GetResult(); + if (prv == null) + throw new NotFoundException(); + + if (context.Value.EmployeeId != prv.EmployeeId) + { + context.AddError(x => x.EmployeeId, ValidatorStrings.ImmutableFormat); + return; + } + } + + // Check that the referenced Employee exists, and the review data is within the bounds of their employment. + var em = (IEmployeeManager)context.ServiceProvider.GetService(typeof(IEmployeeManager)); + var ev = em.GetAsync(context.Value.EmployeeId).GetAwaiter().GetResult(); + if (ev == null) + context.AddError(x => x.EmployeeId, ValidatorStrings.ExistsFormat); + else + { + if (!context.HasError(x => x.Date)) + { + if (context.Value.Date < ev.StartDate) + context.AddError(x => x.Date, "{0} must not be prior to the Employee starting."); + else if (ev.Termination != null && context.Value.Date > ev.Termination.Date) + context.AddError(x => x.Date, "{0} must not be after the Employee has terminated."); + } + } + } + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Business/Validation/TerminationDetailValidator.cs b/samples/My.Hr/My.Hr.Business/Validation/TerminationDetailValidator.cs new file mode 100644 index 000000000..c8a43a409 --- /dev/null +++ b/samples/My.Hr/My.Hr.Business/Validation/TerminationDetailValidator.cs @@ -0,0 +1,20 @@ +using Beef.Validation; +using My.Hr.Common.Entities; + +namespace My.Hr.Business.Validation +{ + /// + /// Represents a validator. + /// + public class TerminationDetailValidator : Validator + { + /// + /// Initializes a new instance of the class. + /// + public TerminationDetailValidator() + { + Property(x => x.Date).Mandatory(); + Property(x => x.Reason).Mandatory().IsValid(); + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.CodeGen/My.Hr.CodeGen.csproj b/samples/My.Hr/My.Hr.CodeGen/My.Hr.CodeGen.csproj new file mode 100644 index 000000000..7a3fde014 --- /dev/null +++ b/samples/My.Hr/My.Hr.CodeGen/My.Hr.CodeGen.csproj @@ -0,0 +1,10 @@ + + + Exe + netcoreapp3.1 + enable + + + + + \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.CodeGen/My.Hr.xml b/samples/My.Hr/My.Hr.CodeGen/My.Hr.xml new file mode 100644 index 000000000..53ef5bacf --- /dev/null +++ b/samples/My.Hr/My.Hr.CodeGen/My.Hr.xml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.CodeGen/My.RefData.xml b/samples/My.Hr/My.Hr.CodeGen/My.RefData.xml new file mode 100644 index 000000000..1cf8d46c6 --- /dev/null +++ b/samples/My.Hr/My.Hr.CodeGen/My.RefData.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.CodeGen/Program.cs b/samples/My.Hr/My.Hr.CodeGen/Program.cs new file mode 100644 index 000000000..008e6b44b --- /dev/null +++ b/samples/My.Hr/My.Hr.CodeGen/Program.cs @@ -0,0 +1,21 @@ +using Beef.CodeGen; +using System.Threading.Tasks; + +namespace My.Hr.CodeGen +{ + /// + /// Represents the code generation program (capability). + /// + public static class Program + { + /// + /// Main startup. + /// + /// The startup arguments. + /// The status code whereby zero indicates success. + public static Task Main(string[] args) + { + return CodeGenConsoleWrapper.Create("My", "Hr").Supports(entity: true, refData: true).RunAsync(args); + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.CodeGen/Properties/launchSettings.json b/samples/My.Hr/My.Hr.CodeGen/Properties/launchSettings.json new file mode 100644 index 000000000..582a141d2 --- /dev/null +++ b/samples/My.Hr/My.Hr.CodeGen/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "My.Hr.CodeGen": { + "commandName": "Project", + "commandLineArgs": "entity" + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Agents/Generated/EmployeeAgent.cs b/samples/My.Hr/My.Hr.Common/Agents/Generated/EmployeeAgent.cs new file mode 100644 index 000000000..f79776495 --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Agents/Generated/EmployeeAgent.cs @@ -0,0 +1,177 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Beef.Entities; +using Beef.WebApi; +using Newtonsoft.Json.Linq; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Agents +{ + /// + /// Defines the Web API agent. + /// + public partial interface IEmployeeAgent + { + /// + /// Gets the specified . + /// + /// The identifier. + /// The optional . + /// A . + Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null); + + /// + /// Creates a new . + /// + /// The . + /// The optional . + /// A . + Task> CreateAsync(Employee value, WebApiRequestOptions? requestOptions = null); + + /// + /// Updates an existing . + /// + /// The . + /// The identifier. + /// The optional . + /// A . + Task> UpdateAsync(Employee value, Guid id, WebApiRequestOptions? requestOptions = null); + + /// + /// Patches an existing . + /// + /// The . + /// The that contains the patch content for the . + /// The identifier. + /// The optional . + /// A . + Task> PatchAsync(WebApiPatchOption patchOption, JToken value, Guid id, WebApiRequestOptions? requestOptions = null); + + /// + /// Deletes the specified . + /// + /// The Id. + /// The optional . + /// A . + Task DeleteAsync(Guid id, WebApiRequestOptions? requestOptions = null); + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The Args (see ). + /// The . + /// The optional . + /// A . + Task> GetByArgsAsync(EmployeeArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null); + + /// + /// Terminates an existing . + /// + /// The . + /// The identifier. + /// The optional . + /// A . + Task> TerminateAsync(TerminationDetail value, Guid id, WebApiRequestOptions? requestOptions = null); + } + + /// + /// Provides the Web API agent. + /// + public partial class EmployeeAgent : WebApiAgentBase, IEmployeeAgent + { + /// + /// Initializes a new instance of the class. + /// + /// The . + public EmployeeAgent(IWebApiAgentArgs args) : base(args) { } + + /// + /// Gets the specified . + /// + /// The identifier. + /// The optional . + /// A . + public Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/employees/{id}", requestOptions: requestOptions, + args: new WebApiArg[] { new WebApiArg("id", id) }); + + /// + /// Creates a new . + /// + /// The . + /// The optional . + /// A . + public Task> CreateAsync(Employee value, WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/employees", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, + args: Array.Empty()); + + /// + /// Updates an existing . + /// + /// The . + /// The identifier. + /// The optional . + /// A . + public Task> UpdateAsync(Employee value, Guid id, WebApiRequestOptions? requestOptions = null) => + PutAsync("api/v1/employees/{id}", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, + args: new WebApiArg[] { new WebApiArg("id", id) }); + + /// + /// Patches an existing . + /// + /// The . + /// The that contains the patch content for the . + /// The identifier. + /// The optional . + /// A . + public Task> PatchAsync(WebApiPatchOption patchOption, JToken value, Guid id, WebApiRequestOptions? requestOptions = null) => + PatchAsync("api/v1/employees/{id}", patchOption, Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, + args: new WebApiArg[] { new WebApiArg("id", id) }); + + /// + /// Deletes the specified . + /// + /// The Id. + /// The optional . + /// A . + public Task DeleteAsync(Guid id, WebApiRequestOptions? requestOptions = null) => + DeleteAsync("api/v1/employees/{id}", requestOptions: requestOptions, + args: new WebApiArg[] { new WebApiArg("id", id) }); + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The Args (see ). + /// The . + /// The optional . + /// A . + public Task> GetByArgsAsync(EmployeeArgs? args, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) => + GetCollectionResultAsync("api/v1/employees", requestOptions: requestOptions, + args: new WebApiArg[] { new WebApiArg("args", args, WebApiArgType.FromUriUseProperties), new WebApiPagingArgsArg("paging", paging) }); + + /// + /// Terminates an existing . + /// + /// The . + /// The identifier. + /// The optional . + /// A . + public Task> TerminateAsync(TerminationDetail value, Guid id, WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/employees/{id}/terminate", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, + args: new WebApiArg[] { new WebApiArg("id", id) }); + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Agents/Generated/PerformanceReviewAgent.cs b/samples/My.Hr/My.Hr.Common/Agents/Generated/PerformanceReviewAgent.cs new file mode 100644 index 000000000..779171bbe --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Agents/Generated/PerformanceReviewAgent.cs @@ -0,0 +1,159 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Beef.Entities; +using Beef.WebApi; +using Newtonsoft.Json.Linq; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Agents +{ + /// + /// Defines the Web API agent. + /// + public partial interface IPerformanceReviewAgent + { + /// + /// Gets the specified . + /// + /// The identifier. + /// The optional . + /// A . + Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null); + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The . + /// The . + /// The optional . + /// A . + Task> GetByEmployeeIdAsync(Guid employeeId, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null); + + /// + /// Creates a new . + /// + /// The . + /// The . + /// The optional . + /// A . + Task> CreateAsync(PerformanceReview value, Guid employeeId, WebApiRequestOptions? requestOptions = null); + + /// + /// Updates an existing . + /// + /// The . + /// The identifier. + /// The optional . + /// A . + Task> UpdateAsync(PerformanceReview value, Guid id, WebApiRequestOptions? requestOptions = null); + + /// + /// Patches an existing . + /// + /// The . + /// The that contains the patch content for the . + /// The identifier. + /// The optional . + /// A . + Task> PatchAsync(WebApiPatchOption patchOption, JToken value, Guid id, WebApiRequestOptions? requestOptions = null); + + /// + /// Deletes the specified . + /// + /// The identifier. + /// The optional . + /// A . + Task DeleteAsync(Guid id, WebApiRequestOptions? requestOptions = null); + } + + /// + /// Provides the Web API agent. + /// + public partial class PerformanceReviewAgent : WebApiAgentBase, IPerformanceReviewAgent + { + /// + /// Initializes a new instance of the class. + /// + /// The . + public PerformanceReviewAgent(IWebApiAgentArgs args) : base(args) { } + + /// + /// Gets the specified . + /// + /// The identifier. + /// The optional . + /// A . + public Task> GetAsync(Guid id, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/reviews/{id}", requestOptions: requestOptions, + args: new WebApiArg[] { new WebApiArg("id", id) }); + + /// + /// Gets the that contains the items that match the selection criteria. + /// + /// The . + /// The . + /// The optional . + /// A . + public Task> GetByEmployeeIdAsync(Guid employeeId, PagingArgs? paging = null, WebApiRequestOptions? requestOptions = null) => + GetCollectionResultAsync("api/v1/employees/{employeeId}/reviews", requestOptions: requestOptions, + args: new WebApiArg[] { new WebApiArg("employeeId", employeeId), new WebApiPagingArgsArg("paging", paging) }); + + /// + /// Creates a new . + /// + /// The . + /// The . + /// The optional . + /// A . + public Task> CreateAsync(PerformanceReview value, Guid employeeId, WebApiRequestOptions? requestOptions = null) => + PostAsync("api/v1/employees/{employeeId}/reviews", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, + args: new WebApiArg[] { new WebApiArg("employeeId", employeeId) }); + + /// + /// Updates an existing . + /// + /// The . + /// The identifier. + /// The optional . + /// A . + public Task> UpdateAsync(PerformanceReview value, Guid id, WebApiRequestOptions? requestOptions = null) => + PutAsync("api/v1/reviews/{id}", Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, + args: new WebApiArg[] { new WebApiArg("id", id) }); + + /// + /// Patches an existing . + /// + /// The . + /// The that contains the patch content for the . + /// The identifier. + /// The optional . + /// A . + public Task> PatchAsync(WebApiPatchOption patchOption, JToken value, Guid id, WebApiRequestOptions? requestOptions = null) => + PatchAsync("api/v1/reviews/{id}", patchOption, Beef.Check.NotNull(value, nameof(value)), requestOptions: requestOptions, + args: new WebApiArg[] { new WebApiArg("id", id) }); + + /// + /// Deletes the specified . + /// + /// The identifier. + /// The optional . + /// A . + public Task DeleteAsync(Guid id, WebApiRequestOptions? requestOptions = null) => + DeleteAsync("api/v1/reviews/{id}", requestOptions: requestOptions, + args: new WebApiArg[] { new WebApiArg("id", id) }); + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Agents/Generated/ReferenceDataAgent.cs b/samples/My.Hr/My.Hr.Common/Agents/Generated/ReferenceDataAgent.cs new file mode 100644 index 000000000..449b513c8 --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Agents/Generated/ReferenceDataAgent.cs @@ -0,0 +1,150 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Beef.RefData; +using Beef.WebApi; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Agents +{ + /// + /// Defines the ReferenceData Web API agent. + /// + public partial interface IReferenceDataAgent + { + /// + /// Gets all of the items that match the filter arguments. + /// + /// The optional arguments. + /// The optional . + /// A . + Task> GenderGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); + + /// + /// Gets all of the items that match the filter arguments. + /// + /// The optional arguments. + /// The optional . + /// A . + Task> TerminationReasonGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); + + /// + /// Gets all of the items that match the filter arguments. + /// + /// The optional arguments. + /// The optional . + /// A . + Task> RelationshipTypeGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); + + /// + /// Gets all of the items that match the filter arguments. + /// + /// The optional arguments. + /// The optional . + /// A . + Task> USStateGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); + + /// + /// Gets all of the items that match the filter arguments. + /// + /// The optional arguments. + /// The optional . + /// A . + Task> PerformanceOutcomeGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null); + + /// + /// Gets the reference data entries for the specified entities and codes from the query string; e.g: api/v1/ref?entity=codeX,codeY&entity2=codeZ&entity3 + /// + /// The optional list of reference data names. + /// The optional . + /// A . + /// The reference data objects will need to be manually extracted from the corresponding response content. + Task GetNamedAsync(string[] names, WebApiRequestOptions? requestOptions = null); + } + + /// + /// Provides the ReferenceData Web API agent. + /// + public partial class ReferenceDataAgent : WebApiAgentBase, IReferenceDataAgent + { + /// + /// Initializes a new instance of the class. + /// + /// The . + public ReferenceDataAgent(IWebApiAgentArgs args) : base(args) { } + + /// + /// Gets all of the items that match the filter arguments. + /// + /// The optional arguments. + /// The optional . + /// A . + public Task> GenderGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/ref/genders", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); + + /// + /// Gets all of the items that match the filter arguments. + /// + /// The optional arguments. + /// The optional . + /// A . + public Task> TerminationReasonGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/ref/terminationReasons", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); + + /// + /// Gets all of the items that match the filter arguments. + /// + /// The optional arguments. + /// The optional . + /// A . + public Task> RelationshipTypeGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/ref/relationshipTypes", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); + + /// + /// Gets all of the items that match the filter arguments. + /// + /// The optional arguments. + /// The optional . + /// A . + public Task> USStateGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/ref/usStates", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); + + /// + /// Gets all of the items that match the filter arguments. + /// + /// The optional arguments. + /// The optional . + /// A . + public Task> PerformanceOutcomeGetAllAsync(ReferenceDataFilter? args = null, WebApiRequestOptions? requestOptions = null) => + GetAsync("api/v1/ref/performanceOutcomes", requestOptions: requestOptions, args: new WebApiArg[] { new WebApiArg("args", args!, WebApiArgType.FromUriUseProperties) }); + + /// + /// Gets the reference data entries for the specified entities and codes from the query string; e.g: api/v1/ref?entity=codeX,codeY&entity2=codeZ&entity3 + /// + /// The optional list of reference data names. + /// The optional . + /// A . + /// The reference data objects will need to be manually extracted from the corresponding response content. + public Task GetNamedAsync(string[] names, WebApiRequestOptions? requestOptions = null) + { + var ro = requestOptions ?? new WebApiRequestOptions(); + if (names != null) + ro.UrlQueryString += string.Join("&", names); + + return GetAsync("api/v1/ref", requestOptions: ro); + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Agents/Generated/ReferenceDataAgentProvider.cs b/samples/My.Hr/My.Hr.Common/Agents/Generated/ReferenceDataAgentProvider.cs new file mode 100644 index 000000000..7777fed46 --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Agents/Generated/ReferenceDataAgentProvider.cs @@ -0,0 +1,172 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Beef.RefData; +using Beef.RefData.Caching; +using Beef.WebApi; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using My.Hr.Common.Entities; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Agents +{ + /// + /// Provides the implementation using the corresponding Web API agent. + /// + public partial class ReferenceDataAgentProvider : RefDataNamespace.ReferenceData + { + private readonly Dictionary _nameDict = new Dictionary(); + private readonly Dictionary _typeDict = new Dictionary(); + private readonly IReferenceDataAgent _agent; + private readonly Dictionary _cacheDict = new Dictionary(); + + #region Ctor + + /// + /// Initializes a new instance of the class. + /// + /// The . + public ReferenceDataAgentProvider(IReferenceDataAgent agent) + { + _agent = Beef.Check.NotNull(agent, nameof(agent)); + + _nameDict.Add(nameof(Gender), typeof(RefDataNamespace.Gender)); + _typeDict.Add(typeof(RefDataNamespace.Gender), nameof(Gender)); + _cacheDict.Add(typeof(RefDataNamespace.Gender), new ReferenceDataCache(() => _agent.GenderGetAllAsync().ContinueWith((t) => t.Result.Value, TaskScheduler.Current))); + + _nameDict.Add(nameof(TerminationReason), typeof(RefDataNamespace.TerminationReason)); + _typeDict.Add(typeof(RefDataNamespace.TerminationReason), nameof(TerminationReason)); + _cacheDict.Add(typeof(RefDataNamespace.TerminationReason), new ReferenceDataCache(() => _agent.TerminationReasonGetAllAsync().ContinueWith((t) => t.Result.Value, TaskScheduler.Current))); + + _nameDict.Add(nameof(RelationshipType), typeof(RefDataNamespace.RelationshipType)); + _typeDict.Add(typeof(RefDataNamespace.RelationshipType), nameof(RelationshipType)); + _cacheDict.Add(typeof(RefDataNamespace.RelationshipType), new ReferenceDataCache(() => _agent.RelationshipTypeGetAllAsync().ContinueWith((t) => t.Result.Value, TaskScheduler.Current))); + + _nameDict.Add(nameof(USState), typeof(RefDataNamespace.USState)); + _typeDict.Add(typeof(RefDataNamespace.USState), nameof(USState)); + _cacheDict.Add(typeof(RefDataNamespace.USState), new ReferenceDataCache(() => _agent.USStateGetAllAsync().ContinueWith((t) => t.Result.Value, TaskScheduler.Current))); + + _nameDict.Add(nameof(PerformanceOutcome), typeof(RefDataNamespace.PerformanceOutcome)); + _typeDict.Add(typeof(RefDataNamespace.PerformanceOutcome), nameof(PerformanceOutcome)); + _cacheDict.Add(typeof(RefDataNamespace.PerformanceOutcome), new ReferenceDataCache(() => _agent.PerformanceOutcomeGetAllAsync().ContinueWith((t) => t.Result.Value, TaskScheduler.Current))); + + ReferenceDataAgentProviderCtor(); + } + + partial void ReferenceDataAgentProviderCtor(); // Enables the ReferenceDataAgentProvider constructor to be extended. + + #endregion + + #region Collections + + /// + /// Gets the . + /// + /// The . + public override RefDataNamespace.GenderCollection Gender => (RefDataNamespace.GenderCollection)this[typeof(RefDataNamespace.Gender)]; + + /// + /// Gets the . + /// + /// The . + public override RefDataNamespace.TerminationReasonCollection TerminationReason => (RefDataNamespace.TerminationReasonCollection)this[typeof(RefDataNamespace.TerminationReason)]; + + /// + /// Gets the . + /// + /// The . + public override RefDataNamespace.RelationshipTypeCollection RelationshipType => (RefDataNamespace.RelationshipTypeCollection)this[typeof(RefDataNamespace.RelationshipType)]; + + /// + /// Gets the . + /// + /// The . + public override RefDataNamespace.USStateCollection USState => (RefDataNamespace.USStateCollection)this[typeof(RefDataNamespace.USState)]; + + /// + /// Gets the . + /// + /// The . + public override RefDataNamespace.PerformanceOutcomeCollection PerformanceOutcome => (RefDataNamespace.PerformanceOutcomeCollection)this[typeof(RefDataNamespace.PerformanceOutcome)]; + + #endregion + + #region This/GetCache/PrefetchAsync + + /// + /// Gets the for the associated . + /// + /// The . + /// The . + public override IReferenceDataCollection this[Type type] => GetCache(type).GetCollection(); + + /// + /// Gets the for the associated . + /// + /// The . + /// The . + public IReferenceDataCache GetCache(Type type) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + + if (!_cacheDict.ContainsKey(type)) + throw new ArgumentException($"Type {type.Name} does not exist within the ReferenceDataProvider cache."); + + return (IReferenceDataCache)_cacheDict[type]; + } + + /// + /// Prefetches all of the named objects where not already cached or have expired. + /// + /// The list of type names. + public override async Task PrefetchAsync(params string[] names) + { + if (names == null || names.Length == 0) + return; + + var getNames = new List(); + foreach (string name in names.Distinct()) + { + if (_nameDict.ContainsKey(name) && GetCache(_nameDict[name]).IsExpired) + getNames.Add(name); + } + + if (getNames.Count == 0) + return; + + var result = await _agent.GetNamedAsync(getNames.ToArray()).ConfigureAwait(false); + foreach (var rdj in JObject.Parse("{ \"content\": " + result.Content ?? "[ ]" + " }")["content"]!.Children()) + { + var name = rdj["name"]?.Value(); + var items = rdj["items"]?.ToString(); + if (name != null) + { + switch (name) + { + case nameof(Gender): GetCache(_nameDict[nameof(Gender)]).SetCollection(JsonConvert.DeserializeObject(items!)); break; + case nameof(TerminationReason): GetCache(_nameDict[nameof(TerminationReason)]).SetCollection(JsonConvert.DeserializeObject(items!)); break; + case nameof(RelationshipType): GetCache(_nameDict[nameof(RelationshipType)]).SetCollection(JsonConvert.DeserializeObject(items!)); break; + case nameof(USState): GetCache(_nameDict[nameof(USState)]).SetCollection(JsonConvert.DeserializeObject(items!)); break; + case nameof(PerformanceOutcome): GetCache(_nameDict[nameof(PerformanceOutcome)]).SetCollection(JsonConvert.DeserializeObject(items!)); break; + } + } + } + } + + #endregion + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Entities/Generated/Address.cs b/samples/My.Hr/My.Hr.Common/Entities/Generated/Address.cs new file mode 100644 index 000000000..99c893864 --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Entities/Generated/Address.cs @@ -0,0 +1,271 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData; +using Newtonsoft.Json; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Entities +{ + /// + /// Represents the Address entity. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public partial class Address : EntityBase, IEquatable
+ { + #region Privates + + private string? _street1; + private string? _street2; + private string? _city; + private string? _stateSid; + private string? _stateText; + private string? _postCode; + + #endregion + + #region Properties + + /// + /// Gets or sets the Street1. + /// + [JsonProperty("street1", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Street1")] + public string? Street1 + { + get => _street1; + set => SetValue(ref _street1, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Street1)); + } + + /// + /// Gets or sets the Street2. + /// + [JsonProperty("street2", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Street2")] + public string? Street2 + { + get => _street2; + set => SetValue(ref _street2, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Street2)); + } + + /// + /// Gets or sets the City. + /// + [JsonProperty("city", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="City")] + public string? City + { + get => _city; + set => SetValue(ref _city, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(City)); + } + + /// + /// Gets or sets the using the underlying Serialization Identifier (SID). + /// + [JsonProperty("state", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="State")] + public string? StateSid + { + get => _stateSid; + set => SetValue(ref _stateSid, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(State)); + } + + /// + /// Gets the corresponding {{State}} text (read-only where selected). + /// + [JsonProperty("stateText", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? StateText { get => _stateText ?? GetRefDataText(() => State); set => _stateText = value; } + + /// + /// Gets or sets the State (see ). + /// + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + [Display(Name="State")] + public RefDataNamespace.USState? State + { + get => _stateSid; + set => SetValue(ref _stateSid, value, false, false, nameof(State)); + } + + /// + /// Gets or sets the Post Code. + /// + [JsonProperty("postCode", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Post Code")] + public string? PostCode + { + get => _postCode; + set => SetValue(ref _postCode, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(PostCode)); + } + + #endregion + + #region IEquatable + + /// + /// Determines whether the specified object is equal to the current object by comparing the values of all the properties. + /// + /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? obj) => obj is Address val && Equals(val); + + /// + /// Determines whether the specified is equal to the current by comparing the values of all the properties. + /// + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(Address? value) + { + if (value == null) + return false; + else if (ReferenceEquals(value, this)) + return true; + + return base.Equals((object)value) + && Equals(Street1, value.Street1) + && Equals(Street2, value.Street2) + && Equals(City, value.City) + && Equals(StateSid, value.StateSid) + && Equals(PostCode, value.PostCode); + } + + /// + /// Compares two types for equality. + /// + /// A. + /// B. + /// true indicates equal; otherwise, false for not equal. + public static bool operator == (Address? a, Address? b) => Equals(a, b); + + /// + /// Compares two types for non-equality. + /// + /// A. + /// B. + /// true indicates not equal; otherwise, false for equal. + public static bool operator != (Address? a, Address? b) => !Equals(a, b); + + /// + /// Returns the hash code for the . + /// + /// The hash code for the . + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(Street1); + hash.Add(Street2); + hash.Add(City); + hash.Add(StateSid); + hash.Add(PostCode); + return base.GetHashCode() ^ hash.ToHashCode(); + } + + #endregion + + #region ICopyFrom + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public override void CopyFrom(object from) + { + var fval = ValidateCopyFromType
(from); + CopyFrom(fval); + } + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public void CopyFrom(Address from) + { + if (from == null) + throw new ArgumentNullException(nameof(from)); + + CopyFrom((EntityBase)from); + Street1 = from.Street1; + Street2 = from.Street2; + City = from.City; + StateSid = from.StateSid; + PostCode = from.PostCode; + + OnAfterCopyFrom(from); + } + + #endregion + + #region ICloneable + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new Address(); + clone.CopyFrom(this); + return clone; + } + + #endregion + + #region ICleanUp + + /// + /// Performs a clean-up of the resetting property values as appropriate to ensure a basic level of data consistency. + /// + public override void CleanUp() + { + base.CleanUp(); + Street1 = Cleaner.Clean(Street1, StringTrim.UseDefault, StringTransform.UseDefault); + Street2 = Cleaner.Clean(Street2, StringTrim.UseDefault, StringTransform.UseDefault); + City = Cleaner.Clean(City, StringTrim.UseDefault, StringTransform.UseDefault); + StateSid = Cleaner.Clean(StateSid); + PostCode = Cleaner.Clean(PostCode, StringTrim.UseDefault, StringTransform.UseDefault); + + OnAfterCleanUp(); + } + + /// + /// Indicates whether considered initial; i.e. all properties have their initial value. + /// + /// true indicates is initial; otherwise, false. + public override bool IsInitial + { + get + { + return Cleaner.IsInitial(Street1) + && Cleaner.IsInitial(Street2) + && Cleaner.IsInitial(City) + && Cleaner.IsInitial(StateSid) + && Cleaner.IsInitial(PostCode); + } + } + + #endregion + + #region PartialMethods + + partial void OnAfterCleanUp(); + + partial void OnAfterCopyFrom(Address from); + + #endregion + } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Entities/Generated/EmergencyContact.cs b/samples/My.Hr/My.Hr.Common/Entities/Generated/EmergencyContact.cs new file mode 100644 index 000000000..d827815dc --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Entities/Generated/EmergencyContact.cs @@ -0,0 +1,334 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData; +using Newtonsoft.Json; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Entities +{ + /// + /// Represents the Emergency Contact entity. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public partial class EmergencyContact : EntityBase, IGuidIdentifier, IEquatable + { + #region Privates + + private Guid _id; + private string? _firstName; + private string? _lastName; + private string? _phoneNo; + private string? _relationshipSid; + private string? _relationshipText; + + #endregion + + #region Properties + + /// + /// Gets or sets the Id. + /// + [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Include)] + [Display(Name="Identifier")] + public Guid Id + { + get => _id; + set => SetValue(ref _id, value, false, false, nameof(Id)); + } + + /// + /// Gets or sets the First Name. + /// + [JsonProperty("firstName", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="First Name")] + public string? FirstName + { + get => _firstName; + set => SetValue(ref _firstName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(FirstName)); + } + + /// + /// Gets or sets the Last Name. + /// + [JsonProperty("lastName", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Last Name")] + public string? LastName + { + get => _lastName; + set => SetValue(ref _lastName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(LastName)); + } + + /// + /// Gets or sets the Phone No. + /// + [JsonProperty("phoneNo", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Phone No")] + public string? PhoneNo + { + get => _phoneNo; + set => SetValue(ref _phoneNo, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(PhoneNo)); + } + + /// + /// Gets or sets the using the underlying Serialization Identifier (SID). + /// + [JsonProperty("relationship", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Relationship")] + public string? RelationshipSid + { + get => _relationshipSid; + set => SetValue(ref _relationshipSid, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Relationship)); + } + + /// + /// Gets the corresponding {{Relationship}} text (read-only where selected). + /// + [JsonProperty("relationshipText", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? RelationshipText { get => _relationshipText ?? GetRefDataText(() => Relationship); set => _relationshipText = value; } + + /// + /// Gets or sets the Relationship (see ). + /// + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + [Display(Name="Relationship")] + public RefDataNamespace.RelationshipType? Relationship + { + get => _relationshipSid; + set => SetValue(ref _relationshipSid, value, false, false, nameof(Relationship)); + } + + #endregion + + #region IUniqueKey + + /// + /// Indicates whether the has a value. + /// + public override bool HasUniqueKey => true; + + /// + /// Gets the list of property names that represent the unique key. + /// + public override string[] UniqueKeyProperties => new string[] { nameof(Id) }; + + /// + /// Creates the . + /// + /// The . + /// The . + public static UniqueKey CreateUniqueKey(Guid id) => new UniqueKey(id); + + /// + /// Gets the (consists of the following property(s): ). + /// + public override UniqueKey UniqueKey => new UniqueKey(Id); + + #endregion + + #region IEquatable + + /// + /// Determines whether the specified object is equal to the current object by comparing the values of all the properties. + /// + /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? obj) => obj is EmergencyContact val && Equals(val); + + /// + /// Determines whether the specified is equal to the current by comparing the values of all the properties. + /// + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(EmergencyContact? value) + { + if (value == null) + return false; + else if (ReferenceEquals(value, this)) + return true; + + return base.Equals((object)value) + && Equals(Id, value.Id) + && Equals(FirstName, value.FirstName) + && Equals(LastName, value.LastName) + && Equals(PhoneNo, value.PhoneNo) + && Equals(RelationshipSid, value.RelationshipSid); + } + + /// + /// Compares two types for equality. + /// + /// A. + /// B. + /// true indicates equal; otherwise, false for not equal. + public static bool operator == (EmergencyContact? a, EmergencyContact? b) => Equals(a, b); + + /// + /// Compares two types for non-equality. + /// + /// A. + /// B. + /// true indicates not equal; otherwise, false for equal. + public static bool operator != (EmergencyContact? a, EmergencyContact? b) => !Equals(a, b); + + /// + /// Returns the hash code for the . + /// + /// The hash code for the . + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(Id); + hash.Add(FirstName); + hash.Add(LastName); + hash.Add(PhoneNo); + hash.Add(RelationshipSid); + return base.GetHashCode() ^ hash.ToHashCode(); + } + + #endregion + + #region ICopyFrom + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public override void CopyFrom(object from) + { + var fval = ValidateCopyFromType(from); + CopyFrom(fval); + } + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public void CopyFrom(EmergencyContact from) + { + if (from == null) + throw new ArgumentNullException(nameof(from)); + + CopyFrom((EntityBase)from); + Id = from.Id; + FirstName = from.FirstName; + LastName = from.LastName; + PhoneNo = from.PhoneNo; + RelationshipSid = from.RelationshipSid; + + OnAfterCopyFrom(from); + } + + #endregion + + #region ICloneable + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new EmergencyContact(); + clone.CopyFrom(this); + return clone; + } + + #endregion + + #region ICleanUp + + /// + /// Performs a clean-up of the resetting property values as appropriate to ensure a basic level of data consistency. + /// + public override void CleanUp() + { + base.CleanUp(); + Id = Cleaner.Clean(Id); + FirstName = Cleaner.Clean(FirstName, StringTrim.UseDefault, StringTransform.UseDefault); + LastName = Cleaner.Clean(LastName, StringTrim.UseDefault, StringTransform.UseDefault); + PhoneNo = Cleaner.Clean(PhoneNo, StringTrim.UseDefault, StringTransform.UseDefault); + RelationshipSid = Cleaner.Clean(RelationshipSid); + + OnAfterCleanUp(); + } + + /// + /// Indicates whether considered initial; i.e. all properties have their initial value. + /// + /// true indicates is initial; otherwise, false. + public override bool IsInitial + { + get + { + return Cleaner.IsInitial(Id) + && Cleaner.IsInitial(FirstName) + && Cleaner.IsInitial(LastName) + && Cleaner.IsInitial(PhoneNo) + && Cleaner.IsInitial(RelationshipSid); + } + } + + #endregion + + #region PartialMethods + + partial void OnAfterCleanUp(); + + partial void OnAfterCopyFrom(EmergencyContact from); + + #endregion + } + + #region Collection + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class EmergencyContactCollection : EntityBaseCollection + { + /// + /// Initializes a new instance of the class. + /// + public EmergencyContactCollection() { } + + /// + /// Initializes a new instance of the class with an entities range. + /// + /// The entities. + public EmergencyContactCollection(IEnumerable entities) => AddRange(entities); + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new EmergencyContactCollection(); + foreach (var item in this) + { + clone.Add((EmergencyContact)item.Clone()); + } + + return clone; + } + } + + #endregion +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Entities/Generated/Employee.cs b/samples/My.Hr/My.Hr.Common/Entities/Generated/Employee.cs new file mode 100644 index 000000000..e58b862e3 --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Entities/Generated/Employee.cs @@ -0,0 +1,228 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData; +using Newtonsoft.Json; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Entities +{ + /// + /// Represents the Employee entity. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public partial class Employee : EmployeeBase, IEquatable + { + #region Privates + + private Address? _address; + private EmergencyContactCollection? _emergencyContacts; + + #endregion + + #region Properties + + /// + /// Gets or sets the Address (see ). + /// + [JsonProperty("address", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Address")] + public Address? Address + { + get => _address; + set => SetValue(ref _address, value, false, true, nameof(Address)); + } + + /// + /// Gets or sets the Emergency Contacts. + /// + [JsonProperty("emergencyContacts", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Emergency Contacts")] + public EmergencyContactCollection? EmergencyContacts + { + get => _emergencyContacts; + set => SetValue(ref _emergencyContacts, value, false, false, nameof(EmergencyContacts)); + } + + #endregion + + #region IChangeTracking + + /// + /// Resets the entity state to unchanged by accepting the changes (resets ). + /// + /// Ends and commits the entity changes (see ). + public override void AcceptChanges() + { + Address?.AcceptChanges(); + base.AcceptChanges(); + } + + /// + /// Determines that until is invoked property changes are to be logged (see ). + /// + public override void TrackChanges() + { + Address?.TrackChanges(); + base.TrackChanges(); + } + + #endregion + + #region IEquatable + + /// + /// Determines whether the specified object is equal to the current object by comparing the values of all the properties. + /// + /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? obj) => obj is Employee val && Equals(val); + + /// + /// Determines whether the specified is equal to the current by comparing the values of all the properties. + /// + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(Employee? value) + { + if (value == null) + return false; + else if (ReferenceEquals(value, this)) + return true; + + return base.Equals((object)value) + && Equals(Address, value.Address) + && Equals(EmergencyContacts, value.EmergencyContacts); + } + + /// + /// Compares two types for equality. + /// + /// A. + /// B. + /// true indicates equal; otherwise, false for not equal. + public static bool operator == (Employee? a, Employee? b) => Equals(a, b); + + /// + /// Compares two types for non-equality. + /// + /// A. + /// B. + /// true indicates not equal; otherwise, false for equal. + public static bool operator != (Employee? a, Employee? b) => !Equals(a, b); + + /// + /// Returns the hash code for the . + /// + /// The hash code for the . + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(Address); + hash.Add(EmergencyContacts); + return base.GetHashCode() ^ hash.ToHashCode(); + } + + #endregion + + #region ICopyFrom + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public override void CopyFrom(object from) + { + var fval = ValidateCopyFromType(from); + CopyFrom(fval); + } + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public void CopyFrom(Employee from) + { + if (from == null) + throw new ArgumentNullException(nameof(from)); + + CopyFrom((EmployeeBase)from); + Address = CopyOrClone(from.Address, Address); + EmergencyContacts = from.EmergencyContacts; + + OnAfterCopyFrom(from); + } + + #endregion + + #region ICloneable + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new Employee(); + clone.CopyFrom(this); + return clone; + } + + #endregion + + #region ICleanUp + + /// + /// Performs a clean-up of the resetting property values as appropriate to ensure a basic level of data consistency. + /// + public override void CleanUp() + { + base.CleanUp(); + Address = Cleaner.Clean(Address); + EmergencyContacts = Cleaner.Clean(EmergencyContacts); + + OnAfterCleanUp(); + } + + /// + /// Indicates whether considered initial; i.e. all properties have their initial value. + /// + /// true indicates is initial; otherwise, false. + public override bool IsInitial + { + get + { + if (!base.IsInitial) + return false; + + return Cleaner.IsInitial(Address) + && Cleaner.IsInitial(EmergencyContacts); + } + } + + #endregion + + #region PartialMethods + + partial void OnAfterCleanUp(); + + partial void OnAfterCopyFrom(Employee from); + + #endregion + } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Entities/Generated/EmployeeArgs.cs b/samples/My.Hr/My.Hr.Common/Entities/Generated/EmployeeArgs.cs new file mode 100644 index 000000000..205990cfb --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Entities/Generated/EmployeeArgs.cs @@ -0,0 +1,281 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData; +using Newtonsoft.Json; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Entities +{ + /// + /// Represents the search arguments entity. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public partial class EmployeeArgs : EntityBase, IEquatable + { + #region Privates + + private string? _firstName; + private string? _lastName; + private List? _gendersSids; + private DateTime? _startFrom; + private DateTime? _startTo; + private bool? _isIncludeTerminated; + + #endregion + + #region Properties + + /// + /// Gets or sets the First Name. + /// + [JsonProperty("firstName", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="First Name")] + public string? FirstName + { + get => _firstName; + set => SetValue(ref _firstName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(FirstName)); + } + + /// + /// Gets or sets the Last Name. + /// + [JsonProperty("lastName", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Last Name")] + public string? LastName + { + get => _lastName; + set => SetValue(ref _lastName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(LastName)); + } + + /// + /// Gets or sets the list using the underlying Serialization Identifier (SID). + /// + [JsonProperty("genders", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Genders")] + public List? GendersSids + { + get => _gendersSids; + set => SetValue(ref _gendersSids, value, false, false, nameof(Genders)); + } + + /// + /// Gets or sets the Genders (see ). + /// + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + [Display(Name="Genders")] + public ReferenceDataSidList? Genders + { + get => new ReferenceDataSidList(ref _gendersSids); + set => SetValue(ref _gendersSids, value?.ToSidList(), false, false, nameof(Genders)); + } + + /// + /// Gets or sets the Start From. + /// + [JsonProperty("startFrom", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Start From")] + public DateTime? StartFrom + { + get => _startFrom; + set => SetValue(ref _startFrom, value, false, DateTimeTransform.DateOnly, nameof(StartFrom)); + } + + /// + /// Gets or sets the Start To. + /// + [JsonProperty("startTo", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Start To")] + public DateTime? StartTo + { + get => _startTo; + set => SetValue(ref _startTo, value, false, DateTimeTransform.DateOnly, nameof(StartTo)); + } + + /// + /// Indicates whether Is Include Terminated. + /// + [JsonProperty("includeTerminated", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Is Include Terminated")] + public bool? IsIncludeTerminated + { + get => _isIncludeTerminated; + set => SetValue(ref _isIncludeTerminated, value, false, false, nameof(IsIncludeTerminated)); + } + + #endregion + + #region IEquatable + + /// + /// Determines whether the specified object is equal to the current object by comparing the values of all the properties. + /// + /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? obj) => obj is EmployeeArgs val && Equals(val); + + /// + /// Determines whether the specified is equal to the current by comparing the values of all the properties. + /// + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(EmployeeArgs? value) + { + if (value == null) + return false; + else if (ReferenceEquals(value, this)) + return true; + + return base.Equals((object)value) + && Equals(FirstName, value.FirstName) + && Equals(LastName, value.LastName) + && Equals(GendersSids, value.GendersSids) + && Equals(StartFrom, value.StartFrom) + && Equals(StartTo, value.StartTo) + && Equals(IsIncludeTerminated, value.IsIncludeTerminated); + } + + /// + /// Compares two types for equality. + /// + /// A. + /// B. + /// true indicates equal; otherwise, false for not equal. + public static bool operator == (EmployeeArgs? a, EmployeeArgs? b) => Equals(a, b); + + /// + /// Compares two types for non-equality. + /// + /// A. + /// B. + /// true indicates not equal; otherwise, false for equal. + public static bool operator != (EmployeeArgs? a, EmployeeArgs? b) => !Equals(a, b); + + /// + /// Returns the hash code for the . + /// + /// The hash code for the . + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(FirstName); + hash.Add(LastName); + hash.Add(GendersSids); + hash.Add(StartFrom); + hash.Add(StartTo); + hash.Add(IsIncludeTerminated); + return base.GetHashCode() ^ hash.ToHashCode(); + } + + #endregion + + #region ICopyFrom + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public override void CopyFrom(object from) + { + var fval = ValidateCopyFromType(from); + CopyFrom(fval); + } + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public void CopyFrom(EmployeeArgs from) + { + if (from == null) + throw new ArgumentNullException(nameof(from)); + + CopyFrom((EntityBase)from); + FirstName = from.FirstName; + LastName = from.LastName; + GendersSids = from.GendersSids; + StartFrom = from.StartFrom; + StartTo = from.StartTo; + IsIncludeTerminated = from.IsIncludeTerminated; + + OnAfterCopyFrom(from); + } + + #endregion + + #region ICloneable + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new EmployeeArgs(); + clone.CopyFrom(this); + return clone; + } + + #endregion + + #region ICleanUp + + /// + /// Performs a clean-up of the resetting property values as appropriate to ensure a basic level of data consistency. + /// + public override void CleanUp() + { + base.CleanUp(); + FirstName = Cleaner.Clean(FirstName, StringTrim.UseDefault, StringTransform.UseDefault); + LastName = Cleaner.Clean(LastName, StringTrim.UseDefault, StringTransform.UseDefault); + GendersSids = Cleaner.Clean(GendersSids); + StartFrom = Cleaner.Clean(StartFrom, DateTimeTransform.DateOnly); + StartTo = Cleaner.Clean(StartTo, DateTimeTransform.DateOnly); + IsIncludeTerminated = Cleaner.Clean(IsIncludeTerminated); + + OnAfterCleanUp(); + } + + /// + /// Indicates whether considered initial; i.e. all properties have their initial value. + /// + /// true indicates is initial; otherwise, false. + public override bool IsInitial + { + get + { + return Cleaner.IsInitial(FirstName) + && Cleaner.IsInitial(LastName) + && Cleaner.IsInitial(GendersSids) + && Cleaner.IsInitial(StartFrom) + && Cleaner.IsInitial(StartTo) + && Cleaner.IsInitial(IsIncludeTerminated); + } + } + + #endregion + + #region PartialMethods + + partial void OnAfterCleanUp(); + + partial void OnAfterCopyFrom(EmployeeArgs from); + + #endregion + } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Entities/Generated/EmployeeBase.cs b/samples/My.Hr/My.Hr.Common/Entities/Generated/EmployeeBase.cs new file mode 100644 index 000000000..cc999e087 --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Entities/Generated/EmployeeBase.cs @@ -0,0 +1,509 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData; +using Newtonsoft.Json; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Entities +{ + /// + /// Represents the base entity. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public partial class EmployeeBase : EntityBase, IGuidIdentifier, IETag, IChangeLog, IEquatable + { + #region Privates + + private Guid _id; + private string? _email; + private string? _firstName; + private string? _lastName; + private string? _genderSid; + private string? _genderText; + private DateTime _birthday; + private DateTime _startDate; + private TerminationDetail? _termination; + private string? _phoneNo; + private string? _eTag; + private ChangeLog? _changeLog; + + #endregion + + #region Properties + + /// + /// Gets or sets the identifier. + /// + [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Include)] + [Display(Name="Identifier")] + public Guid Id + { + get => _id; + set => SetValue(ref _id, value, false, false, nameof(Id)); + } + + /// + /// Gets or sets the Unique Email. + /// + [JsonProperty("email", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Email")] + public string? Email + { + get => _email; + set => SetValue(ref _email, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Email)); + } + + /// + /// Gets or sets the First Name. + /// + [JsonProperty("firstName", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="First Name")] + public string? FirstName + { + get => _firstName; + set => SetValue(ref _firstName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(FirstName)); + } + + /// + /// Gets or sets the Last Name. + /// + [JsonProperty("lastName", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Last Name")] + public string? LastName + { + get => _lastName; + set => SetValue(ref _lastName, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(LastName)); + } + + /// + /// Gets or sets the using the underlying Serialization Identifier (SID). + /// + [JsonProperty("gender", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Gender")] + public string? GenderSid + { + get => _genderSid; + set => SetValue(ref _genderSid, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Gender)); + } + + /// + /// Gets the corresponding {{Gender}} text (read-only where selected). + /// + [JsonProperty("genderText", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? GenderText { get => _genderText ?? GetRefDataText(() => Gender); set => _genderText = value; } + + /// + /// Gets or sets the Gender (see ). + /// + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + [Display(Name="Gender")] + public RefDataNamespace.Gender? Gender + { + get => _genderSid; + set => SetValue(ref _genderSid, value, false, false, nameof(Gender)); + } + + /// + /// Gets or sets the Birthday. + /// + [JsonProperty("birthday", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Birthday")] + public DateTime Birthday + { + get => _birthday; + set => SetValue(ref _birthday, value, false, DateTimeTransform.DateOnly, nameof(Birthday)); + } + + /// + /// Gets or sets the Start Date. + /// + [JsonProperty("startDate", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Start Date")] + public DateTime StartDate + { + get => _startDate; + set => SetValue(ref _startDate, value, false, DateTimeTransform.DateOnly, nameof(StartDate)); + } + + /// + /// Gets or sets the Termination (see ). + /// + [JsonProperty("termination", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Termination")] + public TerminationDetail? Termination + { + get => _termination; + set => SetValue(ref _termination, value, false, true, nameof(Termination)); + } + + /// + /// Gets or sets the Phone No. + /// + [JsonProperty("phoneNo", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Phone No")] + public string? PhoneNo + { + get => _phoneNo; + set => SetValue(ref _phoneNo, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(PhoneNo)); + } + + /// + /// Gets or sets the ETag. + /// + [JsonProperty("etag", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="ETag")] + public string? ETag + { + get => _eTag; + set => SetValue(ref _eTag, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ETag)); + } + + /// + /// Gets or sets the Change Log (see ). + /// + [JsonProperty("changeLog", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Change Log")] + public ChangeLog? ChangeLog + { + get => _changeLog; + set => SetValue(ref _changeLog, value, false, true, nameof(ChangeLog)); + } + + #endregion + + #region IChangeTracking + + /// + /// Resets the entity state to unchanged by accepting the changes (resets ). + /// + /// Ends and commits the entity changes (see ). + public override void AcceptChanges() + { + Termination?.AcceptChanges(); + ChangeLog?.AcceptChanges(); + base.AcceptChanges(); + } + + /// + /// Determines that until is invoked property changes are to be logged (see ). + /// + public override void TrackChanges() + { + Termination?.TrackChanges(); + ChangeLog?.TrackChanges(); + base.TrackChanges(); + } + + #endregion + + #region IUniqueKey + + /// + /// Indicates whether the has a value. + /// + public override bool HasUniqueKey => true; + + /// + /// Gets the list of property names that represent the unique key. + /// + public override string[] UniqueKeyProperties => new string[] { nameof(Id) }; + + /// + /// Creates the . + /// + /// The . + /// The . + public static UniqueKey CreateUniqueKey(Guid id) => new UniqueKey(id); + + /// + /// Gets the (consists of the following property(s): ). + /// + public override UniqueKey UniqueKey => new UniqueKey(Id); + + #endregion + + #region IEquatable + + /// + /// Determines whether the specified object is equal to the current object by comparing the values of all the properties. + /// + /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? obj) => obj is EmployeeBase val && Equals(val); + + /// + /// Determines whether the specified is equal to the current by comparing the values of all the properties. + /// + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(EmployeeBase? value) + { + if (value == null) + return false; + else if (ReferenceEquals(value, this)) + return true; + + return base.Equals((object)value) + && Equals(Id, value.Id) + && Equals(Email, value.Email) + && Equals(FirstName, value.FirstName) + && Equals(LastName, value.LastName) + && Equals(GenderSid, value.GenderSid) + && Equals(Birthday, value.Birthday) + && Equals(StartDate, value.StartDate) + && Equals(Termination, value.Termination) + && Equals(PhoneNo, value.PhoneNo) + && Equals(ETag, value.ETag) + && Equals(ChangeLog, value.ChangeLog); + } + + /// + /// Compares two types for equality. + /// + /// A. + /// B. + /// true indicates equal; otherwise, false for not equal. + public static bool operator == (EmployeeBase? a, EmployeeBase? b) => Equals(a, b); + + /// + /// Compares two types for non-equality. + /// + /// A. + /// B. + /// true indicates not equal; otherwise, false for equal. + public static bool operator != (EmployeeBase? a, EmployeeBase? b) => !Equals(a, b); + + /// + /// Returns the hash code for the . + /// + /// The hash code for the . + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(Id); + hash.Add(Email); + hash.Add(FirstName); + hash.Add(LastName); + hash.Add(GenderSid); + hash.Add(Birthday); + hash.Add(StartDate); + hash.Add(Termination); + hash.Add(PhoneNo); + hash.Add(ETag); + hash.Add(ChangeLog); + return base.GetHashCode() ^ hash.ToHashCode(); + } + + #endregion + + #region ICopyFrom + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public override void CopyFrom(object from) + { + var fval = ValidateCopyFromType(from); + CopyFrom(fval); + } + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public void CopyFrom(EmployeeBase from) + { + if (from == null) + throw new ArgumentNullException(nameof(from)); + + CopyFrom((EntityBase)from); + Id = from.Id; + Email = from.Email; + FirstName = from.FirstName; + LastName = from.LastName; + GenderSid = from.GenderSid; + Birthday = from.Birthday; + StartDate = from.StartDate; + Termination = CopyOrClone(from.Termination, Termination); + PhoneNo = from.PhoneNo; + ETag = from.ETag; + ChangeLog = CopyOrClone(from.ChangeLog, ChangeLog); + + OnAfterCopyFrom(from); + } + + #endregion + + #region ICloneable + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new EmployeeBase(); + clone.CopyFrom(this); + return clone; + } + + #endregion + + #region ICleanUp + + /// + /// Performs a clean-up of the resetting property values as appropriate to ensure a basic level of data consistency. + /// + public override void CleanUp() + { + base.CleanUp(); + Id = Cleaner.Clean(Id); + Email = Cleaner.Clean(Email, StringTrim.UseDefault, StringTransform.UseDefault); + FirstName = Cleaner.Clean(FirstName, StringTrim.UseDefault, StringTransform.UseDefault); + LastName = Cleaner.Clean(LastName, StringTrim.UseDefault, StringTransform.UseDefault); + GenderSid = Cleaner.Clean(GenderSid); + Birthday = Cleaner.Clean(Birthday, DateTimeTransform.DateOnly); + StartDate = Cleaner.Clean(StartDate, DateTimeTransform.DateOnly); + Termination = Cleaner.Clean(Termination); + PhoneNo = Cleaner.Clean(PhoneNo, StringTrim.UseDefault, StringTransform.UseDefault); + ETag = Cleaner.Clean(ETag, StringTrim.UseDefault, StringTransform.UseDefault); + ChangeLog = Cleaner.Clean(ChangeLog); + + OnAfterCleanUp(); + } + + /// + /// Indicates whether considered initial; i.e. all properties have their initial value. + /// + /// true indicates is initial; otherwise, false. + public override bool IsInitial + { + get + { + return Cleaner.IsInitial(Id) + && Cleaner.IsInitial(Email) + && Cleaner.IsInitial(FirstName) + && Cleaner.IsInitial(LastName) + && Cleaner.IsInitial(GenderSid) + && Cleaner.IsInitial(Birthday) + && Cleaner.IsInitial(StartDate) + && Cleaner.IsInitial(Termination) + && Cleaner.IsInitial(PhoneNo) + && Cleaner.IsInitial(ETag) + && Cleaner.IsInitial(ChangeLog); + } + } + + #endregion + + #region PartialMethods + + partial void OnAfterCleanUp(); + + partial void OnAfterCopyFrom(EmployeeBase from); + + #endregion + } + + #region Collection + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class EmployeeBaseCollection : EntityBaseCollection + { + /// + /// Initializes a new instance of the class. + /// + public EmployeeBaseCollection() { } + + /// + /// Initializes a new instance of the class with an entities range. + /// + /// The entities. + public EmployeeBaseCollection(IEnumerable entities) => AddRange(entities); + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new EmployeeBaseCollection(); + foreach (var item in this) + { + clone.Add((EmployeeBase)item.Clone()); + } + + return clone; + } + + /// + /// An implicit cast from the to a corresponding . + /// + /// The . + /// The corresponding . + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator EmployeeBaseCollection(EmployeeBaseCollectionResult result) => result?.Result!; + } + + #endregion + + #region CollectionResult + + /// + /// Represents the collection result. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public class EmployeeBaseCollectionResult : EntityCollectionResult + { + /// + /// Initializes a new instance of the class. + /// + public EmployeeBaseCollectionResult() { } + + /// + /// Initializes a new instance of the class with . + /// + /// The . + public EmployeeBaseCollectionResult(PagingArgs? paging) : base(paging) { } + + /// + /// Initializes a new instance of the class with a of items to add. + /// + /// A collection containing items to add. + /// The . + public EmployeeBaseCollectionResult(IEnumerable collection, PagingArgs? paging = null) : base(paging) => Result.AddRange(collection); + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new EmployeeBaseCollectionResult(); + clone.CopyFrom(this); + return clone; + } + } + + #endregion +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Entities/Generated/Gender.cs b/samples/My.Hr/My.Hr.Common/Entities/Generated/Gender.cs new file mode 100644 index 000000000..72b9275c9 --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Entities/Generated/Gender.cs @@ -0,0 +1,154 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData; +using Newtonsoft.Json; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Entities +{ + /// + /// Represents the Gender entity. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + [ReferenceDataInterface(typeof(IReferenceData))] + public partial class Gender : ReferenceDataBaseGuid + { + #region Operator + + /// + /// An implicit cast from an Id to a . + /// + /// The Id. + /// The corresponding . + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator Gender(Guid id) => ConvertFromId(id); + + /// + /// An implicit cast from a Code to a . + /// + /// The Code. + /// The corresponding . + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator Gender(string? code) => ConvertFromCode(code); + + #endregion + + #region ICopyFrom + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public override void CopyFrom(object from) + { + var fval = ValidateCopyFromType(from); + CopyFrom(fval); + } + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public void CopyFrom(Gender from) + { + if (from == null) + throw new ArgumentNullException(nameof(from)); + + CopyFrom((ReferenceDataBaseGuid)from); + + OnAfterCopyFrom(from); + } + + #endregion + + #region ICloneable + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new Gender(); + clone.CopyFrom(this); + return clone; + } + + #endregion + + #region ICleanUp + + /// + /// Performs a clean-up of the resetting property values as appropriate to ensure a basic level of data consistency. + /// + public override void CleanUp() + { + base.CleanUp(); + + OnAfterCleanUp(); + } + + /// + /// Indicates whether considered initial; i.e. all properties have their initial value. + /// + /// true indicates is initial; otherwise, false. + public override bool IsInitial + { + get + { + if (!base.IsInitial) + return false; + + return true; + } + } + + #endregion + + #region PartialMethods + + partial void OnAfterCleanUp(); + + partial void OnAfterCopyFrom(Gender from); + + #endregion + } + + #region Collection + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class GenderCollection : ReferenceDataCollectionBase + { + /// + /// Initializes a new instance of the class. + /// + public GenderCollection() { } + + /// + /// Initializes a new instance of the class with an entities range. + /// + /// The entities. + public GenderCollection(IEnumerable entities) => AddRange(entities); + } + + #endregion +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Entities/Generated/IReferenceData.cs b/samples/My.Hr/My.Hr.Common/Entities/Generated/IReferenceData.cs new file mode 100644 index 000000000..30794b570 --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Entities/Generated/IReferenceData.cs @@ -0,0 +1,51 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using Beef.RefData; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Entities +{ + /// + /// Provides for the required ReferenceData capabilities. + /// + public partial interface IReferenceData : IReferenceDataProvider + { + #region Collections + + /// + /// Gets the . + /// + RefDataNamespace.GenderCollection Gender { get; } + + /// + /// Gets the . + /// + RefDataNamespace.TerminationReasonCollection TerminationReason { get; } + + /// + /// Gets the . + /// + RefDataNamespace.RelationshipTypeCollection RelationshipType { get; } + + /// + /// Gets the . + /// + RefDataNamespace.USStateCollection USState { get; } + + /// + /// Gets the . + /// + RefDataNamespace.PerformanceOutcomeCollection PerformanceOutcome { get; } + + #endregion + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Entities/Generated/PerformanceOutcome.cs b/samples/My.Hr/My.Hr.Common/Entities/Generated/PerformanceOutcome.cs new file mode 100644 index 000000000..8d59add5d --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Entities/Generated/PerformanceOutcome.cs @@ -0,0 +1,154 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData; +using Newtonsoft.Json; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Entities +{ + /// + /// Represents the Performance Outcome entity. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + [ReferenceDataInterface(typeof(IReferenceData))] + public partial class PerformanceOutcome : ReferenceDataBaseGuid + { + #region Operator + + /// + /// An implicit cast from an Id to a . + /// + /// The Id. + /// The corresponding . + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator PerformanceOutcome(Guid id) => ConvertFromId(id); + + /// + /// An implicit cast from a Code to a . + /// + /// The Code. + /// The corresponding . + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator PerformanceOutcome(string? code) => ConvertFromCode(code); + + #endregion + + #region ICopyFrom + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public override void CopyFrom(object from) + { + var fval = ValidateCopyFromType(from); + CopyFrom(fval); + } + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public void CopyFrom(PerformanceOutcome from) + { + if (from == null) + throw new ArgumentNullException(nameof(from)); + + CopyFrom((ReferenceDataBaseGuid)from); + + OnAfterCopyFrom(from); + } + + #endregion + + #region ICloneable + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new PerformanceOutcome(); + clone.CopyFrom(this); + return clone; + } + + #endregion + + #region ICleanUp + + /// + /// Performs a clean-up of the resetting property values as appropriate to ensure a basic level of data consistency. + /// + public override void CleanUp() + { + base.CleanUp(); + + OnAfterCleanUp(); + } + + /// + /// Indicates whether considered initial; i.e. all properties have their initial value. + /// + /// true indicates is initial; otherwise, false. + public override bool IsInitial + { + get + { + if (!base.IsInitial) + return false; + + return true; + } + } + + #endregion + + #region PartialMethods + + partial void OnAfterCleanUp(); + + partial void OnAfterCopyFrom(PerformanceOutcome from); + + #endregion + } + + #region Collection + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class PerformanceOutcomeCollection : ReferenceDataCollectionBase + { + /// + /// Initializes a new instance of the class. + /// + public PerformanceOutcomeCollection() { } + + /// + /// Initializes a new instance of the class with an entities range. + /// + /// The entities. + public PerformanceOutcomeCollection(IEnumerable entities) => AddRange(entities); + } + + #endregion +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Entities/Generated/PerformanceReview.cs b/samples/My.Hr/My.Hr.Common/Entities/Generated/PerformanceReview.cs new file mode 100644 index 000000000..0d0ff67f2 --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Entities/Generated/PerformanceReview.cs @@ -0,0 +1,456 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData; +using Newtonsoft.Json; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Entities +{ + /// + /// Represents the Performance Review entity. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public partial class PerformanceReview : EntityBase, IGuidIdentifier, IETag, IChangeLog, IEquatable + { + #region Privates + + private Guid _id; + private Guid _employeeId; + private DateTime _date; + private string? _outcomeSid; + private string? _outcomeText; + private string? _reviewer; + private string? _notes; + private string? _eTag; + private ChangeLog? _changeLog; + + #endregion + + #region Properties + + /// + /// Gets or sets the identifier. + /// + [JsonProperty("id", DefaultValueHandling = DefaultValueHandling.Include)] + [Display(Name="Identifier")] + public Guid Id + { + get => _id; + set => SetValue(ref _id, value, false, false, nameof(Id)); + } + + /// + /// Gets or sets the (value is immutable). + /// + [JsonProperty("employeeId", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Employee")] + public Guid EmployeeId + { + get => _employeeId; + set => SetValue(ref _employeeId, value, false, false, nameof(EmployeeId)); + } + + /// + /// Gets or sets the Date. + /// + [JsonProperty("date", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Date")] + public DateTime Date + { + get => _date; + set => SetValue(ref _date, value, false, DateTimeTransform.UseDefault, nameof(Date)); + } + + /// + /// Gets or sets the using the underlying Serialization Identifier (SID). + /// + [JsonProperty("outcome", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Outcome")] + public string? OutcomeSid + { + get => _outcomeSid; + set => SetValue(ref _outcomeSid, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Outcome)); + } + + /// + /// Gets the corresponding {{Outcome}} text (read-only where selected). + /// + [JsonProperty("outcomeText", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? OutcomeText { get => _outcomeText ?? GetRefDataText(() => Outcome); set => _outcomeText = value; } + + /// + /// Gets or sets the Outcome (see ). + /// + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + [Display(Name="Outcome")] + public RefDataNamespace.PerformanceOutcome? Outcome + { + get => _outcomeSid; + set => SetValue(ref _outcomeSid, value, false, false, nameof(Outcome)); + } + + /// + /// Gets or sets the Reviewer. + /// + [JsonProperty("reviewer", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Reviewer")] + public string? Reviewer + { + get => _reviewer; + set => SetValue(ref _reviewer, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Reviewer)); + } + + /// + /// Gets or sets the Notes. + /// + [JsonProperty("notes", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Notes")] + public string? Notes + { + get => _notes; + set => SetValue(ref _notes, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Notes)); + } + + /// + /// Gets or sets the ETag. + /// + [JsonProperty("etag", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="ETag")] + public string? ETag + { + get => _eTag; + set => SetValue(ref _eTag, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(ETag)); + } + + /// + /// Gets or sets the Change Log (see ). + /// + [JsonProperty("changeLog", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Change Log")] + public ChangeLog? ChangeLog + { + get => _changeLog; + set => SetValue(ref _changeLog, value, false, true, nameof(ChangeLog)); + } + + #endregion + + #region IChangeTracking + + /// + /// Resets the entity state to unchanged by accepting the changes (resets ). + /// + /// Ends and commits the entity changes (see ). + public override void AcceptChanges() + { + ChangeLog?.AcceptChanges(); + base.AcceptChanges(); + } + + /// + /// Determines that until is invoked property changes are to be logged (see ). + /// + public override void TrackChanges() + { + ChangeLog?.TrackChanges(); + base.TrackChanges(); + } + + #endregion + + #region IUniqueKey + + /// + /// Indicates whether the has a value. + /// + public override bool HasUniqueKey => true; + + /// + /// Gets the list of property names that represent the unique key. + /// + public override string[] UniqueKeyProperties => new string[] { nameof(Id) }; + + /// + /// Creates the . + /// + /// The . + /// The . + public static UniqueKey CreateUniqueKey(Guid id) => new UniqueKey(id); + + /// + /// Gets the (consists of the following property(s): ). + /// + public override UniqueKey UniqueKey => new UniqueKey(Id); + + #endregion + + #region IEquatable + + /// + /// Determines whether the specified object is equal to the current object by comparing the values of all the properties. + /// + /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? obj) => obj is PerformanceReview val && Equals(val); + + /// + /// Determines whether the specified is equal to the current by comparing the values of all the properties. + /// + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(PerformanceReview? value) + { + if (value == null) + return false; + else if (ReferenceEquals(value, this)) + return true; + + return base.Equals((object)value) + && Equals(Id, value.Id) + && Equals(EmployeeId, value.EmployeeId) + && Equals(Date, value.Date) + && Equals(OutcomeSid, value.OutcomeSid) + && Equals(Reviewer, value.Reviewer) + && Equals(Notes, value.Notes) + && Equals(ETag, value.ETag) + && Equals(ChangeLog, value.ChangeLog); + } + + /// + /// Compares two types for equality. + /// + /// A. + /// B. + /// true indicates equal; otherwise, false for not equal. + public static bool operator == (PerformanceReview? a, PerformanceReview? b) => Equals(a, b); + + /// + /// Compares two types for non-equality. + /// + /// A. + /// B. + /// true indicates not equal; otherwise, false for equal. + public static bool operator != (PerformanceReview? a, PerformanceReview? b) => !Equals(a, b); + + /// + /// Returns the hash code for the . + /// + /// The hash code for the . + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(Id); + hash.Add(EmployeeId); + hash.Add(Date); + hash.Add(OutcomeSid); + hash.Add(Reviewer); + hash.Add(Notes); + hash.Add(ETag); + hash.Add(ChangeLog); + return base.GetHashCode() ^ hash.ToHashCode(); + } + + #endregion + + #region ICopyFrom + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public override void CopyFrom(object from) + { + var fval = ValidateCopyFromType(from); + CopyFrom(fval); + } + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public void CopyFrom(PerformanceReview from) + { + if (from == null) + throw new ArgumentNullException(nameof(from)); + + CopyFrom((EntityBase)from); + Id = from.Id; + EmployeeId = from.EmployeeId; + Date = from.Date; + OutcomeSid = from.OutcomeSid; + Reviewer = from.Reviewer; + Notes = from.Notes; + ETag = from.ETag; + ChangeLog = CopyOrClone(from.ChangeLog, ChangeLog); + + OnAfterCopyFrom(from); + } + + #endregion + + #region ICloneable + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new PerformanceReview(); + clone.CopyFrom(this); + return clone; + } + + #endregion + + #region ICleanUp + + /// + /// Performs a clean-up of the resetting property values as appropriate to ensure a basic level of data consistency. + /// + public override void CleanUp() + { + base.CleanUp(); + Id = Cleaner.Clean(Id); + EmployeeId = Cleaner.Clean(EmployeeId); + Date = Cleaner.Clean(Date, DateTimeTransform.UseDefault); + OutcomeSid = Cleaner.Clean(OutcomeSid); + Reviewer = Cleaner.Clean(Reviewer, StringTrim.UseDefault, StringTransform.UseDefault); + Notes = Cleaner.Clean(Notes, StringTrim.UseDefault, StringTransform.UseDefault); + ETag = Cleaner.Clean(ETag, StringTrim.UseDefault, StringTransform.UseDefault); + ChangeLog = Cleaner.Clean(ChangeLog); + + OnAfterCleanUp(); + } + + /// + /// Indicates whether considered initial; i.e. all properties have their initial value. + /// + /// true indicates is initial; otherwise, false. + public override bool IsInitial + { + get + { + return Cleaner.IsInitial(Id) + && Cleaner.IsInitial(EmployeeId) + && Cleaner.IsInitial(Date) + && Cleaner.IsInitial(OutcomeSid) + && Cleaner.IsInitial(Reviewer) + && Cleaner.IsInitial(Notes) + && Cleaner.IsInitial(ETag) + && Cleaner.IsInitial(ChangeLog); + } + } + + #endregion + + #region PartialMethods + + partial void OnAfterCleanUp(); + + partial void OnAfterCopyFrom(PerformanceReview from); + + #endregion + } + + #region Collection + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class PerformanceReviewCollection : EntityBaseCollection + { + /// + /// Initializes a new instance of the class. + /// + public PerformanceReviewCollection() { } + + /// + /// Initializes a new instance of the class with an entities range. + /// + /// The entities. + public PerformanceReviewCollection(IEnumerable entities) => AddRange(entities); + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new PerformanceReviewCollection(); + foreach (var item in this) + { + clone.Add((PerformanceReview)item.Clone()); + } + + return clone; + } + + /// + /// An implicit cast from the to a corresponding . + /// + /// The . + /// The corresponding . + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator PerformanceReviewCollection(PerformanceReviewCollectionResult result) => result?.Result!; + } + + #endregion + + #region CollectionResult + + /// + /// Represents the collection result. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public class PerformanceReviewCollectionResult : EntityCollectionResult + { + /// + /// Initializes a new instance of the class. + /// + public PerformanceReviewCollectionResult() { } + + /// + /// Initializes a new instance of the class with . + /// + /// The . + public PerformanceReviewCollectionResult(PagingArgs? paging) : base(paging) { } + + /// + /// Initializes a new instance of the class with a of items to add. + /// + /// A collection containing items to add. + /// The . + public PerformanceReviewCollectionResult(IEnumerable collection, PagingArgs? paging = null) : base(paging) => Result.AddRange(collection); + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new PerformanceReviewCollectionResult(); + clone.CopyFrom(this); + return clone; + } + } + + #endregion +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ReferenceData_cs.xml b/samples/My.Hr/My.Hr.Common/Entities/Generated/ReferenceData.cs similarity index 58% rename from tools/Beef.CodeGen.Core/Templates/ReferenceData_cs.xml rename to samples/My.Hr/My.Hr.Common/Entities/Generated/ReferenceData.cs index 14e9b7b4d..ff9ba39ad 100644 --- a/tools/Beef.CodeGen.Core/Templates/ReferenceData_cs.xml +++ b/samples/My.Hr/My.Hr.Common/Entities/Generated/ReferenceData.cs @@ -1,11 +1,4 @@ - - - \ No newline at end of file +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Entities/Generated/RelationshipType.cs b/samples/My.Hr/My.Hr.Common/Entities/Generated/RelationshipType.cs new file mode 100644 index 000000000..322584920 --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Entities/Generated/RelationshipType.cs @@ -0,0 +1,154 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData; +using Newtonsoft.Json; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Entities +{ + /// + /// Represents the Relationship Type entity. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + [ReferenceDataInterface(typeof(IReferenceData))] + public partial class RelationshipType : ReferenceDataBaseGuid + { + #region Operator + + /// + /// An implicit cast from an Id to a . + /// + /// The Id. + /// The corresponding . + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator RelationshipType(Guid id) => ConvertFromId(id); + + /// + /// An implicit cast from a Code to a . + /// + /// The Code. + /// The corresponding . + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator RelationshipType(string? code) => ConvertFromCode(code); + + #endregion + + #region ICopyFrom + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public override void CopyFrom(object from) + { + var fval = ValidateCopyFromType(from); + CopyFrom(fval); + } + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public void CopyFrom(RelationshipType from) + { + if (from == null) + throw new ArgumentNullException(nameof(from)); + + CopyFrom((ReferenceDataBaseGuid)from); + + OnAfterCopyFrom(from); + } + + #endregion + + #region ICloneable + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new RelationshipType(); + clone.CopyFrom(this); + return clone; + } + + #endregion + + #region ICleanUp + + /// + /// Performs a clean-up of the resetting property values as appropriate to ensure a basic level of data consistency. + /// + public override void CleanUp() + { + base.CleanUp(); + + OnAfterCleanUp(); + } + + /// + /// Indicates whether considered initial; i.e. all properties have their initial value. + /// + /// true indicates is initial; otherwise, false. + public override bool IsInitial + { + get + { + if (!base.IsInitial) + return false; + + return true; + } + } + + #endregion + + #region PartialMethods + + partial void OnAfterCleanUp(); + + partial void OnAfterCopyFrom(RelationshipType from); + + #endregion + } + + #region Collection + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class RelationshipTypeCollection : ReferenceDataCollectionBase + { + /// + /// Initializes a new instance of the class. + /// + public RelationshipTypeCollection() { } + + /// + /// Initializes a new instance of the class with an entities range. + /// + /// The entities. + public RelationshipTypeCollection(IEnumerable entities) => AddRange(entities); + } + + #endregion +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Entities/Generated/TerminationDetail.cs b/samples/My.Hr/My.Hr.Common/Entities/Generated/TerminationDetail.cs new file mode 100644 index 000000000..c1dd44174 --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Entities/Generated/TerminationDetail.cs @@ -0,0 +1,220 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData; +using Newtonsoft.Json; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Entities +{ + /// + /// Represents the Termination Detail entity. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public partial class TerminationDetail : EntityBase, IEquatable + { + #region Privates + + private DateTime _date; + private string? _reasonSid; + private string? _reasonText; + + #endregion + + #region Properties + + /// + /// Gets or sets the Date. + /// + [JsonProperty("date", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Date")] + public DateTime Date + { + get => _date; + set => SetValue(ref _date, value, false, DateTimeTransform.DateOnly, nameof(Date)); + } + + /// + /// Gets or sets the using the underlying Serialization Identifier (SID). + /// + [JsonProperty("reason", DefaultValueHandling = DefaultValueHandling.Ignore)] + [Display(Name="Reason")] + public string? ReasonSid + { + get => _reasonSid; + set => SetValue(ref _reasonSid, value, false, StringTrim.UseDefault, StringTransform.UseDefault, nameof(Reason)); + } + + /// + /// Gets the corresponding {{Reason}} text (read-only where selected). + /// + [JsonProperty("reasonText", DefaultValueHandling = DefaultValueHandling.Ignore)] + public string? ReasonText { get => _reasonText ?? GetRefDataText(() => Reason); set => _reasonText = value; } + + /// + /// Gets or sets the Reason (see ). + /// + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + [Display(Name="Reason")] + public RefDataNamespace.TerminationReason? Reason + { + get => _reasonSid; + set => SetValue(ref _reasonSid, value, false, false, nameof(Reason)); + } + + #endregion + + #region IEquatable + + /// + /// Determines whether the specified object is equal to the current object by comparing the values of all the properties. + /// + /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? obj) => obj is TerminationDetail val && Equals(val); + + /// + /// Determines whether the specified is equal to the current by comparing the values of all the properties. + /// + /// The to compare with the current . + /// true if the specified is equal to the current ; otherwise, false. + public bool Equals(TerminationDetail? value) + { + if (value == null) + return false; + else if (ReferenceEquals(value, this)) + return true; + + return base.Equals((object)value) + && Equals(Date, value.Date) + && Equals(ReasonSid, value.ReasonSid); + } + + /// + /// Compares two types for equality. + /// + /// A. + /// B. + /// true indicates equal; otherwise, false for not equal. + public static bool operator == (TerminationDetail? a, TerminationDetail? b) => Equals(a, b); + + /// + /// Compares two types for non-equality. + /// + /// A. + /// B. + /// true indicates not equal; otherwise, false for equal. + public static bool operator != (TerminationDetail? a, TerminationDetail? b) => !Equals(a, b); + + /// + /// Returns the hash code for the . + /// + /// The hash code for the . + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(Date); + hash.Add(ReasonSid); + return base.GetHashCode() ^ hash.ToHashCode(); + } + + #endregion + + #region ICopyFrom + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public override void CopyFrom(object from) + { + var fval = ValidateCopyFromType(from); + CopyFrom(fval); + } + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public void CopyFrom(TerminationDetail from) + { + if (from == null) + throw new ArgumentNullException(nameof(from)); + + CopyFrom((EntityBase)from); + Date = from.Date; + ReasonSid = from.ReasonSid; + + OnAfterCopyFrom(from); + } + + #endregion + + #region ICloneable + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new TerminationDetail(); + clone.CopyFrom(this); + return clone; + } + + #endregion + + #region ICleanUp + + /// + /// Performs a clean-up of the resetting property values as appropriate to ensure a basic level of data consistency. + /// + public override void CleanUp() + { + base.CleanUp(); + Date = Cleaner.Clean(Date, DateTimeTransform.DateOnly); + ReasonSid = Cleaner.Clean(ReasonSid); + + OnAfterCleanUp(); + } + + /// + /// Indicates whether considered initial; i.e. all properties have their initial value. + /// + /// true indicates is initial; otherwise, false. + public override bool IsInitial + { + get + { + return Cleaner.IsInitial(Date) + && Cleaner.IsInitial(ReasonSid); + } + } + + #endregion + + #region PartialMethods + + partial void OnAfterCleanUp(); + + partial void OnAfterCopyFrom(TerminationDetail from); + + #endregion + } +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Entities/Generated/TerminationReason.cs b/samples/My.Hr/My.Hr.Common/Entities/Generated/TerminationReason.cs new file mode 100644 index 000000000..b739e56b2 --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Entities/Generated/TerminationReason.cs @@ -0,0 +1,154 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData; +using Newtonsoft.Json; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Entities +{ + /// + /// Represents the Termination Reason entity. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + [ReferenceDataInterface(typeof(IReferenceData))] + public partial class TerminationReason : ReferenceDataBaseGuid + { + #region Operator + + /// + /// An implicit cast from an Id to a . + /// + /// The Id. + /// The corresponding . + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator TerminationReason(Guid id) => ConvertFromId(id); + + /// + /// An implicit cast from a Code to a . + /// + /// The Code. + /// The corresponding . + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator TerminationReason(string? code) => ConvertFromCode(code); + + #endregion + + #region ICopyFrom + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public override void CopyFrom(object from) + { + var fval = ValidateCopyFromType(from); + CopyFrom(fval); + } + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public void CopyFrom(TerminationReason from) + { + if (from == null) + throw new ArgumentNullException(nameof(from)); + + CopyFrom((ReferenceDataBaseGuid)from); + + OnAfterCopyFrom(from); + } + + #endregion + + #region ICloneable + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new TerminationReason(); + clone.CopyFrom(this); + return clone; + } + + #endregion + + #region ICleanUp + + /// + /// Performs a clean-up of the resetting property values as appropriate to ensure a basic level of data consistency. + /// + public override void CleanUp() + { + base.CleanUp(); + + OnAfterCleanUp(); + } + + /// + /// Indicates whether considered initial; i.e. all properties have their initial value. + /// + /// true indicates is initial; otherwise, false. + public override bool IsInitial + { + get + { + if (!base.IsInitial) + return false; + + return true; + } + } + + #endregion + + #region PartialMethods + + partial void OnAfterCleanUp(); + + partial void OnAfterCopyFrom(TerminationReason from); + + #endregion + } + + #region Collection + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class TerminationReasonCollection : ReferenceDataCollectionBase + { + /// + /// Initializes a new instance of the class. + /// + public TerminationReasonCollection() { } + + /// + /// Initializes a new instance of the class with an entities range. + /// + /// The entities. + public TerminationReasonCollection(IEnumerable entities) => AddRange(entities); + } + + #endregion +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/Entities/Generated/USState.cs b/samples/My.Hr/My.Hr.Common/Entities/Generated/USState.cs new file mode 100644 index 000000000..d500e0d00 --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/Entities/Generated/USState.cs @@ -0,0 +1,154 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData; +using Newtonsoft.Json; +using RefDataNamespace = My.Hr.Common.Entities; + +namespace My.Hr.Common.Entities +{ + /// + /// Represents the US State entity. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + [ReferenceDataInterface(typeof(IReferenceData))] + public partial class USState : ReferenceDataBaseGuid + { + #region Operator + + /// + /// An implicit cast from an Id to a . + /// + /// The Id. + /// The corresponding . + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator USState(Guid id) => ConvertFromId(id); + + /// + /// An implicit cast from a Code to a . + /// + /// The Code. + /// The corresponding . + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator USState(string? code) => ConvertFromCode(code); + + #endregion + + #region ICopyFrom + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public override void CopyFrom(object from) + { + var fval = ValidateCopyFromType(from); + CopyFrom(fval); + } + + /// + /// Performs a copy from another updating this instance. + /// + /// The to copy from. + public void CopyFrom(USState from) + { + if (from == null) + throw new ArgumentNullException(nameof(from)); + + CopyFrom((ReferenceDataBaseGuid)from); + + OnAfterCopyFrom(from); + } + + #endregion + + #region ICloneable + + /// + /// Creates a deep copy of the . + /// + /// A deep copy of the . + public override object Clone() + { + var clone = new USState(); + clone.CopyFrom(this); + return clone; + } + + #endregion + + #region ICleanUp + + /// + /// Performs a clean-up of the resetting property values as appropriate to ensure a basic level of data consistency. + /// + public override void CleanUp() + { + base.CleanUp(); + + OnAfterCleanUp(); + } + + /// + /// Indicates whether considered initial; i.e. all properties have their initial value. + /// + /// true indicates is initial; otherwise, false. + public override bool IsInitial + { + get + { + if (!base.IsInitial) + return false; + + return true; + } + } + + #endregion + + #region PartialMethods + + partial void OnAfterCleanUp(); + + partial void OnAfterCopyFrom(USState from); + + #endregion + } + + #region Collection + + /// + /// Represents the collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class USStateCollection : ReferenceDataCollectionBase + { + /// + /// Initializes a new instance of the class. + /// + public USStateCollection() { } + + /// + /// Initializes a new instance of the class with an entities range. + /// + /// The entities. + public USStateCollection(IEnumerable entities) => AddRange(entities); + } + + #endregion +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Common/My.Hr.Common.csproj b/samples/My.Hr/My.Hr.Common/My.Hr.Common.csproj new file mode 100644 index 000000000..8eb1a813c --- /dev/null +++ b/samples/My.Hr/My.Hr.Common/My.Hr.Common.csproj @@ -0,0 +1,13 @@ + + + netstandard2.1 + enable + + + + + + + + + \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Data/RefData.yaml b/samples/My.Hr/My.Hr.Database/Data/RefData.yaml new file mode 100644 index 000000000..ce76f68f8 --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Data/RefData.yaml @@ -0,0 +1,72 @@ +Ref: + - $Gender: + - M: Male + - F: Female + - N: Not specified + - $TerminationReason: + - RE: Resigned + - RD: Redundant + - TM: Terminated + - $RelationshipType: + - SPO: Spouse + - PTN: Partner + - PAR: Parent + - CHI: Child + - SIB: Sibling + - EXF: Extended family + - FRD: Friend + - $USState: + - AL: Alabama + - AK: Alaska + - AZ: Arizona + - AR: Arkansas + - CA: California + - CO: Colorado + - CT: Connecticut + - DE: Delaware + - FL: Florida + - GA: Georgia + - HI: Hawaii + - ID: Idaho + - IL: Illinois + - IN: Indiana + - IA: Iowa + - KS: Kansas + - KY: Kentucky + - LA: Louisiana + - ME: Maine + - MD: Maryland + - MA: Massachusetts + - MI: Michigan + - MN: Minnesota + - MS: Mississippi + - MO: Missouri + - MT: Montana + - NE: Nebraska + - NV: Nevada + - NH: New Hampshire + - NJ: New Jersey + - NM: New Mexico + - NY: New York + - NC: North Carolina + - ND: North Dakota + - OH: Ohio + - OK: Oklahoma + - OR: Oregon + - PA: Pennsylvania + - RI: Rhode Island + - SC: South Carolina + - SD: South Dakota + - TN: Tennessee + - TX: Texas + - UT: Utah + - VT: Vermont + - VA: Virginia + - WA: Washington + - WV: West Virginia + - WI: Wisconsin + - WY: Wyoming + - $PerformanceOutcome: + - DN: Does not meet expectations + - ME: Meets expectations + - EE: Exceeds expectations \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Migrations/20190101-000000-create-Ref-schema.sql b/samples/My.Hr/My.Hr.Database/Migrations/20190101-000000-create-Ref-schema.sql new file mode 100644 index 000000000..104128143 --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Migrations/20190101-000000-create-Ref-schema.sql @@ -0,0 +1,2 @@ +CREATE SCHEMA [Ref] + AUTHORIZATION [dbo]; \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Migrations/20190101-000001-create-Hr-schema.sql b/samples/My.Hr/My.Hr.Database/Migrations/20190101-000001-create-Hr-schema.sql new file mode 100644 index 000000000..5fc766a55 --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Migrations/20190101-000001-create-Hr-schema.sql @@ -0,0 +1,2 @@ +CREATE SCHEMA [Hr] + AUTHORIZATION [dbo]; \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Migrations/20200909-162702-create-Hr-Employee.sql b/samples/My.Hr/My.Hr.Database/Migrations/20200909-162702-create-Hr-Employee.sql new file mode 100644 index 000000000..e6d953e18 --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Migrations/20200909-162702-create-Hr-Employee.sql @@ -0,0 +1,24 @@ +-- Migration Script + +BEGIN TRANSACTION + +CREATE TABLE [Hr].[Employee] ( + [EmployeeId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY, -- This is the primary key + [Email] NVARCHAR(250) NULL UNIQUE, -- This is the employee's unique email address + [FirstName] NVARCHAR(100) NULL, + [LastName] NVARCHAR(100) NULL, + [GenderCode] NVARCHAR(50) NULL, -- This is the related Gender code; see Ref.Gender table + [Birthday] DATE NULL, + [StartDate] DATE NULL, + [TerminationDate] DATE NULL, + [TerminationReasonCode] NVARCHAR(50) NULL, -- This is the related Termination Reason code; see Ref.TerminationReason table + [PhoneNo] NVARCHAR(50) NULL, + [AddressJson] NVARCHAR(500) NULL, -- This is the full address persisted as JSON. + [RowVersion] TIMESTAMP NOT NULL, -- This is used for concurrency version checking. + [CreatedBy] NVARCHAR(250) NULL, -- The following are standard audit columns. + [CreatedDate] DATETIME2 NULL, + [UpdatedBy] NVARCHAR(250) NULL, + [UpdatedDate] DATETIME2 NULL +); + +COMMIT TRANSACTION \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Migrations/20200909-163321-create-Hr-EmergencyContact.sql b/samples/My.Hr/My.Hr.Database/Migrations/20200909-163321-create-Hr-EmergencyContact.sql new file mode 100644 index 000000000..45a6652a9 --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Migrations/20200909-163321-create-Hr-EmergencyContact.sql @@ -0,0 +1,14 @@ +-- Migration Script + +BEGIN TRANSACTION + +CREATE TABLE [Hr].[EmergencyContact] ( + [EmergencyContactId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY, + [EmployeeId] UNIQUEIDENTIFIER NOT NULL, + [FirstName] NVARCHAR(100) NULL, + [LastName] NVARCHAR(100) NULL, + [PhoneNo] NVARCHAR(50) NULL, + [RelationshipTypeCode] NVARCHAR(50) NULL +); + +COMMIT TRANSACTION \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Migrations/20200909-164735-create-Ref-Gender.sql b/samples/My.Hr/My.Hr.Database/Migrations/20200909-164735-create-Ref-Gender.sql new file mode 100644 index 000000000..85a452d5b --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Migrations/20200909-164735-create-Ref-Gender.sql @@ -0,0 +1,18 @@ +-- Migration Script + +BEGIN TRANSACTION + +CREATE TABLE [Ref].[Gender] ( + [GenderId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY, + [Code] NVARCHAR(50) NOT NULL UNIQUE, + [Text] NVARCHAR(250) NULL, + [IsActive] BIT NULL, + [SortOrder] INT NULL, + [RowVersion] TIMESTAMP NOT NULL, + [CreatedBy] NVARCHAR(250) NULL, + [CreatedDate] DATETIME2 NULL, + [UpdatedBy] NVARCHAR(250) NULL, + [UpdatedDate] DATETIME2 NULL +); + +COMMIT TRANSACTION \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Migrations/20200909-164828-create-Ref-TerminationReason.sql b/samples/My.Hr/My.Hr.Database/Migrations/20200909-164828-create-Ref-TerminationReason.sql new file mode 100644 index 000000000..fcafb1e4e --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Migrations/20200909-164828-create-Ref-TerminationReason.sql @@ -0,0 +1,18 @@ +-- Migration Script + +BEGIN TRANSACTION + +CREATE TABLE [Ref].[TerminationReason] ( + [TerminationReasonId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY, + [Code] NVARCHAR(50) NOT NULL UNIQUE, + [Text] NVARCHAR(250) NULL, + [IsActive] BIT NULL, + [SortOrder] INT NULL, + [RowVersion] TIMESTAMP NOT NULL, + [CreatedBy] NVARCHAR(250) NULL, + [CreatedDate] DATETIME2 NULL, + [UpdatedBy] NVARCHAR(250) NULL, + [UpdatedDate] DATETIME2 NULL +); + +COMMIT TRANSACTION \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Migrations/20200909-165308-create-Ref-RelationshipType.sql b/samples/My.Hr/My.Hr.Database/Migrations/20200909-165308-create-Ref-RelationshipType.sql new file mode 100644 index 000000000..ada87d92a --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Migrations/20200909-165308-create-Ref-RelationshipType.sql @@ -0,0 +1,18 @@ +-- Migration Script + +BEGIN TRANSACTION + +CREATE TABLE [Ref].[RelationshipType] ( + [RelationshipTypeId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY, + [Code] NVARCHAR(50) NOT NULL UNIQUE, + [Text] NVARCHAR(250) NULL, + [IsActive] BIT NULL, + [SortOrder] INT NULL, + [RowVersion] TIMESTAMP NOT NULL, + [CreatedBy] NVARCHAR(250) NULL, + [CreatedDate] DATETIME2 NULL, + [UpdatedBy] NVARCHAR(250) NULL, + [UpdatedDate] DATETIME2 NULL +); + +COMMIT TRANSACTION \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Migrations/20200909-165752-create-Ref-USState.sql b/samples/My.Hr/My.Hr.Database/Migrations/20200909-165752-create-Ref-USState.sql new file mode 100644 index 000000000..b6d60d3f5 --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Migrations/20200909-165752-create-Ref-USState.sql @@ -0,0 +1,18 @@ +-- Migration Script + +BEGIN TRANSACTION + +CREATE TABLE [Ref].[USState] ( + [USStateId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY, + [Code] NVARCHAR(50) NOT NULL UNIQUE, + [Text] NVARCHAR(250) NULL, + [IsActive] BIT NULL, + [SortOrder] INT NULL, + [RowVersion] TIMESTAMP NOT NULL, + [CreatedBy] NVARCHAR(250) NULL, + [CreatedDate] DATETIME2 NULL, + [UpdatedBy] NVARCHAR(250) NULL, + [UpdatedDate] DATETIME2 NULL +); + +COMMIT TRANSACTION \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Migrations/20200915-160812-create-Hr-PerformanceReview.sql b/samples/My.Hr/My.Hr.Database/Migrations/20200915-160812-create-Hr-PerformanceReview.sql new file mode 100644 index 000000000..5582abf7c --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Migrations/20200915-160812-create-Hr-PerformanceReview.sql @@ -0,0 +1,19 @@ +-- Migration Script + +BEGIN TRANSACTION + +CREATE TABLE [Hr].[PerformanceReview] ( + [PerformanceReviewId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY, + [EmployeeId] UNIQUEIDENTIFIER NOT NULL, + [Date] DATETIME2 NULL, + [PerformanceOutcomeCode] NVARCHAR(50) NULL, + [Reviewer] NVARCHAR(100) NULL, + [Notes] NVARCHAR(4000) NULL, + [RowVersion] TIMESTAMP NOT NULL, + [CreatedBy] NVARCHAR(250) NULL, + [CreatedDate] DATETIME2 NULL, + [UpdatedBy] NVARCHAR(250) NULL, + [UpdatedDate] DATETIME2 NULL +); + +COMMIT TRANSACTION \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Migrations/20200915-161927-create-Ref-PerformanceOutcome.sql b/samples/My.Hr/My.Hr.Database/Migrations/20200915-161927-create-Ref-PerformanceOutcome.sql new file mode 100644 index 000000000..d56829800 --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Migrations/20200915-161927-create-Ref-PerformanceOutcome.sql @@ -0,0 +1,18 @@ +-- Migration Script + +BEGIN TRANSACTION + +CREATE TABLE [Ref].[PerformanceOutcome] ( + [PerformanceOutcomeId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY, + [Code] NVARCHAR(50) NOT NULL UNIQUE, + [Text] NVARCHAR(250) NULL, + [IsActive] BIT NULL, + [SortOrder] INT NULL, + [RowVersion] TIMESTAMP NOT NULL, + [CreatedBy] NVARCHAR(250) NULL, + [CreatedDate] DATETIME2 NULL, + [UpdatedBy] NVARCHAR(250) NULL, + [UpdatedDate] DATETIME2 NULL +); + +COMMIT TRANSACTION \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/My.Hr.Database.csproj b/samples/My.Hr/My.Hr.Database/My.Hr.Database.csproj new file mode 100644 index 000000000..214ec724c --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/My.Hr.Database.csproj @@ -0,0 +1,29 @@ + + + + Exe + netcoreapp3.1 + enable + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/My.Hr/My.Hr.Database/My.Hr.Database.xml b/samples/My.Hr/My.Hr.Database/My.Hr.Database.xml new file mode 100644 index 000000000..e75dfe2cb --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/My.Hr.Database.xml @@ -0,0 +1,49 @@ + + + + +
+
+
+ + +
+ + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + + + +
+
+ \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Program.cs b/samples/My.Hr/My.Hr.Database/Program.cs new file mode 100644 index 000000000..271833311 --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Program.cs @@ -0,0 +1,21 @@ +using Beef.Database.Core; +using System.Threading.Tasks; + +namespace My.Hr.Database +{ + /// + /// Represents the database utilities program (capability). + /// + public static class Program + { + /// + /// Main startup. + /// + /// The startup arguments. + /// The status code whereby zero indicates success. + static Task Main(string[] args) + { + return DatabaseConsoleWrapper.Create("Data Source=.;Initial Catalog=My.Hr;Integrated Security=True", "My", "Hr", useBeefDbo: true).RunAsync(args); + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmergencyContactGetByEmployeeId.sql b/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmergencyContactGetByEmployeeId.sql new file mode 100644 index 000000000..96b35b423 --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmergencyContactGetByEmployeeId.sql @@ -0,0 +1,21 @@ +CREATE PROCEDURE [Hr].[spEmergencyContactGetByEmployeeId] + @EmployeeId AS UNIQUEIDENTIFIER +AS +BEGIN + /* + * This is automatically generated; any changes will be lost. + */ + + SET NOCOUNT ON; + + -- Select the requested data. + SELECT + [ec].[EmergencyContactId] + ,[ec].[EmployeeId] + ,[ec].[FirstName] + ,[ec].[LastName] + ,[ec].[PhoneNo] + ,[ec].[RelationshipTypeCode] + FROM [Hr].[EmergencyContact] AS [ec] + WHERE ([ec].[EmployeeId] = @EmployeeId) +END \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmergencyContactMerge.sql b/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmergencyContactMerge.sql new file mode 100644 index 000000000..43d7591dc --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmergencyContactMerge.sql @@ -0,0 +1,74 @@ +CREATE PROCEDURE [Hr].[spEmergencyContactMerge] + @EmployeeId AS UNIQUEIDENTIFIER + ,@List AS [Hr].[udtEmergencyContactList] READONLY +AS +BEGIN + /* + * This is automatically generated; any changes will be lost. + */ + + SET NOCOUNT ON; + + BEGIN TRY + -- Wrap in a transaction. + BEGIN TRANSACTION + + -- Check valid for merge. + DECLARE @ListCount INT + SET @ListCount = (SELECT COUNT(*) FROM @List WHERE [EmergencyContactId] IS NOT NULL AND [EmergencyContactId] <> CONVERT(UNIQUEIDENTIFIER, '00000000-0000-0000-0000-000000000000')) + + DECLARE @RecordCount INT + SET @RecordCount = (SELECT COUNT(*) FROM @List as [list] + INNER JOIN [Hr].[EmergencyContact] as [ec] + ON [ec].[EmergencyContactId] = [list].[EmergencyContactId] + AND [ec].[EmployeeId] = @EmployeeId) + + IF @ListCount <> @RecordCount + BEGIN + EXEC spThrowConcurrencyException + END + + -- Merge the records. + MERGE INTO [Hr].[EmergencyContact] WITH (HOLDLOCK) AS [t] + USING @List as [s] + ON ([t].[EmergencyContactId] = [s].[EmergencyContactId] + AND [t].[EmployeeId] = @EmployeeId) + WHEN MATCHED AND EXISTS + (SELECT [s].[FirstName], [s].[LastName], [s].[PhoneNo], [s].[RelationshipTypeCode] + EXCEPT + SELECT [t].[FirstName], [t].[LastName], [t].[PhoneNo], [t].[RelationshipTypeCode]) + THEN UPDATE SET + [t].[FirstName] = [s].[FirstName] + ,[t].[LastName] = [s].[LastName] + ,[t].[PhoneNo] = [s].[PhoneNo] + ,[t].[RelationshipTypeCode] = [s].[RelationshipTypeCode] + WHEN NOT MATCHED BY TARGET + THEN INSERT ( + [EmployeeId] + ,[FirstName] + ,[LastName] + ,[PhoneNo] + ,[RelationshipTypeCode] + ) + VALUES ( + @EmployeeId + ,[s].[FirstName] + ,[s].[LastName] + ,[s].[PhoneNo] + ,[s].[RelationshipTypeCode] + ) + WHEN NOT MATCHED BY SOURCE + AND [t].[EmployeeId] = @EmployeeId + THEN DELETE; + + -- Commit the transaction. + COMMIT TRANSACTION + END TRY + BEGIN CATCH + -- Rollback transaction and rethrow error. + IF @@TRANCOUNT > 0 + ROLLBACK TRANSACTION; + + THROW; + END CATCH +END \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeCreate.sql b/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeCreate.sql new file mode 100644 index 000000000..88ca21b04 --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeCreate.sql @@ -0,0 +1,88 @@ +CREATE PROCEDURE [Hr].[spEmployeeCreate] + @EmployeeId AS UNIQUEIDENTIFIER = NULL OUTPUT + ,@Email AS NVARCHAR(250) NULL = NULL + ,@FirstName AS NVARCHAR(100) NULL = NULL + ,@LastName AS NVARCHAR(100) NULL = NULL + ,@GenderCode AS NVARCHAR(50) NULL = NULL + ,@Birthday AS DATE NULL = NULL + ,@StartDate AS DATE NULL = NULL + ,@TerminationDate AS DATE NULL = NULL + ,@TerminationReasonCode AS NVARCHAR(50) NULL = NULL + ,@PhoneNo AS NVARCHAR(50) NULL = NULL + ,@AddressJson AS NVARCHAR(500) NULL = NULL + ,@CreatedBy AS NVARCHAR(250) NULL = NULL + ,@CreatedDate AS DATETIME2 NULL = NULL + ,@EmergencyContactList AS [Hr].[udtEmergencyContactList] READONLY + ,@ReselectRecord AS BIT = 0 +AS +BEGIN + /* + * This file is automatically generated; any changes will be lost. + */ + + SET NOCOUNT ON; + + BEGIN TRY + -- Wrap in a transaction. + BEGIN TRANSACTION + + -- Set audit details. + EXEC @CreatedDate = fnGetTimestamp @CreatedDate + EXEC @CreatedBy = fnGetUsername @CreatedBy + + DECLARE @InsertedIdentity TABLE([EmployeeId] UNIQUEIDENTIFIER) + + -- Create the record. + INSERT INTO [Hr].[Employee] ( + [Email] + ,[FirstName] + ,[LastName] + ,[GenderCode] + ,[Birthday] + ,[StartDate] + ,[TerminationDate] + ,[TerminationReasonCode] + ,[PhoneNo] + ,[AddressJson] + ,[CreatedBy] + ,[CreatedDate] + ) + OUTPUT inserted.EmployeeId INTO @InsertedIdentity + VALUES ( + @Email + ,@FirstName + ,@LastName + ,@GenderCode + ,@Birthday + ,@StartDate + ,@TerminationDate + ,@TerminationReasonCode + ,@PhoneNo + ,@AddressJson + ,@CreatedBy + ,@CreatedDate + ) + + -- Get the inserted identity. + SELECT @EmployeeId = [EmployeeId] FROM @InsertedIdentity + + -- Execute additional statements. + EXEC [Hr].[spEmergencyContactMerge] @EmployeeId, @EmergencyContactList + + -- Commit the transaction. + COMMIT TRANSACTION + END TRY + BEGIN CATCH + -- Rollback transaction and rethrow error. + IF @@TRANCOUNT > 0 + ROLLBACK TRANSACTION; + + THROW; + END CATCH + + -- Reselect record. + IF @ReselectRecord = 1 + BEGIN + EXEC [Hr].[spEmployeeGet] @EmployeeId + END +END \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeDelete.sql b/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeDelete.sql new file mode 100644 index 000000000..174d294d4 --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeDelete.sql @@ -0,0 +1,31 @@ +CREATE PROCEDURE [Hr].[spEmployeeDelete] + @EmployeeId AS UNIQUEIDENTIFIER +AS +BEGIN + /* + * This is automatically generated; any changes will be lost. + */ + + BEGIN TRY + -- Wrap in a transaction. + BEGIN TRANSACTION + + -- Delete the record. + DELETE FROM [Hr].[Employee] + WHERE [EmployeeId] = @EmployeeId + + -- Execute additional statements. + DELETE FROM [Hr].[EmergencyContact] WHERE [EmployeeId] = @EmployeeId + DELETE FROM [Hr].[PerformanceReview] WHERE [EmployeeId] = @EmployeeId + + -- Commit the transaction. + COMMIT TRANSACTION + END TRY + BEGIN CATCH + -- Rollback transaction and rethrow error. + IF @@TRANCOUNT > 0 + ROLLBACK TRANSACTION; + + THROW; + END CATCH +END \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeGet.sql b/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeGet.sql new file mode 100644 index 000000000..b5a75fb1d --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeGet.sql @@ -0,0 +1,33 @@ +CREATE PROCEDURE [Hr].[spEmployeeGet] + @EmployeeId AS UNIQUEIDENTIFIER +AS +BEGIN + /* + * This is automatically generated; any changes will be lost. + */ + + SET NOCOUNT ON; + + SELECT + [e].[EmployeeId] + ,[e].[Email] + ,[e].[FirstName] + ,[e].[LastName] + ,[e].[GenderCode] + ,[e].[Birthday] + ,[e].[StartDate] + ,[e].[TerminationDate] + ,[e].[TerminationReasonCode] + ,[e].[PhoneNo] + ,[e].[AddressJson] + ,[e].[RowVersion] + ,[e].[CreatedBy] + ,[e].[CreatedDate] + ,[e].[UpdatedBy] + ,[e].[UpdatedDate] + FROM [Hr].[Employee] AS [e] + WHERE [e].[EmployeeId] = @EmployeeId + + -- Execute additional statements. + EXEC [Hr].[spEmergencyContactGetByEmployeeId] @EmployeeId +END \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeUpdate.sql b/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeUpdate.sql new file mode 100644 index 000000000..5b577fa55 --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Schema/Hr/Stored Procedures/Generated/spEmployeeUpdate.sql @@ -0,0 +1,83 @@ +CREATE PROCEDURE [Hr].[spEmployeeUpdate] + @EmployeeId AS UNIQUEIDENTIFIER + ,@Email AS NVARCHAR(250) NULL = NULL + ,@FirstName AS NVARCHAR(100) NULL = NULL + ,@LastName AS NVARCHAR(100) NULL = NULL + ,@GenderCode AS NVARCHAR(50) NULL = NULL + ,@Birthday AS DATE NULL = NULL + ,@StartDate AS DATE NULL = NULL + ,@TerminationDate AS DATE NULL = NULL + ,@TerminationReasonCode AS NVARCHAR(50) NULL = NULL + ,@PhoneNo AS NVARCHAR(50) NULL = NULL + ,@AddressJson AS NVARCHAR(500) NULL = NULL + ,@RowVersion AS TIMESTAMP + ,@UpdatedBy AS NVARCHAR(250) NULL = NULL + ,@UpdatedDate AS DATETIME2 NULL = NULL + ,@EmergencyContactList AS [Hr].[udtEmergencyContactList] READONLY + ,@ReselectRecord AS BIT = 0 +AS +BEGIN + /* + * This file is automatically generated; any changes will be lost. + */ + + SET NOCOUNT ON; + + BEGIN TRY + -- Wrap in a transaction. + BEGIN TRANSACTION + + -- Set audit details, etc. + EXEC @UpdatedDate = fnGetTimestamp @UpdatedDate + EXEC @UpdatedBy = fnGetUsername @UpdatedBy + + -- Check exists. + DECLARE @PrevRowVersion BINARY(8) + SET @PrevRowVersion = (SELECT TOP 1 x.[RowVersion] FROM [Hr].[Employee] AS x WHERE x.[EmployeeId] = @EmployeeId) + IF @PrevRowVersion IS NULL + BEGIN + EXEC spThrowNotFoundException + END + + -- Check concurrency (where provided). + IF @RowVersion IS NULL OR @PrevRowVersion <> @RowVersion + BEGIN + EXEC spThrowConcurrencyException + END + + -- Update the record. + UPDATE [Hr].[Employee] SET + [Email] = @Email + ,[FirstName] = @FirstName + ,[LastName] = @LastName + ,[GenderCode] = @GenderCode + ,[Birthday] = @Birthday + ,[StartDate] = @StartDate + ,[TerminationDate] = @TerminationDate + ,[TerminationReasonCode] = @TerminationReasonCode + ,[PhoneNo] = @PhoneNo + ,[AddressJson] = @AddressJson + ,[UpdatedBy] = @UpdatedBy + ,[UpdatedDate] = @UpdatedDate + WHERE [EmployeeId] = @EmployeeId + + -- Execute additional statements. + EXEC [Hr].[spEmergencyContactMerge] @EmployeeId, @EmergencyContactList + + -- Commit the transaction. + COMMIT TRANSACTION + END TRY + BEGIN CATCH + -- Rollback transaction and rethrow error. + IF @@TRANCOUNT > 0 + ROLLBACK TRANSACTION; + + THROW; + END CATCH + + -- Reselect record. + IF @ReselectRecord = 1 + BEGIN + EXEC [Hr].[spEmployeeGet] @EmployeeId + END +END \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Database/Schema/Hr/Types/User-Defined Table Types/Generated/udtEmergencyContactList.sql b/samples/My.Hr/My.Hr.Database/Schema/Hr/Types/User-Defined Table Types/Generated/udtEmergencyContactList.sql new file mode 100644 index 000000000..7411e12c7 --- /dev/null +++ b/samples/My.Hr/My.Hr.Database/Schema/Hr/Types/User-Defined Table Types/Generated/udtEmergencyContactList.sql @@ -0,0 +1,11 @@ +CREATE TYPE [Hr].[udtEmergencyContactList] AS TABLE ( + /* + * This is automatically generated; any changes will be lost. + */ + + [EmergencyContactId] UNIQUEIDENTIFIER NULL + ,[FirstName] NVARCHAR(100) NULL + ,[LastName] NVARCHAR(100) NULL + ,[PhoneNo] NVARCHAR(50) NULL + ,[RelationshipTypeCode] NVARCHAR(50) NULL +) \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Test/Data/Data.yaml b/samples/My.Hr/My.Hr.Test/Data/Data.yaml new file mode 100644 index 000000000..13cb11910 --- /dev/null +++ b/samples/My.Hr/My.Hr.Test/Data/Data.yaml @@ -0,0 +1,10 @@ +Hr: + - Employee: + - { EmployeeId: 1, Email: w.jones@org.com, FirstName: Wendy, LastName: Jones, GenderCode: F, Birthday: 1985-03-18, StartDate: 2000-12-11, PhoneNo: (425) 612 8113 } + - { EmployeeId: 2, Email: b.smith@org.com, FirstName: Brian, LastName: Smith, GenderCode: M, Birthday: 1994-11-07, StartDate: 2013-08-06, TerminationDate: 2015-04-08, TerminationReasonCode: RE, PhoneNo: (429) 120 0098 } + - { EmployeeId: 3, Email: r.Browne@org.com, FirstName: Rachael, LastName: Browne, GenderCode: F, Birthday: 1972-06-28, StartDate: 2019-11-06, PhoneNo: (421) 783 2343 } + - { EmployeeId: 4, Email: w.smither@org.com, FirstName: Waylon, LastName: Smithers, GenderCode: M, Birthday: 1952-02-21, StartDate: 2001-01-22, PhoneNo: (428) 893 2793, AddressJson: '{ "street1": "8365 851 PL NE", "city": "Redmond", "state": "WA", "postCode": "98052" }' } + - EmergencyContact: + - { EmergencyContactId: 201, EmployeeId: 2, FirstName: Garth, LastName: Smith, PhoneNo: (443) 678 1827, RelationshipTypeCode: PAR } + - { EmergencyContactId: 202, EmployeeId: 2, FirstName: Sarah, LastName: Smith, PhoneNo: (443) 234 3837, RelationshipTypeCode: PAR } + - { EmergencyContactId: 401, EmployeeId: 4, FirstName: Michael, LastName: Manners, PhoneNo: (234) 297 9834, RelationshipTypeCode: FRD } \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Test/Data/PerfReview.yaml b/samples/My.Hr/My.Hr.Test/Data/PerfReview.yaml new file mode 100644 index 000000000..c62953d0a --- /dev/null +++ b/samples/My.Hr/My.Hr.Test/Data/PerfReview.yaml @@ -0,0 +1,5 @@ +Hr: + - PerformanceReview: + - { PerformanceReviewId: 1, EmployeeId: 2, Date: 2014-03-15, PerformanceOutcomeCode: DN, Reviewer: r.Browne@org.com, Notes: Work quality low. } + - { PerformanceReviewId: 2, EmployeeId: 1, Date: 2016-11-12, PerformanceOutcomeCode: EE, Reviewer: r.Browne@org.com, Notes: They are awesome! } + - { PerformanceReviewId: 3, EmployeeId: 2, Date: 2014-01-15, PerformanceOutcomeCode: DN, Reviewer: r.Browne@org.com, Notes: Work quality below standard. } \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Test/EmployeeTest.cs b/samples/My.Hr/My.Hr.Test/EmployeeTest.cs new file mode 100644 index 000000000..beccc3e9b --- /dev/null +++ b/samples/My.Hr/My.Hr.Test/EmployeeTest.cs @@ -0,0 +1,529 @@ +using Beef; +using Beef.Entities; +using Beef.Test.NUnit; +using Beef.WebApi; +using My.Hr.Api; +using My.Hr.Common.Agents; +using My.Hr.Common.Entities; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; + +namespace My.Hr.Test +{ + [TestFixture, NonParallelizable] + public class EmployeeTest : UsingAgentTesterServer + { + #region Get + + [Test, TestSetUp] + public void A110_Get_NotFound() + { + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.NotFound) + .Run(a => a.GetAsync(404.ToGuid())); + } + + [Test, TestSetUp] + public void A120_Get_Found_NoAddress() + { + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .IgnoreChangeLog() + .IgnoreETag() + .ExpectValue(_ => new Employee + { + Id = 1.ToGuid(), + Email = "w.jones@org.com", + FirstName = "Wendy", + LastName = "Jones", + GenderSid = "F", + Birthday = new DateTime(1985, 03, 18), + StartDate = new DateTime(2000, 12, 11), + PhoneNo = "(425) 612 8113" + }) + .Run(a => a.GetAsync(1.ToGuid())); + } + + [Test, TestSetUp] + public void A120_Get_Found_WithAddress() + { + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .IgnoreChangeLog() + .IgnoreETag() + .ExpectValue(_ => new Employee + { + Id = 4.ToGuid(), + Email = "w.smither@org.com", + FirstName = "Waylon", + LastName = "Smithers", + GenderSid = "M", + Birthday = new DateTime(1952, 02, 21), + StartDate = new DateTime(2001, 01, 22), + PhoneNo = "(428) 893 2793", + Address = new Address { Street1 = "8365 851 PL NE", City = "Redmond", StateSid = "WA", PostCode = "98052" }, + EmergencyContacts = new EmergencyContactCollection { new EmergencyContact { Id = 401.ToGuid(), FirstName = "Michael", LastName = "Manners", PhoneNo = "(234) 297 9834", RelationshipSid = "FRD" } } + }) + .Run(a => a.GetAsync(4.ToGuid())); + } + + [Test, TestSetUp] + public void A120_Get_Modified_NotModified() + { + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetAsync(1.ToGuid(), new WebApiRequestOptions { ETag = TestSetUp.ConcurrencyErrorETag })).Value!; + + Assert.NotNull(v); + + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.NotModified) + .Run(a => a.GetAsync(1.ToGuid(), new WebApiRequestOptions { ETag = v.ETag })); + } + + #endregion + + #region GetByArgs + + [Test, TestSetUp] + public void A210_GetByArgs_All() + { + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetByArgsAsync(null)).Value; + + Assert.IsNotNull(v); + Assert.IsNotNull(v.Result); + Assert.AreEqual(3, v.Result.Count); + Assert.AreEqual(new string[] { "Browne", "Jones", "Smithers" }, v.Result.Select(x => x.LastName).ToArray()); + } + + [Test, TestSetUp] + public void A210_GetByArgs_All_Paging() + { + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetByArgsAsync(new EmployeeArgs { IsIncludeTerminated = true }, PagingArgs.CreateSkipAndTake(1,2))).Value; + + Assert.IsNotNull(v); + Assert.IsNotNull(v.Result); + Assert.AreEqual(2, v.Result.Count); + Assert.AreEqual(new string[] { "Jones", "Smith" }, v.Result.Select(x => x.LastName).ToArray()); + } + + [Test, TestSetUp] + public void A220_GetByArgs_FirstName() + { + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetByArgsAsync(new EmployeeArgs { FirstName = "*a*" })).Value; + + Assert.IsNotNull(v); + Assert.IsNotNull(v.Result); + Assert.AreEqual(2, v.Result.Count); + Assert.AreEqual(new string[] { "Browne", "Smithers" }, v.Result.Select(x => x.LastName).ToArray()); + } + + [Test, TestSetUp] + public void A230_GetByArgs_LastName() + { + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetByArgsAsync(new EmployeeArgs { LastName = "s*" })).Value; + + Assert.IsNotNull(v); + Assert.IsNotNull(v.Result); + Assert.AreEqual(1, v.Result.Count); + Assert.AreEqual(new string[] { "Smithers" }, v.Result.Select(x => x.LastName).ToArray()); + } + + [Test, TestSetUp] + public void A230_GetByArgs_LastName_IncludeTerminated() + { + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetByArgsAsync(new EmployeeArgs { LastName = "s*", IsIncludeTerminated = true })).Value; + + Assert.IsNotNull(v); + Assert.IsNotNull(v.Result); + Assert.AreEqual(2, v.Result.Count); + Assert.AreEqual(new string[] { "Smith", "Smithers" }, v.Result.Select(x => x.LastName).ToArray()); + } + + [Test, TestSetUp] + public void A240_GetByArgs_Gender() + { + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetByArgsAsync(new EmployeeArgs { GendersSids = new List { "F" } })).Value; + + Assert.IsNotNull(v); + Assert.IsNotNull(v.Result); + Assert.AreEqual(2, v.Result.Count); + Assert.AreEqual(new string[] { "Browne", "Jones" }, v.Result.Select(x => x.LastName).ToArray()); + } + + [Test, TestSetUp] + public void A250_GetByArgs_Empty() + { + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetByArgsAsync(new EmployeeArgs { LastName = "s*", FirstName = "b*", GendersSids = new List { "F" } })).Value; + + Assert.IsNotNull(v); + Assert.IsNotNull(v.Result); + Assert.AreEqual(0, v.Result.Count); + } + + [Test, TestSetUp] + public void A260_GetByArgs_FieldSelection() + { + var r = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetByArgsAsync(new EmployeeArgs { GendersSids = new List { "F" } }, requestOptions: new WebApiRequestOptions().Include("firstname", "lastname"))); + + Assert.IsNotNull(r.Value); + Assert.IsNotNull(r.Value.Result); + Assert.AreEqual(2, r.Value.Result.Count); + Assert.AreEqual(new string[] { "Browne", "Jones" }, r.Value.Result.Select(x => x.LastName).ToArray()); + + Assert.AreEqual("[{\"firstName\":\"Rachael\",\"lastName\":\"Browne\"},{\"firstName\":\"Wendy\",\"lastName\":\"Jones\"}]", r.Content); + } + + [Test, TestSetUp] + public void A270_GetByArgs_RefDataText() + { + var r = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetByArgsAsync(new EmployeeArgs { GendersSids = new List { "F" } }, requestOptions: new WebApiRequestOptions { IncludeRefDataText = true })); + + Assert.IsNotNull(r.Value); + Assert.IsNotNull(r.Value.Result); + Assert.AreEqual(2, r.Value.Result.Count); + Assert.AreEqual(new string[] { "Browne", "Jones" }, r.Value.Result.Select(x => x.LastName).ToArray()); + + Assert.AreEqual(2, Newtonsoft.Json.Linq.JArray.Parse(r.Content!).Descendants().OfType().Where(p => p.Name == "genderText").Count()); + } + + #endregion + + #region Create + + [Test, TestSetUp] + public void B110_Create() + { + var v = new Employee + { + Email = "j.smith@org.com", + FirstName = "Jill", + LastName = "Smith", + GenderSid = "F", + Birthday = new DateTime(1955, 10, 28), + StartDate = DateTime.Today, + PhoneNo = "(456) 789 0123", + Address = new Address { Street1 = "2732 85 PL NE", City = "Bellevue", StateSid = "WA", PostCode = "98101" }, + EmergencyContacts = new EmergencyContactCollection { new EmergencyContact { FirstName = "Danny", LastName = "Keen", PhoneNo = "(234) 297 9834", RelationshipSid = "FRD" } } + }; + + // Create value. + v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.Created) + .ExpectChangeLogCreated() + .ExpectETag() + .ExpectUniqueKey() + .ExpectValue(_ => v) + .ExpectEvent("My.Hr.Employee.*", "Created") + .Run(a => a.CreateAsync(v)).Value!; + + // Check the value was created properly. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .ExpectValue(_ => v) + .Run(a => a.GetAsync(v.Id)); + } + + [Test, TestSetUp] + public void B120_Create_Duplicate() + { + var v = new Employee + { + Email = "w.jones@org.com", + FirstName = "Wendy", + LastName = "Jones", + GenderSid = "F", + Birthday = new DateTime(1985, 03, 18), + StartDate = new DateTime(2000, 12, 11), + PhoneNo = "(425) 612 8113" + }; + + // Create value. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.Conflict) + .Run(a => a.CreateAsync(v)); + } + + #endregion + + #region Update + + [Test, TestSetUp] + public void C110_Update_NotFound() + { + // Get an existing value. + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetAsync(4.ToGuid())).Value!; + + // Try updating with an invalid identifier. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.NotFound) + .Run(a => a.UpdateAsync(v, 404.ToGuid())); + } + + [Test, TestSetUp] + public void C120_Update_Concurrency() + { + // Get an existing value. + var id = 4.ToGuid(); + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetAsync(id)).Value!; + + // Try updating the value with an invalid eTag (if-match). + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.PreconditionFailed) + .Run(a => a.UpdateAsync(v, id, new WebApiRequestOptions { ETag = TestSetUp.ConcurrencyErrorETag })); + + // Try updating the value with an invalid eTag. + v.ETag = TestSetUp.ConcurrencyErrorETag; + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.PreconditionFailed) + .Run(a => a.UpdateAsync(v, id)); + } + + [Test, TestSetUp] + public void C130_Update() + { + // Get an existing value. + var id = 4.ToGuid(); + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetAsync(id)).Value!; + + // Make some changes to the data. + v.FirstName += "X"; + v.LastName += "Y"; + v.Address!.Street2 = "Street 2"; + v.EmergencyContacts![0].FirstName += "Y"; + v.EmergencyContacts.Add(new EmergencyContact { FirstName = "Danny", LastName = "Keen", PhoneNo = "(234) 297 9834", RelationshipSid = "FRD" }); + + // Update the value. + v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .ExpectChangeLogUpdated() + .ExpectETag(v.ETag) + .ExpectUniqueKey() + .ExpectValue(_ => v) + .ExpectEvent($"My.Hr.Employee.{id}", "Updated") + .Run(a => a.UpdateAsync(v, id)).Value!; + + // Check the value was updated properly. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .ExpectValue(_ => v) + .Run(a => a.GetAsync(id)); + } + + [Test, TestSetUp] + public void C140_Update_AlreadyTerminated() + { + // Get an existing value. + var id = 2.ToGuid(); + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetAsync(id)).Value!; + + // Make some changes to the data. + v.FirstName += "X"; + v.LastName += "Y"; + + // Update the value. + var r = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.BadRequest) + .Run(a => a.UpdateAsync(v, id)); + + Assert.AreEqual("Once an Employee has been Terminated the data can no longer be updated.", r.Content); + } + + #endregion + + #region Patch + + [Test, TestSetUp] + public void D110_Patch_NotFound() + { + // Get an existing value. + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetAsync(4.ToGuid())).Value!; + + // Try patching with an invalid identifier. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.NotFound) + .Run(a => a.PatchAsync(WebApiPatchOption.MergePatch, "{ \"lastName\": \"Smithers\" }", 404.ToGuid())); + } + + [Test, TestSetUp] + public void D120_Patch_Concurrency() + { + // Get an existing value. + var id = 4.ToGuid(); + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetAsync(id)).Value!; + + // Try updating the value with an invalid eTag (if-match). + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.PreconditionFailed) + .Run(a => a.PatchAsync(WebApiPatchOption.MergePatch, "{ \"lastName\": \"Smithers\" }", id, new WebApiRequestOptions { ETag = TestSetUp.ConcurrencyErrorETag })); + + // Try updating the value with an eTag header (json payload eTag is ignored). + v.ETag = TestSetUp.ConcurrencyErrorETag; + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.PreconditionFailed) + .Run(a => a.PatchAsync(WebApiPatchOption.MergePatch, "{{ \"lastName\": \"Smithers\", \"etag\": {TestSetUp.ConcurrencyErrorETag} }}", id)); + } + + [Test, TestSetUp] + public void D130_Patch() + { + // Get an existing value. + var id = 4.ToGuid(); + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetAsync(id)).Value!; + + // Make some changes to the data. + v.LastName = "Bartholomew"; + v.EmergencyContacts = null; + + // Update the value. + v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .ExpectChangeLogUpdated() + .ExpectETag(v.ETag) + .ExpectUniqueKey() + .ExpectValue(_ => v) + .ExpectEvent($"My.Hr.Employee.{id}", "Updated") + .Run(a => a.PatchAsync(WebApiPatchOption.MergePatch, $"{{ \"lastName\": \"{v.LastName}\", \"emergencyContacts\": null }}", id, new WebApiRequestOptions { ETag = v.ETag })).Value; + + // Check the value was updated properly. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .ExpectValue(_ => v) + .Run(a => a.GetAsync(id)); + } + + #endregion + + #region Delete + + [Test, TestSetUp] + public void E110_Delete() + { + var v = new Employee + { + Email = "j.jones@org.com", + FirstName = "Jarrod", + LastName = "Jones", + GenderSid = "M", + Birthday = new DateTime(1928, 10, 28), + StartDate = DateTime.UtcNow.AddDays(1), + PhoneNo = "(456) 789 0123", + Address = new Address { Street1 = "2732 85 PL NE", City = "Bellevue", StateSid = "WA", PostCode = "98101" }, + EmergencyContacts = new EmergencyContactCollection { new EmergencyContact { FirstName = "Danny", LastName = "Keen", PhoneNo = "(234) 297 9834", RelationshipSid = "FRD" } } + }; + + // Create an employee in the future. + v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.Created) + .ExpectEvent("My.Hr.Employee.*", "Created") + .Run(a => a.CreateAsync(v)).Value; + + // Delete value. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.NoContent) + .ExpectEvent($"My.Hr.Employee.{v.Id}", "Deleted") + .Run(a => a.DeleteAsync(v.Id)); + + // Check value no longer exists. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.NotFound) + .Run(a => a.GetAsync(v.Id)); + + // Delete again (should still be successful as a Delete is idempotent); note there should be no corresponding event as nothing actually happened. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.NoContent) + .Run(a => a.DeleteAsync(v.Id)); + } + + #endregion + + #region Terminate + + [Test, TestSetUp] + public void F110_Terminate_NotFound() + { + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.NotFound) + .Run(a => a.TerminateAsync(new TerminationDetail { Date = DateTime.Now, ReasonSid = "RD" }, 404.ToGuid())); + } + + [Test, TestSetUp] + public void F120_Terminate_MoreThanOnce() + { + var r = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.BadRequest) + .ExpectErrorType(ErrorType.ValidationError, "An Employee can not be terminated more than once.") + .Run(a => a.TerminateAsync(new TerminationDetail { Date = DateTime.Now, ReasonSid = "RD" }, 2.ToGuid())); + } + + [Test, TestSetUp] + public void F130_Terminate_BeforeStart() + { + var r = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.BadRequest) + .ExpectErrorType(ErrorType.ValidationError, "An Employee can not be terminated prior to their start date.") + .Run(a => a.TerminateAsync(new TerminationDetail { Date = new DateTime(1999, 12, 31), ReasonSid = "RD" }, 1.ToGuid())); + } + + [Test, TestSetUp] + public void F140_Terminate() + { + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetAsync(1.ToGuid())).Value!; + + v.Termination = new TerminationDetail { Date = DateTime.Now, ReasonSid = "RD" }; + + v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .ExpectChangeLogUpdated() + .ExpectETag(v.ETag) + .ExpectUniqueKey() + .ExpectValue(_ => v) + .ExpectEvent($"My.Hr.Employee.{v.Id}", "Terminated") + .Run(a => a.TerminateAsync(v.Termination, 1.ToGuid())).Value!; + + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .ExpectValue(_ => v) + .Run(a => a.GetAsync(1.ToGuid())); + } + + #endregion + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Test/FixtureSetup.cs b/samples/My.Hr/My.Hr.Test/FixtureSetup.cs new file mode 100644 index 000000000..f57fcb148 --- /dev/null +++ b/samples/My.Hr/My.Hr.Test/FixtureSetup.cs @@ -0,0 +1,31 @@ +using Beef.Database.Core; +using Beef.Test.NUnit; +using NUnit.Framework; +using System.Reflection; +using System.Threading.Tasks; +using My.Hr.Api; +using My.Hr.Common.Agents; +using My.Hr.Common.Entities; + +namespace My.Hr.Test +{ + [SetUpFixture] + public class FixtureSetUp + { + [OneTimeSetUp] + public void OneTimeSetUp() + { + TestSetUp.DefaultEnvironmentVariablePrefix = "Hr"; + TestSetUp.SetDefaultLocalReferenceData(); + TestSetUp.DefaultExpectNoEvents = true; + var config = AgentTester.BuildConfiguration(); + + TestSetUp.RegisterSetUp(async (count, _) => + { + return await DatabaseExecutor.RunAsync(new DatabaseExecutorArgs( + count == 0 ? DatabaseExecutorCommand.ResetAndDatabase : DatabaseExecutorCommand.ResetAndData, config["ConnectionStrings:Database"], + typeof(Database.Program).Assembly, Assembly.GetExecutingAssembly()) { UseBeefDbo = true } ).ConfigureAwait(false) == 0; + }); + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Test/My.Hr.Test.csproj b/samples/My.Hr/My.Hr.Test/My.Hr.Test.csproj new file mode 100644 index 000000000..3ca18324a --- /dev/null +++ b/samples/My.Hr/My.Hr.Test/My.Hr.Test.csproj @@ -0,0 +1,38 @@ + + + + netcoreapp3.1 + false + enable + + + + 1701;1702;CA1707 + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Test/PerformanceReviewTest.cs b/samples/My.Hr/My.Hr.Test/PerformanceReviewTest.cs new file mode 100644 index 000000000..3e281aaf5 --- /dev/null +++ b/samples/My.Hr/My.Hr.Test/PerformanceReviewTest.cs @@ -0,0 +1,226 @@ +using Beef; +using Beef.Entities; +using Beef.Test.NUnit; +using Beef.WebApi; +using My.Hr.Api; +using My.Hr.Business.Validation; +using My.Hr.Common.Agents; +using My.Hr.Common.Entities; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; + +namespace My.Hr.Test +{ + [TestFixture, NonParallelizable] + public class PerformanceReviewTest : UsingAgentTesterServer + { + #region Get + + [Test, TestSetUp] + public void A110_Get_NotFound() + { + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.NotFound) + .Run(a => a.GetAsync(404.ToGuid())); + } + + [Test, TestSetUp] + public void A110_Get() + { + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .IgnoreChangeLog() + .IgnoreETag() + .ExpectValue(_ => new PerformanceReview + { + Id = 2.ToGuid(), + EmployeeId = 1.ToGuid(), + Date = new DateTime(2016, 11, 12), + OutcomeSid = "EE", + Reviewer = "r.Browne@org.com", + Notes = "They are awesome!" + }) + .Run(a => a.GetAsync(2.ToGuid())); + } + + #endregion + + #region GetByEmployeeId + + [Test, TestSetUp] + public void A210_GetByEmployeeId_NotFound() + { + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetByEmployeeIdAsync(4.ToGuid())).Value!; + + Assert.IsNotNull(v); + Assert.IsNotNull(v.Result); + Assert.AreEqual(0, v.Result.Count); + } + + [Test, TestSetUp] + public void A220_GetByEmployeeId() + { + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetByEmployeeIdAsync(2.ToGuid())).Value!; + + Assert.IsNotNull(v); + Assert.IsNotNull(v.Result); + Assert.AreEqual(2, v.Result.Count); + Assert.AreEqual(new string[] { "Work quality low.", "Work quality below standard." }, v.Result.Select(x => x.Notes).ToArray()); + } + + [Test, TestSetUp] + public void A220_GetByEmployeeId_Last() + { + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetByEmployeeIdAsync(2.ToGuid(), PagingArgs.CreateSkipAndTake(0, 1))).Value!; + + Assert.IsNotNull(v); + Assert.IsNotNull(v.Result); + Assert.AreEqual(1, v.Result.Count); + Assert.AreEqual(new string[] { "Work quality low." }, v.Result.Select(x => x.Notes).ToArray()); + } + + #endregion + + #region Create + + [Test, TestSetUp] + public void B110_Create() + { + var v = new PerformanceReview + { + Date = new DateTime(2020, 06, 15), + OutcomeSid = "ME", + Notes = "Solid performance :-)", + Reviewer = "the.boss@org.com", + }; + + // Create value. + v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.Created) + .ExpectChangeLogCreated() + .ExpectETag() + .ExpectUniqueKey() + .ExpectValue(_ => v, "EmployeeId") + .ExpectEvent("My.Hr.PerformanceReview.*", "Created") + .Run(a => a.CreateAsync(v, 3.ToGuid())).Value!; + + Assert.AreEqual(3.ToGuid(), v.EmployeeId); + + // Check the value was created properly. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .ExpectValue(_ => v) + .Run(a => a.GetAsync(v.Id)); + } + + #endregion + + #region Update + + [Test, TestSetUp] + public void C110_Update_NotFound() + { + // Get an existing value. + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetAsync(3.ToGuid())).Value!; + + // Try updating with an invalid identifier. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.NotFound) + .Run(a => a.UpdateAsync(v, 404.ToGuid())); + } + + [Test, TestSetUp] + public void C120_Update_Concurrency() + { + // Get an existing value. + var id = 3.ToGuid(); + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetAsync(id)).Value!; + + // Try updating the value with an invalid eTag (if-match). + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.PreconditionFailed) + .Run(a => a.UpdateAsync(v, id, new WebApiRequestOptions { ETag = TestSetUp.ConcurrencyErrorETag })); + + // Try updating the value with an invalid eTag. + v.ETag = TestSetUp.ConcurrencyErrorETag; + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.PreconditionFailed) + .Run(a => a.UpdateAsync(v, id)); + } + + [Test, TestSetUp] + public void C130_Update() + { + // Get an existing value. + var id = 3.ToGuid(); + var v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetAsync(id)).Value!; + + // Make some changes to the data. + v.Notes += "X"; + + // Update the value. + v = AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .ExpectChangeLogUpdated() + .ExpectETag(v.ETag) + .ExpectUniqueKey() + .ExpectValue(_ => v) + .ExpectEvent($"My.Hr.PerformanceReview.{id}", "Updated") + .Run(a => a.UpdateAsync(v, id)).Value!; + + // Check the value was updated properly. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .ExpectValue(_ => v) + .Run(a => a.GetAsync(id)); + } + + #endregion + + #region Delete + + [Test, TestSetUp] + public void E110_Delete() + { + var id = 3.ToGuid(); + + // Get an existing value. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.OK) + .Run(a => a.GetAsync(id)); + + // Delete value. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.NoContent) + .ExpectEvent($"My.Hr.PerformanceReview.{id}", "Deleted") + .Run(a => a.DeleteAsync(id)); + + // Check value no longer exists. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.NotFound) + .Run(a => a.GetAsync(id)); + + // Delete again (should still be successful as a Delete is idempotent); note there should be no corresponding event as nothing actually happened. + AgentTester.Test() + .ExpectStatusCode(HttpStatusCode.NoContent) + .Run(a => a.DeleteAsync(id)); + } + + #endregion + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Test/Validators/EmployeeValidatorTest.cs b/samples/My.Hr/My.Hr.Test/Validators/EmployeeValidatorTest.cs new file mode 100644 index 000000000..d1c293468 --- /dev/null +++ b/samples/My.Hr/My.Hr.Test/Validators/EmployeeValidatorTest.cs @@ -0,0 +1,231 @@ +using Beef.Test.NUnit; +using Moq; +using My.Hr.Business.DataSvc; +using My.Hr.Business.Validation; +using My.Hr.Common.Agents; +using My.Hr.Common.Entities; +using NUnit.Framework; +using System; + +namespace My.Hr.Test.Validators +{ + [TestFixture] + public class EmployeeValidatorTest + { + private readonly Mock _referenceData = new Mock(); + + [OneTimeSetUp] + public void OneTimeSetUp() + { + _referenceData.Setup(x => x.GenderGetAllAsync(null, null)).ReturnsWebApiAgentResultAsync(new GenderCollection { new Gender { Id = Guid.NewGuid(), Code = "F" } }); + _referenceData.Setup(x => x.USStateGetAllAsync(null, null)).ReturnsWebApiAgentResultAsync(new USStateCollection { new USState { Id = Guid.NewGuid(), Code = "WA" } }); + _referenceData.Setup(x => x.RelationshipTypeGetAllAsync(null, null)).ReturnsWebApiAgentResultAsync(new RelationshipTypeCollection { new RelationshipType { Id = Guid.NewGuid(), Code = "FR" } }); + } + + private Employee ValidEmployee => new Employee + { + Email = "sarah.smith@org.com", + FirstName = "Sarah", + LastName = "Smith", + GenderSid = "F", + Birthday = DateTime.Now.AddYears(-20), + StartDate = new DateTime(2010, 01, 01), + PhoneNo = "(425) 333 4444", + }; + + [Test] + public void A110_Validate_Initial() + { + ValidationTester.Test() + .ExpectMessages( + "First Name is required.", + "Email is required.", + "Last Name is required.", + "Gender is required.", + "Birthday is required.", + "Start Date is required.", + "Phone No is required.") + .Run(() => EmployeeValidator.Default.Validate(new Employee())); + } + + [Test] + public void A120_Validate_BadData() + { + var e = new Employee + { + Email = "xxx", + FirstName = 'x'.ToLongString(), + LastName = 'x'.ToLongString(), + GenderSid = "X", + Birthday = DateTime.Now.AddYears(10), + StartDate = new DateTime(1996, 12, 31), + PhoneNo = "(425) 333 4444" + }; + + ValidationTester.Test() + .AddScopedService(_referenceData) + .ExpectMessages( + "Email is invalid.", + "First Name must not exceed 100 characters in length.", + "Last Name must not exceed 100 characters in length.", + "Gender is invalid.", + "Birthday is invalid as the Employee must be at least 18 years of age.", + "Start Date must be greater than or equal to January 1, 1999.") + .Run(() => EmployeeValidator.Default.Validate(e)); + } + + [Test] + public void A130_Validate_Address_Empty() + { + var e = (Employee)ValidEmployee.Clone(); + e.Address = new Address(); + + ValidationTester.Test() + .AddScopedService(_referenceData) + .ExpectMessages( + "Street1 is required.", + "City is required.", + "State is required.", + "Post Code is required.") + .Run(() => EmployeeValidator.Default.Validate(e)); + } + + [Test] + public void A130_Validate_Address_Invalid() + { + var e = (Employee)ValidEmployee.Clone(); + e.Address = new Address + { + Street1 = "8365 Rode Road", + City = "Redmond", + StateSid = "FR", + PostCode = "XXXXXXXXXX" + }; + + ValidationTester.Test() + .AddScopedService(_referenceData) + .ExpectMessages( + "State is invalid.", + "Post Code is invalid.") + .Run(() => EmployeeValidator.Default.Validate(e)); + } + + [Test] + public void A130_Validate_Address_OK() + { + var e = (Employee)ValidEmployee.Clone(); + e.Address = new Address + { + Street1 = "8365 Rode Road", + City = "Redmond", + StateSid = "WA", + PostCode = "98052" + }; + + ValidationTester.Test() + .AddScopedService(_referenceData) + .Run(() => EmployeeValidator.Default.Validate(e)); + } + + [Test] + public void A130_Validate_Contacts_Empty() + { + var e = (Employee)ValidEmployee.Clone(); + e.EmergencyContacts = new EmergencyContactCollection { new EmergencyContact() }; + + ValidationTester.Test() + .AddScopedService(_referenceData) + .ExpectMessages( + "First Name is required.", + "Last Name is required.", + "Phone No is required.", + "Relationship is required.") + .Run(() => EmployeeValidator.Default.Validate(e)); + } + + [Test] + public void A130_Validate_Contacts_Invalid() + { + var e = (Employee)ValidEmployee.Clone(); + e.EmergencyContacts = new EmergencyContactCollection + { + new EmergencyContact + { + FirstName = "Brian", + LastName = "Bellows", + PhoneNo = "425 333 4445", + RelationshipSid = "XX" + } + }; + + ValidationTester.Test() + .AddScopedService(_referenceData) + .ExpectMessages("Relationship is invalid.") + .Run(() => EmployeeValidator.Default.Validate(e)); + } + + [Test] + public void A130_Validate_Contacts_TooMany() + { + var e = (Employee)ValidEmployee.Clone(); + e.EmergencyContacts = new EmergencyContactCollection(); + + for (int i = 0; i < 6; i++) + { + e.EmergencyContacts.Add(new EmergencyContact + { + FirstName = "Brian", + LastName = "Bellows", + PhoneNo = "425 333 4445", + RelationshipSid = "FR" + }); + } + + ValidationTester.Test() + .AddScopedService(_referenceData) + .ExpectMessages("Emergency Contacts must not exceed 5 item(s).") + .Run(() => EmployeeValidator.Default.Validate(e)); + } + + [Test] + public void A130_Validate_UpdateTerminated() + { + var e = (Employee)ValidEmployee.Clone(); + e.Id = 1.ToGuid(); + + var eds = new Mock(); + eds.Setup(x => x.GetAsync(1.ToGuid())).ReturnsAsync(new Employee { Termination = new TerminationDetail { Date = DateTime.UtcNow } }); + + ValidationTester.Test() + .OperationType(Beef.OperationType.Update) + .AddScopedService(_referenceData) + .AddScopedService(eds) + .ExpectErrorType(Beef.ErrorType.ValidationError, "Once an Employee has been Terminated the data can no longer be updated.") + .Run(() => EmployeeValidator.Default.Validate(e)); + } + + [Test] + public void B110_CanDelete_NotFound() + { + var eds = new Mock(); + eds.Setup(x => x.GetAsync(1.ToGuid())).ReturnsAsync((Employee)null!); + + ValidationTester.Test() + .AddScopedService(eds) + .ExpectErrorType(Beef.ErrorType.NotFoundError) + .Run(() => EmployeeValidator.CanDelete.Validate(1.ToGuid())); + } + + [Test] + public void B110_CanDelete_Invalid() + { + var eds = new Mock(); + eds.Setup(x => x.GetAsync(1.ToGuid())).ReturnsAsync(new Employee { StartDate = DateTime.UtcNow.AddDays(-1) }); + + ValidationTester.Test() + .AddScopedService(eds) + .ExpectErrorType(Beef.ErrorType.ValidationError, "An employee cannot be deleted after they have started their employment.") + .Run(() => EmployeeValidator.CanDelete.Validate(1.ToGuid())); + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/My.Hr.Test/Validators/PerformanceReviewValidatorTest.cs b/samples/My.Hr/My.Hr.Test/Validators/PerformanceReviewValidatorTest.cs new file mode 100644 index 000000000..602e7ae67 --- /dev/null +++ b/samples/My.Hr/My.Hr.Test/Validators/PerformanceReviewValidatorTest.cs @@ -0,0 +1,192 @@ +using Beef.Test.NUnit; +using Moq; +using My.Hr.Business; +using My.Hr.Business.Validation; +using My.Hr.Common.Agents; +using My.Hr.Common.Entities; +using NUnit.Framework; +using System; + +namespace My.Hr.Test.Validators +{ + [TestFixture] + public class PerformanceReviewValidatorTest + { + private readonly Mock _referenceData = new Mock(); + private readonly Mock _employeeManager = new Mock(); + private readonly Mock _perfReviewManager = new Mock(); + + [OneTimeSetUp] + public void OneTimeSetUp() + { + _referenceData.Setup(x => x.PerformanceOutcomeGetAllAsync(null, null)).ReturnsWebApiAgentResultAsync(new PerformanceOutcomeCollection { new PerformanceOutcome { Id = Guid.NewGuid(), Code = "ME" } }); + + _employeeManager.Setup(x => x.GetAsync(404.ToGuid())).ReturnsAsync((Employee)null!); + _employeeManager.Setup(x => x.GetAsync(1.ToGuid())).ReturnsAsync(new Employee { Id = 1.ToGuid(), StartDate = DateTime.Now.AddYears(-1) }); + _employeeManager.Setup(x => x.GetAsync(2.ToGuid())).ReturnsAsync(new Employee { Id = 2.ToGuid(), StartDate = DateTime.Now.AddYears(-1), Termination = new TerminationDetail { Date = DateTime.Now.AddMonths(-1) } }); + + _perfReviewManager.Setup(x => x.GetAsync(1.ToGuid())).ReturnsAsync(new PerformanceReview { Id = 1.ToGuid(), EmployeeId = 2.ToGuid() }); + } + + [Test] + public void A110_Validate_Initial() + { + ValidationTester.Test() + .ExpectMessages( + "Employee is required.", + "Date is required.", + "Outcome is required.", + "Reviewer is required.") + .Run(() => PerformanceReviewValidator.Default.Validate(new PerformanceReview())); + } + + [Test] + public void A120_Validate_BadData() + { + var pr = new PerformanceReview + { + EmployeeId = 404.ToGuid(), + Date = DateTime.Now.AddDays(1), + OutcomeSid = "XX", + Reviewer = new string('X', 5000), + Notes = new string('X', 5000) + }; + + ValidationTester.Test() + .AddScopedService(_referenceData) + .AddScopedService(_employeeManager) + .ExpectMessages( + "Date must be less than or equal to today.", + "Outcome is invalid.", + "Employee is not found; a valid value is required.", + "Reviewer must not exceed 256 characters in length.", + "Notes must not exceed 4000 characters in length.") + .Run(() => PerformanceReviewValidator.Default.Validate(pr)); + } + + [Test] + public void A130_Validate_BeforeStarting() + { + var pr = new PerformanceReview + { + EmployeeId = 1.ToGuid(), + Date = DateTime.Now.AddYears(-2), + OutcomeSid = "ME", + Reviewer = "test@org.com", + Notes = "Thumbs up!" + }; + + ValidationTester.Test() + .AddScopedService(_referenceData) + .AddScopedService(_employeeManager) + .ExpectMessages("Date must not be prior to the Employee starting.") + .Run(() => PerformanceReviewValidator.Default.Validate(pr)); + } + + [Test] + public void A140_Validate_AfterTermination() + { + var pr = new PerformanceReview + { + EmployeeId = 2.ToGuid(), + Date = DateTime.Now, + OutcomeSid = "ME", + Reviewer = "test@org.com", + Notes = "Thumbs up!" + }; + + ValidationTester.Test() + .AddScopedService(_referenceData) + .AddScopedService(_employeeManager) + .ExpectMessages("Date must not be after the Employee has terminated.") + .Run(() => PerformanceReviewValidator.Default.Validate(pr)); + } + + [Test] + public void A150_Validate_EmployeeNotFound() + { + var pr = new PerformanceReview + { + Id = 404.ToGuid(), + EmployeeId = 2.ToGuid(), + Date = DateTime.Now.AddMonths(-3), + OutcomeSid = "ME", + Reviewer = "test@org.com", + Notes = "Thumbs up!" + }; + + // Need to set the OperationType to Update to exercise logic. + ValidationTester.Test() + .OperationType(Beef.OperationType.Update) + .AddScopedService(_referenceData) + .AddScopedService(_perfReviewManager) + .ExpectErrorType(Beef.ErrorType.NotFoundError) + .Run(() => PerformanceReviewValidator.Default.Validate(pr)); + } + + [Test] + public void A160_Validate_EmployeeImmutable() + { + var pr = new PerformanceReview + { + Id = 1.ToGuid(), + EmployeeId = 1.ToGuid(), + Date = DateTime.Now.AddMonths(-3), + OutcomeSid = "ME", + Reviewer = "test@org.com", + Notes = "Thumbs up!" + }; + + // Need to set the OperationType to Update to exercise logic. + ValidationTester.Test() + .OperationType(Beef.OperationType.Update) + .AddScopedService(_referenceData) + .AddScopedService(_perfReviewManager) + .ExpectMessages("Employee is not allowed to change; please reset value.") + .Run(() => PerformanceReviewValidator.Default.Validate(pr)); + } + + [Test] + public void A170_Validate_CreateOK() + { + var pr = new PerformanceReview + { + EmployeeId = 2.ToGuid(), + Date = DateTime.Now.AddMonths(-3), + OutcomeSid = "ME", + Reviewer = "test@org.com", + Notes = "Thumbs up!" + }; + + // Need to set the OperationType to Create to exercise logic. + ValidationTester.Test() + .OperationType(Beef.OperationType.Create) + .AddScopedService(_referenceData) + .AddScopedService(_employeeManager) + .AddScopedService(_perfReviewManager) + .Run(() => PerformanceReviewValidator.Default.Validate(pr)); + } + + [Test] + public void A180_Validate_UpdateOK() + { + var pr = new PerformanceReview + { + Id = 1.ToGuid(), + EmployeeId = 2.ToGuid(), + Date = DateTime.Now.AddMonths(-3), + OutcomeSid = "ME", + Reviewer = "test@org.com", + Notes = "Thumbs up!" + }; + + // Need to set the OperationType to Update to exercise logic. + ValidationTester.Test() + .OperationType(Beef.OperationType.Update) + .AddScopedService(_referenceData) + .AddScopedService(_employeeManager) + .AddScopedService(_perfReviewManager) + .Run(() => PerformanceReviewValidator.Default.Validate(pr)); + } + } +} \ No newline at end of file diff --git a/samples/My.Hr/README.md b/samples/My.Hr/README.md new file mode 100644 index 000000000..2976f331a --- /dev/null +++ b/samples/My.Hr/README.md @@ -0,0 +1,103 @@ +# My.Hr APIs + +The purpose of this sample is to demonstrate the usage of _Beef_ within the context of a fictitious Human Resources solution. The main intent is to show how _Beef_ can be used against a relational database (SQL Server) leveraging both direct ADO.NET (with stored procedures) and Entity Framework (EF) where applicable. + +Also, it will demonstrate how the data can be shaped differently between the database and the entity to leverage both relational and object-oriented constructs to provide a natural consuming experience from the API that accounts for the [object-relational impedence mismatch](https://en.wikipedia.org/wiki/Object-relational_impedance_mismatch#:~:text=The%20object-relational%20impedance%20mismatch%20is%20a%20set%20of,to%20database%20tables%20defined%20by%20a%20relational%20schema.). + +This sample will walkthrough an approach of adding the capabilities in a series of logical steps, versus big-bang (all at once), as this is more typical of how a developer may implement. + +
+ +## Scope + +Within the sample there will two primary entities exposed: +- **Employee** - being an employee that either is, or was, employed by the ficticous organization. +- **Performance Review** - being a recording of a number of performance reviews for an employee over time. + +
+ +### Employee + +This will represent an employee within the organization, and house key data such as their name, address, phone number, gender, date of birth, start and termination dates, and a list of up to five emergency contacts. + +From an endpoint perspective it will support the following. + +Endpoint | Description +-|- +`GET /employees/id` | Get employee by primary identifier. +`POST /employees` | Create a new employee. +`PUT /employees/id` | Update (replace) the existing employee (only where not terminated). +`PATCH /employees/id` | Patch the existing employee (only where not terminated). +`DELETE /employees/id` | Delete an existing employee (only where not started). +`GET /employees` | Gets employee(s) that match the selection criteria (a subset of the fields to be returned, plus support for paging). +`POST /employees/id/terminate` | Updates the employee as terminated (other endpoints do not allow termination). + +
+ +### Performance Review + +This will respresent a performance review (multiple over time), and house key data such as date, outcome, notes and reviewer. + +From an endpoint perspective it will support the following. + +Endpoint | Description +-|- +`GET /reviews/id` | Get review by primary identifier. +`POST /employees/id/reviews` | Create a review for a specified employee. +`PUT /reviews/id` | Update (replace) the review. +`PATCH /reviews/id` | Patch the existing review. +`DELETE /reviews/id` | Delete an existing employee (only where not started). +`GET /employee/id/reviews` | Gets all review(s) for the employee (with paging support). + +
+ +## Solution skeleton + +This solution should be created using the solution [template](../../templates/Beef.Template.Solution/README.md) capability, following the getting started [guide](../../docs/Sample-EntityFramework-GettingStarted.md). + +The following four commands should be invoked to create the solution structure. Start in a folder where the solution should reside. To simplify the ongoing copy and paste activities within this sample it is highly recommended that the `My.Hr` naming convention below is used. + +``` +dotnet new -i beef.template.solution --nuget-source https://api.nuget.org/v3/index.json +mkdir My.Hr +cd My.Hr +dotnet new beef --company My --appname Hr --datasource EntityFramework +``` + +The following solution structure will have been generated. Open `My.Hr.sln` in Visual Studio. + +``` +└── My.Hr + └── My.Hr.Api # API end-point and operations + └── My.Hr.Business # Core business logic components + └── My.Hr.CodeGen # Entity and Reference Data code generation console + └── My.Hr.Common # Common / shared components + └── My.Hr.Test # Unit and intra-integration tests + └── My.Hr.sln # Solution file that references all above projects +``` + +_Note:_ Code generation should **not** be performed before updating the corresponding XML files as described in the next sections. Otherwise, extraneous files will be generated that will then need to be manually removed. + +Also, any files that start with `Person` (being the demonstration entity) should be removed (deleted) from their respective projects as they are encountered. This then represents the base-line to build up the solution from. + +
+ +## Implementation steps + +As described earlier, this sample will walk through the implementation in a number of logical steps: +1. [Employee DB](./docs/Employee-DB.md) - creates the `Employee` database table and related stored procedures. +2. [Employee API](./docs/Employee-Api.md) - creates the `Employee` entities, API and related data access logic. +3. [Employee Test](./docs/Employee-Test.md) - creates the `Employee` end-to-end integration tests to validate the API and database functionality. +4. [Employee Search](./docs/Employee-Search.md) - adds the `Employee` search capability and tests. +5. [Employee Terminate](./docs/Employee-Terminate.md) - adds the `Employee` termination capability and tests. +6. [Employee Performance Review](./docs/Performance-Review.md) - adds the employee `PerformanceReview` capability end-to-end, from the the database, through the APIs and corresponding testing. + +
+ +## Conclusion + +The basis of the functional capabilities have been created for our fictitious solution. In the end, the developer should have a reasonable understanding of how to build a relatively complicated back-end (API and database) solution leveraging _Beef_. + +The developer should have witnessed that reasonably complicated logic can be built using this _config_ to _code-gen_ to _custom_ approach. Where the _custom_ effort is for the most part focused on the key business value delivery; not the related boilerplate. Plus, with the testing framework, how complex end-to-end intra-domain integration tests can be created to appropriately validate the underlying logic - which can easily be integrated into the developer build-test-release lifecycle. + +It is acknowledged that there is a learning curve required for using _Beef_; and in time greater acceleration will be achieved as experience is gained. Please review the extended documentation and provide feedback, questions, defects, etc. via a [issue](https://github.com/Avanade/Beef/issues). Thanks and enjoy :-) \ No newline at end of file diff --git a/samples/My.Hr/docs/Employee-Api.md b/samples/My.Hr/docs/Employee-Api.md new file mode 100644 index 000000000..168e8a634 --- /dev/null +++ b/samples/My.Hr/docs/Employee-Api.md @@ -0,0 +1,377 @@ +# Step 2 - Employee API + +This will walk through the process of creating the required APIs, including the related business and data access logic. + +The [`Beef.CodeGen.Core`](../../../tools/Beef.CodeGen.Core/README.md) provides the code-generation capabilities that will be leveraged. The underlying documentation describes these capabilities and the code-gen approach in greater detail. + +_Note:_ Any time that command line execution is requested, this should be performed from the base `My.Hr.CodeGen` folder. + +
+ +## Clean up existing + +The following files were created when the solution was provisioned, these should be removed (deleted): +- `My.Hr.Business/Data/Person.cs` +- `My.Hr.Business/Validation/PersonArgsValidator.cs` +- `My.Hr.Business/Validation/PersonValidator.cs` + +
+ +## Reference Data configuration + +The `My.RefData.xml` within `My.Hr.CodeGen` provides the code-gen configuration for the [Reference Data](../../../docs/Reference-Data.md). For the purposes of this sample, this configuration is relatively straighforward. + +Each reference data entity is defined, by specifying the name, the Web API route prefix (i.e. its endpoint), that it is to be automatically implemented using Entity Framework, and the name of the corresponding Entity Framework model (which was previously generated from the database; see `My.Hr.Business/Data/EfModel/Generated` folder). + +Replace the existing `Entity` XML (keeping the `CodeGeneration` element) with the following. + +``` xml + + + + +``` + +
+ +## Reference Data code-gen + +Once the reference data has been configured the code-generation can be performed. Use the following command line to generate. This will generate all of the required layers, from the API controller, through to the database access using Entity Framework. This is all that is required to operationalize the reference data. + +``` +dotnet run refdata +``` + +
+ +## Employee CRUD + +To implement the core `Employee` CRUD operations, the following will need to be performed: +- Code-gen configuration +- Code-gen execution +- Data access logic +- Validation + +
+ +### Code-gen configuration + +First up, the entities need to be defined (configured) within the `My.Hr.xml` (`My.Hr.CodeGen` project). The entities that will be created are as follows. Note that we will add some shape (e.g. address) to the data so it is easier (and more logical) to consume and understand, versus mapping directly to the database structure. + +- `EmployeeBase` - this represents the base Employee in that it contains the key properties and will be used as the base for searching as a means to minimise the properties that are available outside of the `Employee` CRUD itself. +- `Employee` - this represents the complete detailed Employee, which inherits from the `EmployeeBase`. All of the key operations for the Employee including the search will be configured/grouped as a logical set here. +- `TerminationDetail` - this represents an employee's termination (being date and reason). By having as a sub-type this enables additional related data to be more easily added under the single `Employee.Termination` property. +- `Address` - this represents the employees' address. By having as a sub-type it makes it easier and more explicit that there is a valid address via the `Employee.Address` property; in that we can validate the full address on the existence of the property itself (i.e. not `null`). +- `EmergencyContact` - this represents the collection of emergency contacts for an employee. + +Replace the existing `Entity` XML (keeping the `CodeGeneration`) with the following. The comments including are intended to describe the usage and why certain attributes have been specified. + +``` xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +
+ +### Code-gen execution + +Once the entities has been configured the code-generation can be performed. Use the following command line to generate. This will generate all of the required layers, from the API controller, through to the database access using Stored Procedures where configured. + +``` +dotnet run entity +``` + +
+ +### Data access logic + +Within the entity code-gen configuration where auto-implmentation is not possible then the data access logic must be specified explicitly. _Beef_ will still generate the boilerplate/skeleton wrapping logic for the data access to ensure consistency, and will invoke a corresponding `OnImplementation` method to perform (which the developer is required to implement). + +This logic must be implemented by the developer in a non-generated `partial` class. A new `EmployeeData.cs` must be created within `My.Hr.Business/Data`; do **not** change any files under the `Generated` folder as these will be overridden during the next code-gen execution. + +The following represents the initial implementation. + +``` csharp +using Beef; +using Beef.Data.Database; +using Microsoft.EntityFrameworkCore; +using My.Hr.Common.Entities; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace My.Hr.Business.Data +{ + public partial class EmployeeData + { + /// + /// Executes the 'Get' stored procedure passing the identifier and returns the result. + /// + private Task GetOnImplementationAsync(Guid id) => + ExecuteStatement(_db.StoredProcedure("[Hr].[spEmployeeGet]").Param(DbMapper.Default.GetParamName(nameof(Employee.Id)), id)); + + /// + /// Executes the 'Create' stored procedure and returns the result. + /// + private Task CreateOnImplementationAsync(Employee value) => + ExecuteStatement("[Hr].[spEmployeeCreate]", value, Beef.Mapper.OperationTypes.Create); + + /// + /// Executes the 'Update' stored procedure and returns the result. + /// + private Task UpdateOnImplementationAsync(Employee value) => + ExecuteStatement("[Hr].[spEmployeeUpdate]", value, Beef.Mapper.OperationTypes.Update); + + /// + /// Executes the stored procedure, passing Employee parameters, and the EmergencyContacts as a table-valued parameter (TVP), the operation type to aid mapping, + /// and requests for the result to be reselected. + /// + private Task ExecuteStatement(string storedProcedureName, Employee value, Beef.Mapper.OperationTypes operationType) + { + var sp = _db.StoredProcedure(storedProcedureName) + .Params(p => DbMapper.Default.MapToDb(value, p, operationType)) + .TableValuedParam("@EmergencyContactList", EmergencyContactData.DbMapper.Default.CreateTableValuedParameter(value.EmergencyContacts!)) + .ReselectRecordParam(); + + return ExecuteStatement(sp)!; + } + + /// + /// Executes the underlying stored procedure and processes the result (used by Get, Create and Update). + /// + private async Task ExecuteStatement(DatabaseCommand db) + { + Employee? employee = null; + + // Execute the generated stored procedure, selecting (querying) two sets of data: + // 1. The selected Employee (single row), the row is not mandatory, and stop (do not goto second set) where null. Use the underlying DbMapper to map between columns and .NET Type. + // 2. Zero or more EmergencyContact rows. Use EmergencyContactData.DbMapper to map between columns and .NET Type. Update the Employee with result. + await db.SelectQueryMultiSetAsync( + new MultiSetSingleArgs(DbMapper.Default, r => employee = r, isMandatory: false, stopOnNull: true), + new MultiSetCollArgs(EmergencyContactData.DbMapper.Default, r => employee!.EmergencyContacts = r)); + + return employee; + } + } +} +``` + +
+ +### Validation + +The final component that must be implemented by the developer is the validation logic. _Beef_ provides a rich, integrated, [validation framework](../../../docs/Beef-Validation.md) to simplify and standardize the validation as much as possible. This is also intended to encourage a more thorough approach to validation as the API is considered the primary custodian of the underlying data integrity - as Deep Throat said to Mulder in the [X-Files](https://en.wikipedia.org/wiki/The_X-Files), "trust no one"! + +To encourage reuse, _Beef_ has the concept of common validators which allow for standardised validations to be created that are then reusable. Within the `My.Hr.Business/Validation` folder create `CommonValidators.cs` and implement as follows. + +``` csharp +using Beef.Validation; +using System.ComponentModel.DataAnnotations; + +namespace My.Hr.Business.Validation +{ + /// + /// Provides common validator capabilities. + /// + public static class CommonValidators + { + private static readonly EmailAddressAttribute _emailValidator = new EmailAddressAttribute(); + + /// + /// Provides a common person's name validator, ensure max length is 100. + /// + public static CommonValidator PersonName = CommonValidator.Create(cv => cv.String(100)); + + /// + /// Provides a common address's street validator, ensure max length is 100. + /// + public static CommonValidator Street = CommonValidator.Create(cv => cv.String(100)); + + /// + /// Provides a common email validator, ensure max length is 250, is all lowercase, and use validator. + /// + public static CommonValidator Email = CommonValidator.Create(cv => cv.String(250).Override(v => v.Value!.ToLowerInvariant()).Must(v => _emailValidator.IsValid(v.Value))); + + /// + /// Provides a common phone number validator, just length, but could be regex or other. + /// + public static CommonValidator PhoneNo = CommonValidator.Create(cv => cv.String(50)); + } +} +``` + +The entity code-gen configuration references a `EmployeeValidator` that needs to be implemented. Within the `My.Hr.Business/Validation` folder create `EmployeeValidator.cs` and implement as follows. + +``` chsarp +using Beef; +using Beef.Validation; +using Beef.Validation.Rules; +using My.Hr.Business.DataSvc; +using My.Hr.Common.Entities; +using System; +using System.Text.RegularExpressions; + +namespace My.Hr.Business.Validation +{ + /// + /// Represents a validator. + /// + public class EmployeeValidator : Validator + { + // Address validator implemented using fluent-style method-chaining. + private static readonly Validator
_addressValidator = Validator.Create
() + .HasProperty(x => x.Street1, p => p.Mandatory().Common(CommonValidators.Street)) + .HasProperty(x => x.Street2, p => p.Common(CommonValidators.Street)) + .HasProperty(x => x.City, p => p.Mandatory().String(50)) + .HasProperty(x => x.State, p => p.Mandatory().IsValid()) + .HasProperty(x => x.PostCode, p => p.Mandatory().String(new Regex(@"^\d{5}(?:[-\s]\d{4})?$"))); + + // Emergency Contact validator implemented using fluent-style method-chaining. + public static readonly Validator _emergencyContactValidator = Validator.Create() + .HasProperty(x => x.FirstName, p => p.Mandatory().Common(CommonValidators.PersonName)) + .HasProperty(x => x.LastName, p => p.Mandatory().Common(CommonValidators.PersonName)) + .HasProperty(x => x.PhoneNo, p => p.Mandatory().Common(CommonValidators.PhoneNo)) + .HasProperty(x => x.Relationship, p => p.Mandatory().IsValid()); + + /// + /// Initializes a new instance of the class. + /// + public EmployeeValidator() + { + Property(x => x.Email).Mandatory().Common(CommonValidators.Email); + Property(x => x.FirstName).Mandatory().Common(CommonValidators.PersonName); + Property(x => x.LastName).Mandatory().Common(CommonValidators.PersonName); + Property(x => x.Gender).Mandatory().IsValid(); + Property(x => x.Birthday).Mandatory().CompareValue(CompareOperator.LessThanEqual, _ => DateTime.UtcNow.AddYears(-18), errorText: "Birthday is invalid as the Employee must be at least 18 years of age."); + Property(x => x.StartDate).Mandatory().CompareValue(CompareOperator.GreaterThanEqual, new DateTime(1999, 01, 01, 0, 0, 0, DateTimeKind.Utc), "January 1, 1999"); + Property(x => x.PhoneNo).Mandatory().Common(CommonValidators.PhoneNo); + Property(x => x.Address).Entity(_addressValidator); + Property(x => x.EmergencyContacts).Collection(maxCount: 5, item: new CollectionRuleItem(_emergencyContactValidator)); + } + + /// + /// Add further validation logic non-property bound. + /// + protected override void OnValidate(ValidationContext context) + { + base.OnValidate(context); + + // Ensure that the termination data is always null on an update; unless already terminated then it can no longer be updated. + switch (ExecutionContext.Current.OperationType) + { + case OperationType.Create: + context.Value.Termination = null; + break; + + case OperationType.Update: + var existing = context.GetService().GetAsync(context.Value.Id).GetAwaiter().GetResult(); + if (existing == null) + throw new NotFoundException(); + + if (existing.Termination != null) + throw new ValidationException("Once an Employee has been Terminated the data can no longer be updated."); + + context.Value.Termination = null; + break; + } + } + + /// + /// Common validator that will be referenced by the Delete operation to ensure that the employee can indeed be deleted. + /// + public static CommonValidator CanDelete = CommonValidator.Create(cv => cv.Custom(context => + { + var existing = context.GetService().GetAsync(context.Value).GetAwaiter().GetResult(); + if (existing == null) + throw new NotFoundException(); + + if (existing.StartDate <= DateTime.Now) + throw new ValidationException("An employee cannot be deleted after they have started their employment."); + })); + } +} +``` + +
+ +## Conclusion + +At this stage we now have a compiling and working API including database access logic for the reference data and key employee CRUD activities. + +Next we need to perform end-to-end [intra-integration testing](./Employee-Test.md) to ensure it is functioning as expected. \ No newline at end of file diff --git a/samples/My.Hr/docs/Employee-DB.md b/samples/My.Hr/docs/Employee-DB.md new file mode 100644 index 000000000..d966127f6 --- /dev/null +++ b/samples/My.Hr/docs/Employee-DB.md @@ -0,0 +1,231 @@ +# Step 1 - Employee DB + +This will walk through the process of creating the required tables, and stored procedures, etc. needed for the `Employee` within a Microsoft SQL Server database. All of this work will occur within the context of the `My.Hr.Database` project. + +The [`Beef.Database.Core`](../../../tools/Beef.Database.Core/README.md) provides the capabilities that will be leveraged. The underlying documentation describes these capabuilities and the database approach in greater detail. + +_Note:_ Any time that command line execution is requested, this should be performed from the base `My.Hr.Database` folder. + +
+ +## Clean up existing migrations + +Within the `Migrations` folder there will four entries that were created during the initial solution skeleton creation. The last two of these should be removed. The first two create the `Ref` and `Hr` database schemas. The `Ref` is for the reference data, and `Hr` is for the master data; in some scenarios it may make sense to use only the `Hr` schema which will house both types of data. For this sample two schemas will be used. + +``` +└── Migrations + └── 20190101-000000-create-Ref-schema.sql <- leave + └── 20190101-000001-create-Hr-schema.sql <- leave + └── 20190101-000002-create-Ref-Gender.sql <- remove + └── 20190101-000003-create-Hr-Person.sql <- remove +``` + +
+ +## Create Employee table + +First step is to create the `Employee` table migration script itself, following a similar naming convention to ensure it is executed (applied) in the correct order. This will create the migration script using a pre-defined naming convention and templated T-SQL to aid development. + +``` +dotnet run scriptnew -create Hr.Employee +``` + +For the purposes of this step, open the newly created migration script and replace its contents with the following. Additional notes have been added to give context/purpose where applicable. + +``` SQL +-- Migration Script + +BEGIN TRANSACTION + +CREATE TABLE [Hr].[Employee] ( + [EmployeeId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY, -- This is the primary key + [Email] NVARCHAR(250) NULL UNIQUE, -- This is the employee's unique email address + [FirstName] NVARCHAR(100) NULL, + [LastName] NVARCHAR(100) NULL, + [GenderCode] NVARCHAR(50) NULL, -- This is the related Gender code; see Ref.Gender table + [Birthday] DATE NULL, + [StartDate] DATE NULL, + [TerminationDate] DATE NULL, + [TerminationReasonCode] NVARCHAR(50) NULL, -- This is the related Termination Reason code; see Ref.TerminationReason table + [PhoneNo] NVARCHAR(50) NULL, + [AddressJson] NVARCHAR(500) NULL, -- This is the full address persisted as JSON. + [RowVersion] TIMESTAMP NOT NULL, -- This is used for concurrency version checking. + [CreatedBy] NVARCHAR(250) NULL, -- The following are standard audit columns. + [CreatedDate] DATETIME2 NULL, + [UpdatedBy] NVARCHAR(250) NULL, + [UpdatedDate] DATETIME2 NULL +); + +COMMIT TRANSACTION +``` + +
+ +## Create Emergency Contacts table + +Use the following command line to create the `EmergencyContact` table migration script. + +``` +dotnet run scriptnew -create Hr.EmergencyContact +``` + +Replace the contents with the following. _Note_: that we removed the row version and auditing columns as these are not required as this table is to be tightly-coupled to the `Employee`, and therefore can only (and should only) be updated in that context (i.e. is a sub-table). + +``` SQL +-- Migration Script + +BEGIN TRANSACTION + +CREATE TABLE [Hr].[EmergencyContact] ( + [EmergencyContactId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY, + [EmployeeId] UNIQUEIDENTIFIER NOT NULL, + [FirstName] NVARCHAR(100) NULL, + [LastName] NVARCHAR(100) NULL, + [PhoneNo] NVARCHAR(50) NULL, + [RelationshipTypeCode] NVARCHAR(50) NULL +); + +COMMIT TRANSACTION +``` + +
+ +## Create Reference Data tables + +To support the capabilities of the tables above the following Reference Data tables are also required. +- `Ref.Gender` +- `Ref.TerminationReason` +- `Ref.RelationshipType` +- `Ref.USState` + +At the command line execute the following commands. This will automatically create the tables as required using the reference data template given the `-creatref` option specified. No further changes will be needed for these tables. + +``` +dotnet run scriptnew -createref Ref.Gender +dotnet run scriptnew -createref Ref.TerminationReason +dotnet run scriptnew -createref Ref.RelationshipType +dotnet run scriptnew -createref Ref.USState +``` + +
+ +## Reference Data data + +Now that the Reference Data tables exist they will need to be populated. It is recommended that where possible that the Production environment values are specified (as these are intended to be deployed to all environments). + +These values (database rows) are specified using YAML. For brevity in this document, copy the data for the above tables **only** (for now) from [`RefData.yaml`](../My.Hr.Database/Data/RefData.yaml) replacing the contents of the prefilled `RefData.yaml` within the `My.Hr.Database/Data` folder. + +_Note:_ The format and hierarchy for the YAML, is: Schema, Table, Row. For reference data tables where only `Code: Text` is provided, this is treated as a special case shorthand to update those two columns accordingly (the other columns will be updated automatically). + +``` yaml +Ref: + - $Gender: + - M: Male + - F: Female + - N: Not specified +``` + +
+ +## Reference Data query + +To support the requirement to query the Reference Data values from the database we will use Entity Framework (EF) to simplify. The Reference Data table configuration will drive the EF .NET (C#) model code-generation via the `EfModel="true"` option. + +Remove all existing configuration from `My.Hr.Database.xml` and replace. Each table configuration is referencing the underlying table and schema, then requesting an EF model is created for all related columns found within the database. _Beef_ will query the database to infer the columns during code-generation to ensure it "understands" the latest configuration. + +``` XML + + + +
+
+
+
+ +``` + +
+ +## Stored Procedure CRUD + +Stored procedures will be used for the primary `Employee` CRUD as this also allows a simplified (and performant) means to select and update related tables as required, such as `EmergencyContact`. + +Copy the following configuration and append (after reference data) to the `My.Hr.Database.xml`; see comments within for the details. Again, _Beef_ will query the database to infer the columns during code-generation. + +``` XML + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + +
+``` + +
+ +## Entity Framework query + +To support a flexible query approach for the `Employee` Entity Framework (EF) will be used. To further optimize only the key data will be surfaced via the generated .NET (C#) data model. Append the following to the end of `My.Hr.Database.xml`. + +``` xml + + +``` + +
+ +## Database management + +Once the configuration has been completed then the database can be created/updated, the code-generation performed, and the corresponding reference data loaded into the corresponding tables. + +At the command line execute the following command to perform. The log output will describe all actions that were performed. + +``` +dotnet run all +``` + +If at any stage the database becomes corrupted or you need to rebuild, execute the following to drop and start again. + +``` +dotnet run drop +``` + +
+ +## Indexes, etc. + +Where tables need indexes and other constraints added these would be created using additional migration scripts. None have been included in the sample for brevity. + +
+ +## Conclusion + +At this stage we now have a working database ready for the consuming API logic to be added. The required database tables exist, the Reference Data data has been loaded, the required stored procedures and user-defined type (UDT) have been generated and added to the database. The .NET (C#) Entity Framework models have been generated and added to the `My.Hr.Business` project, including the requisite table-valued parameter (TVP). + +Next we need to create the [employee API](./Employee-Api.md) endpoint to perform the desired CRUD operations. \ No newline at end of file diff --git a/samples/My.Hr/docs/Employee-Search.md b/samples/My.Hr/docs/Employee-Search.md new file mode 100644 index 000000000..5b6f13719 --- /dev/null +++ b/samples/My.Hr/docs/Employee-Search.md @@ -0,0 +1,162 @@ +# Step 4 - Employee Search + +This will walk through the process of creating and testing the employee search capability. + +
+ +## Functional requirement + +The employee search will allow the following criteria to be searched: +- First and last name using wildcard; e.g. `Smi*`. +- Gender selection; one or more. +- Start date range (from/to). +- Option to include terminated employees (default is to exclude). + +The search should also support paging. The results should be returned in last name, first name and start date order. + +Examples of the endpoint are as follows (or any combination thereof). + +Endpoint | Description +-|- +`GET /employees?lastName=smi*` | all employees whose last name starts with `smi`. +`GET /employees?gender=f` | all female employees. +`GET /employees?startFrom=2000-01-01&startTo=2002-12-31` | all employess who started between 01-Jan-2000 and 31-Dec-2002 (inclusive). +`GET /employess?lastName=smi*&includeTerminated=true` | all employees whose last name starts with `smi`, both current and terminated. +`GET /employees?gender=f&$skip=10&$take25` | all female employees with paging (skipping the first 10 employees and getting the next 25 in sequence). + +
+ +## Data repository + +During the initial database set up process the `Employee` table (with a subset of columns) was enabled via code-generation leveraging an Entity Framework model class. This will be used to perform the query operations so that we can leverage .NET's LINQ-style query filtering and sorting. + +
+ +## Selection criteria + +The API selection criteria will be enabled by defining a class with all of the requisite properties. _Beef_ will then expose each of the properties individually within the API Controller to enable their usage. _Beef_ can automatically enable paging support when selected to do so; therefore, this does not need to be enabled via the selection criteria directly. + +Add the following entity code-gen configuration after all the other existing entities within `My.Hr.xml` (`My.Hr.CodeGen` project). + +``` xml + + + + + + + + + +``` + +The `GetByArgs` operation needs to be added to the `Employee` entity configuration; add the following after the existing `Delete` operation. + +``` xml + + + + +``` + +So that the code-gen knows what Entity Framework model is to be used this needs to be appended to the existing `Employee` element configuration. Replace the previous XML with the following (note that the xml comment termination will need to be fixed if copied). + +``` xml + - The EntityFrameworkEntity is required so that the GetByArgs code-gen knows what EfModel is to be used; however, DataEntityFrameworkCustomMapper is also used so that a corresponding EfMapper is not output (not required). --> + + +``` + +Execute the code-generation using the command line. + +``` +dotnet run entity +``` + +
+ +## Data access logic + +The existing `EmployeeData.cs` logic will need to be extended to support the new `GetByArgs`. + +For query operations generally we do not implement using the custom `OnImplementation` approach, as the primary code with the exception of the actual search criteria can be generated. As such, in this case _Beef_ will have generated an extension delegate named `_getByArgsOnQuery` to enable. This extension delegate will be passed in the `IQueryable` so that filtering and sorting, etc. can be applied, as well as the search arguments (`EmployeeArgs`). _Note:_ no paging is applied as _Beef_ will apply this automatically. + +Extensions within _Beef_ are leveraged by implementing the partial constructor method (`EmployeeDataCtor`) and providing an implementation for the requisite extension delegate (`_getByArgsOnQuery`). + +Add the following code to the non-generated `EmployeeData.cs` (`My.Hr.Business/Data`) that was created earlier. The `With` methods are enabled by _Beef_ to simplify the code logic to apply the filter only where the value is not `null`, plus specifically handle the likes of wildcards. + +``` csharp + partial void EmployeeDataCtor() + { + // Implement the GetByArgs OnQuery search/filtering logic. + _getByArgsOnQuery = (q, args, _) => + { + _ef.WithWildcard(args?.FirstName, (w) => q = q.Where(x => EF.Functions.Like(x.FirstName, w))); + _ef.WithWildcard(args?.LastName, (w) => q = q.Where(x => EF.Functions.Like(x.LastName, w))); + _ef.With(args?.Genders, () => q = q.Where(x => args!.Genders!.ToCodeList().Contains(x.GenderCode))); + _ef.With(args?.StartFrom, () => q = q.Where(x => x.StartDate >= args!.StartFrom)); + _ef.With(args?.StartTo, () => q = q.Where(x => x.StartDate <= args!.StartTo)); + + if (args?.IsIncludeTerminated == null || !args.IsIncludeTerminated.Value) + q = q.Where(x => x.TerminationDate == null); + + return q.OrderBy(x => x.LastName).ThenBy(x => x.FirstName).ThenBy(x => x.StartDate); + }; + } +``` + +
+ +## Validation + +Within the `My.Hr.Business/Validation` folder create `EmployeeArgsValidator.cs` and implement as follows. + +``` csharp +using Beef.Validation; +using My.Hr.Common.Entities; + +namespace My.Hr.Business.Validation +{ + /// + /// Represents a validator. + /// + public class EmployeeArgsValidator : Validator + { + /// + /// Initializes a new instance of the class. + /// + public EmployeeArgsValidator() + { + Property(x => x.FirstName).Common(CommonValidators.PersonName).Wildcard(); + Property(x => x.LastName).Common(CommonValidators.PersonName).Wildcard(); + Property(x => x.Genders).AreValid(); + Property(x => x.StartFrom).CompareProperty(CompareOperator.LessThanEqual, x => x.StartTo); + } + } +} +``` + +
+ +## End-to-End testing + +For the purposes of this sample un-comment the region `GetByArgs` within `EmployeeTest.cs`. Execute the tests and ensure they all pass as expected. + +As extra homework, you should also consider implementing unit testing for the validator. + +
+ +## Conclusion + +At this stage we now have added and tested the employee search, in addition to the employee CRUD APIs. + +Next we will implement the [employee termination](./Employee-Terminate.md) endpoint. \ No newline at end of file diff --git a/samples/My.Hr/docs/Employee-Terminate.md b/samples/My.Hr/docs/Employee-Terminate.md new file mode 100644 index 000000000..7725b0e8e --- /dev/null +++ b/samples/My.Hr/docs/Employee-Terminate.md @@ -0,0 +1,123 @@ +# Step 5 - Employee Terminate + +This will walk through the process of creating and testing the employee termination capability. + +
+ +## Functional requirement + +The employee termination will only terminate an existing employee under certain conditions. +- An employee can not be terminated more than once. +- An employee can not be termintaed on a date prior to their start date. + +
+ +## Data repository + +No additional data repository effort is required as the intent is to reuse what already exists; specifically the Update stored procedure that already exposes the termination related columns. + +
+ +## Code generation + +The `Termination` operation needs to be added to the `Employee` entity configuration; add the following after the existing `GetByArgs` operation. + +``` xml + + + + +``` + +Execute the code-generation using the command line. + +``` +dotnet run entity +``` + +
+ +## Data access logic + +The existing `EmployeeData.cs` logic will need to be extended to support the new `Terminate`. + +This is an instance where there is some validation logic that has been added to this data component versus in the related validator. The primary reason for this is efficiency, as we need to do a `Get` to then `Update`, so to minimize chattiness and keep this logic together it is all implemented here. + +Add the following code to the non-generated `EmployeeData.cs` (`My.Hr.Business/Data`) that was created earlier. _Note_ that we are reusing the `Get` and the `Update` we implemented previously. + +``` csharp + /// + /// Terminates an existing employee by updating their termination columns. + /// + private async Task TerminateOnImplementationAsync(TerminationDetail value, Guid id) + { + // Need to pre-query the data to, 1) check they exist, 2) check they are still employed, and 3) update. + var curr = await GetOnImplementationAsync(id); + if (curr == null) + throw new NotFoundException(); + + if (curr.Termination != null) + throw new ValidationException("An Employee can not be terminated more than once."); + + if (value.Date < curr.StartDate) + throw new ValidationException("An Employee can not be terminated prior to their start date."); + + curr.Termination = value; + return await UpdateOnImplementationAsync(curr); + } +``` + +
+ +## Validation + +Although, some of the validation was added into the data access logic, the property-level validation should still occur as usual within a validator. It is considered best practice to ensure the integrity of the data prior to making more expensive data access calls where possible; i.e. fail-fast. + +Within the `My.Hr.Business/Validation` folder create `TerminationDetailValidator.cs` and implement as follows. + +``` csharp +using Beef.Validation; +using My.Hr.Common.Entities; + +namespace My.Hr.Business.Validation +{ + /// + /// Represents a validator. + /// + public class TerminationDetailValidator : Validator + { + /// + /// Initializes a new instance of the class. + /// + public TerminationDetailValidator() + { + Property(x => x.Date).Mandatory(); + Property(x => x.Reason).Mandatory().IsValid(); + } + } +} +``` + +
+ +## End-to-End testing + +For the purposes of this sample un-comment the region `Terminate` within `EmployeeTest.cs`. Execute the tests and ensure they all pass as expected. + +As extra homework, you should also consider implementing unit testing for the validator. + +
+ +## Conclusion + +At this stage we now have added and tested the employee termination and search, in addition to the employee CRUD APIs. + +Next we will implement the employee [performance review](./Performance-Review.md) endpoints. \ No newline at end of file diff --git a/samples/My.Hr/docs/Employee-Test.md b/samples/My.Hr/docs/Employee-Test.md new file mode 100644 index 000000000..a1469e342 --- /dev/null +++ b/samples/My.Hr/docs/Employee-Test.md @@ -0,0 +1,68 @@ +# Step 3 - Employee Test + +This will walk through the process of creating the required end-to-end intra-domain integration tests to validate the employee CRUD APIs. + +The [`Beef.Test.NUnit`](../../../tools/Beef.Test.NUnit/README.md) provides the integration testing capabilities that will be leveraged. The underlying documentation describes these capabilities and the approach in greater detail. + +
+ +## Project structure + +The overall `My.Hr.Test` solution was created with the following; the existing `PersonTest.cs` and `PersonValidatorTest.cs` should be removed (deleted). + +``` +└── Data + └── Data.yaml <- leave; will replace contents +└── FixtureSetup.cs <- leave; contains key logic to set up database +└── PersonTest.cs <- remove +└── Validators + └── PersonValidatorTest.cs <- remove +``` + +
+ +## Data population + +For the end-to-end testing to function data must first be populated into the database; noting that the Reference Data configured and created earlier will be included automatically. Foundationally, the `My.Hr.Database` is leveraged to create and set up the database, as well as populate it with data. + +For the purposes of testing the APIs implemented so far, a set of employees and related data is required. The YAML defines the schema, table(s) and colums(s) with the required column data values. Replace the existing `Data.yaml` with the following. + +``` yaml +Hr: + - Employee: + - { EmployeeId: 1, Email: w.jones@org.com, FirstName: Wendy, LastName: Jones, GenderCode: F, Birthday: 1985-03-18, StartDate: 2000-12-11, PhoneNo: (425) 612 8113 } + - { EmployeeId: 2, Email: b.smith@org.com, FirstName: Brian, LastName: Smith, GenderCode: M, Birthday: 1994-11-07, StartDate: 2013-08-06, TerminationDate: 2015-04-08, TerminationReasonCode: RE, PhoneNo: (429) 120 0098 } + - { EmployeeId: 3, Email: r.Browne@org.com, FirstName: Rachael, LastName: Browne, GenderCode: F, Birthday: 1972-06-28, StartDate: 2019-11-06, PhoneNo: (421) 783 2343 } + - { EmployeeId: 4, Email: w.smither@org.com, FirstName: Waylon, LastName: Smithers, GenderCode: M, Birthday: 1952-02-21, StartDate: 2001-01-22, PhoneNo: (428) 893 2793, AddressJson: '{ "street1": "8365 851 PL NE", "city": "Redmond", "state": "WA", "postCode": "98052" }' } + - EmergencyContact: + - { EmergencyContactId: 201, EmployeeId: 2, FirstName: Garth, LastName: Smith, PhoneNo: (443) 678 1827, RelationshipTypeCode: PAR } + - { EmergencyContactId: 202, EmployeeId: 2, FirstName: Sarah, LastName: Smith, PhoneNo: (443) 234 3837, RelationshipTypeCode: PAR } + - { EmergencyContactId: 401, EmployeeId: 4, FirstName: Michael, LastName: Manners, PhoneNo: (234) 297 9834, RelationshipTypeCode: FRD } +``` + +
+ +## Employee test + +For the purposes of this sample, copy the contents of [`EmployeeTest.cs`](../My.Hr.Test/EmployeeTest.cs) and paste into an equivalent (new) `EmployeeTest.cs`. Comment out the region `GetByArgs` and `Termination` as these capabilities have not been implemented yet. + +Review and execute the tests and ensure they all pass as expected. + +
+ +## Employee Validator test + +This is more of a pure unit test; in that all data repository access is mocked out. This allows these to execute faster without database set up requirements, but will need the likes of reference data, and other, mocked as required. The sample demonstrates how these validators can be easily and thoroughly tested. + +For the purposes of this sample, copy the contents of [`EmployeeValidatorTest.cs`](../My.Hr.Test/Validators/EmployeeValidatorTest.cs) and paste into an equiavlent (new) `Validators/EmployeeValidatorTest.cs`. + +Review and execute the tests and ensure they all pass as expected. + +
+ +## Conclusion + +At this stage we now have a set of functioning and tested performance review APIs. All the desired functionality is now complete. These are now essentially ready for deployment; obviously, before doing so security would need to be integrated into the solution. + +Next we will implement the [employee search](./Employee-Search.md) endpoint. + diff --git a/samples/My.Hr/docs/Performance-Review.md b/samples/My.Hr/docs/Performance-Review.md new file mode 100644 index 000000000..b4d6aac69 --- /dev/null +++ b/samples/My.Hr/docs/Performance-Review.md @@ -0,0 +1,307 @@ +# Step 6 - Employee Performance Review + +This will walk through the process of creating and testing the employee performance review capability. + +
+ +## Functional requirement + +In simple terms an employee performance review can occur at any time during an employee's employment. It captures the basic information, when, the reviewer, the outcome and related notes. + +
+ +## Data repository + +A new `PerformanceReview` table and related Entity Framework model will be required to support. + +
+ +### Create Performance Review table + +First step, as was previously the case, is to create the `PerformanceReview` table migration script. Execute the following to create the migration script. + +``` +dotnet run scriptnew -create Hr.PerformanceReview +``` + +Open the newly created migration script and replace its contents with the following. + +``` sql +-- Migration Script + +BEGIN TRANSACTION + +CREATE TABLE [Hr].[PerformanceReview] ( + [PerformanceReviewId] UNIQUEIDENTIFIER NOT NULL DEFAULT (NEWSEQUENTIALID()) PRIMARY KEY, + [EmployeeId] UNIQUEIDENTIFIER NOT NULL, + [Date] DATETIME2 NULL, + [PerformanceOutcomeCode] NVARCHAR(50) NULL, + [Reviewer] NVARCHAR(100) NULL, + [Notes] NVARCHAR(4000) NULL, + [RowVersion] TIMESTAMP NOT NULL, + [CreatedBy] NVARCHAR(250) NULL, + [CreatedDate] DATETIME2 NULL, + [UpdatedBy] NVARCHAR(250) NULL, + [UpdatedDate] DATETIME2 NULL +); + +COMMIT TRANSACTION +``` + +
+ +### Create Reference Data table + +To support the above a `Ref.PerformanceOutcome` table is also required. Execute the following to create the migration script. No further changes will be needed. + +``` +dotnet run scriptnew -createref Ref.PerformanceOutcome +``` + +
+ +### Reference Data data + +Now that the Reference Data table exists it will need to be populated. Append the following YAML to the `RefData.yaml` file. + +``` yaml + - $PerformanceOutcome: + - DN: Does not meet expectations + - ME: Meets expectations + - EE: Exceeds expectations +``` + +
+ +### Entity Framework model + +For the performance review Entity Framework will be used exclusively to support the full CRUD capabilities. Append the following to the end of `My.Hr.Database.xml`. + +``` xml + +
+
+``` + +
+ +### Cascading-style delete + +Without an explict cascading-style delete implemented using a TSQL constraint an explicit delete for the performance reviews is required when deleting an employee. + +To accomplish, replace the `Delete` stored procedure configuration as follows. + +``` xml +
+ ... + + + + +``` + +
+ +### Database management + +Once the configuration has been completed then the database can be created/updated, the code-generation performed, and the corresponding reference data loaded into the corresponding tables. + +At the command line execute the following command to perform. The log output will describe all actions that were performed. + +``` +dotnet run all +``` + +
+ +## Reference Data API + +The reference data API for the `PerformanceOutcome` needs to be added. Append the following to the end of `My.RefData.xml` within the `My.Hr.CodeGen` project. + +``` xml + +``` + +Once the reference data has been configured the code-generation can be performed. Use the following command line to generate. + +``` +dotnet run refdata +``` + +
+ +## Performance Review API + +To implement the core `PerformanceReview` operations the following will need to be performed: +- Code configuration +- Data access logic +- Validation + +
+ +### Code generation + +The `PerformanceReview` entity and operations configuration is required as follows, append to the end of `My.Hr.xml` (`My.Hr.CodeGen` project). Given that we are updating a single table row within the database, all of the operations will be able to be automatically implemented (generated) limiting the amount of code that needs to be added. + +``` xml + + + + + + + + + + + + + + + + + + + + + + + +``` + +Once configured the code-generation can be performed. Use the following command line to generate. + +``` +dotnet run entity +``` + +
+ +### Data access logic + +The generated `PerformanceReviewData.cs` logic will need to be extended to support the filtering for the `GetByEmployeeId` operation. This logic must be implemented by the developer in a non-generated `partial` class. A new `PerformanceReviewData.cs` must be created within `My.Hr.Business/Data`. + +To implement the filtering the extension delegate named `_getByEmployeeIdOnQuery` is implemented. The following represents the implementation. + +``` csharp +using System.Linq; + +namespace My.Hr.Business.Data +{ + public partial class PerformanceReviewData + { + partial void PerformanceReviewDataCtor() + { + _getByEmployeeIdOnQuery = (q_, employeeId, _) => q_.Where(x => x.EmployeeId == employeeId).OrderByDescending(x => x.Date); + } + } +} +``` + +
+ +### Validation + +Within the `My.Hr.Business/Validation` folder create `PerformanceReviewValidator.cs` and implement as follows. Of note, the `Context.ServiceProvider` is the means to get the required implementation for classes that are instantiated via dependency injection. + +``` csharp +using Beef; +using Beef.Entities; +using Beef.Validation; +using My.Hr.Common.Entities; +using System; + +namespace My.Hr.Business.Validation +{ + /// + /// Represents a validator. + /// + public class PerformanceReviewValidator : Validator + { + /// + /// Initializes a new instance of the class. + /// + public PerformanceReviewValidator() + { + Property(x => x.EmployeeId).Mandatory(); + Property(x => x.Date).Mandatory().CompareValue(CompareOperator.LessThanEqual, _ => Cleaner.Clean(DateTime.Now), _ => "today"); + Property(x => x.Notes).String(4000); + Property(x => x.Reviewer).Mandatory().String(256); + Property(x => x.Outcome).Mandatory().IsValid(); + } + + /// + /// Add further validation logic. + /// + protected override void OnValidate(ValidationContext context) + { + if (!context.HasError(x => x.EmployeeId)) + { + // Ensure that the EmployeeId has not be changed (change back) as it is immutable. + if (ExecutionContext.Current.OperationType == OperationType.Update) + { + var prm = (IPerformanceReviewManager)context.ServiceProvider.GetService(typeof(IPerformanceReviewManager)); + var prv = prm.GetAsync(context.Value.Id).GetAwaiter().GetResult(); + if (prv == null) + throw new NotFoundException(); + + if (context.Value.EmployeeId != prv.EmployeeId) + { + context.AddError(x => x.EmployeeId, ValidatorStrings.ImmutableFormat); + return; + } + } + + // Check that the referenced Employee exists, and the review data is within the bounds of their employment. + var em = (IEmployeeManager)context.ServiceProvider.GetService(typeof(IEmployeeManager)); + var ev = em.GetAsync(context.Value.EmployeeId).GetAwaiter().GetResult(); + if (ev == null) + context.AddError(x => x.EmployeeId, ValidatorStrings.ExistsFormat); + else + { + if (!context.HasError(x => x.Date)) + { + if (context.Value.Date < ev.StartDate) + context.AddError(x => x.Date, "{0} must not be prior to the Employee starting."); + else if (ev.Termination != null && context.Value.Date > ev.Termination.Date) + context.AddError(x => x.Date, "{0} must not be after the Employee has terminated."); + } + } + } + } + } +} +``` + +
+ +## End-to-End testing + +For the purposes of this sample, copy the contents of [`PerformanceReviewTest.cs`](../My.Hr.Test/PerformanceReviewTest.cs) (`My.Hr.Test` root folder) and [`PerformanceReviewValidatorTest.cs`](../My.Hr.Test/Validators/PerformanceReviewValidatorTest.cs) (`My.Hr.Test/Validators` folder). + +For the end-to-end testing to function performance review related data must first be populated into the database; append the following into the existing `Data.yaml` (`My.Hr.Test/Data`). + +``` yaml + - PerformanceReview: + - { PerformanceReviewId: 1, EmployeeId: 2, Date: 2014-03-15, PerformanceOutcomeCode: DN, Reviewer: r.Browne@org.com, Notes: Work quality low. } + - { PerformanceReviewId: 2, EmployeeId: 1, Date: 2016-11-12, PerformanceOutcomeCode: EE, Reviewer: r.Browne@org.com, Notes: They are awesome! } + - { PerformanceReviewId: 3, EmployeeId: 2, Date: 2014-01-15, PerformanceOutcomeCode: DN, Reviewer: r.Browne@org.com, Notes: Work quality below standard. } +``` + +Review and execute the tests and ensure they all pass as expected. + +
+ +## Conclusion + +At this stage we now have added and tested the performance review capabilities. All the desired functional requirements have now been implemented. + +Next we will [wrap up](./../README.md#conclusion) the sample. \ No newline at end of file diff --git a/src/Beef.AspNetCore.WebApi/Beef.AspNetCore.WebApi.csproj b/src/Beef.AspNetCore.WebApi/Beef.AspNetCore.WebApi.csproj index 9e215003e..28af1965d 100644 --- a/src/Beef.AspNetCore.WebApi/Beef.AspNetCore.WebApi.csproj +++ b/src/Beef.AspNetCore.WebApi/Beef.AspNetCore.WebApi.csproj @@ -3,7 +3,7 @@ netcoreapp3.1 Beef.AspNetCore.WebApi - 4.1.1 + 4.1.2 false Beef Developers Avanade diff --git a/src/Beef.AspNetCore.WebApi/ChangeLog.md b/src/Beef.AspNetCore.WebApi/ChangeLog.md index 0060974ee..ccfe63c42 100644 --- a/src/Beef.AspNetCore.WebApi/ChangeLog.md +++ b/src/Beef.AspNetCore.WebApi/ChangeLog.md @@ -2,11 +2,13 @@ Represents the **NuGet** versions. +## v4.1.2 +- *Fixed:* An HTTP Delete will now catch a `NotFoundException` and return an HTTP Status Code 204 (no content) as a delete is considered idempotent. + ## v4.1.1 - *Enhancement:* Introduction of Dependency Injection support. ## v3.1.7 - - *Fixed:* Issue [60](https://github.com/Avanade/Beef/issues/60) fixed; ETag will be generated and returned correctly where underlying `Type` does not implement `IETag`. ## v3.1.6 diff --git a/src/Beef.AspNetCore.WebApi/WebApiActionBase.cs b/src/Beef.AspNetCore.WebApi/WebApiActionBase.cs index 584574991..5e0468d38 100644 --- a/src/Beef.AspNetCore.WebApi/WebApiActionBase.cs +++ b/src/Beef.AspNetCore.WebApi/WebApiActionBase.cs @@ -305,11 +305,12 @@ protected static IActionResult CreateResult(ActionContext context, HttpStatusCod /// /// The . /// The The function to invoke. + /// Indicates whether a should be converted to an . /// A that represents the asynchronous execute operation. [DebuggerStepThrough()] - protected virtual Task ExecuteResultAsync(ActionContext context, Func func) + protected virtual Task ExecuteResultAsync(ActionContext context, Func func, bool convertNotfoundToNoContent) { - return WebApiControllerInvoker.Current.InvokeAsync(Controller, () => ExecuteResultAsyncInternal(context, func), + return WebApiControllerInvoker.Current.InvokeAsync(Controller, () => ExecuteResultAsyncInternal(context, func, convertNotfoundToNoContent), memberName: CallerMemberName, filePath: CallerFilePath, lineNumber: CallerLineNumber); } @@ -317,7 +318,7 @@ protected virtual Task ExecuteResultAsync(ActionContext context, Func func /// Does the actual execution of the asynchronously where there is no result. /// [DebuggerStepThrough()] - private async Task ExecuteResultAsyncInternal(ActionContext context, Func func) + private async Task ExecuteResultAsyncInternal(ActionContext context, Func func, bool convertNotfoundToNoContent) { try { @@ -329,6 +330,12 @@ private async Task ExecuteResultAsyncInternal(ActionContext context, Func } catch (Exception ex) { + if (convertNotfoundToNoContent && ex is NotFoundException) + { + await CreateResult(context, HttpStatusCode.NoContent).ExecuteResultAsync(context).ConfigureAwait(false); + return; + } + var ai = ExecuteExceptionHandler == null ? null : ExecuteExceptionHandler(context, ex); if (ai == null) throw; @@ -635,7 +642,7 @@ public WebApiPost(ControllerBase controller, Func func, OperationType oper [DebuggerStepThrough()] public override Task ExecuteResultAsync(ActionContext context) { - return ExecuteResultAsync(context, _func); + return ExecuteResultAsync(context, _func, false); } } @@ -715,7 +722,7 @@ public WebApiPut(ControllerBase controller, Func func, OperationType opera [DebuggerStepThrough()] public override Task ExecuteResultAsync(ActionContext context) { - return ExecuteResultAsync(context, _func); + return ExecuteResultAsync(context, _func, false); } } @@ -795,7 +802,7 @@ public WebApiDelete(ControllerBase controller, Func func, OperationType op [DebuggerStepThrough()] public override Task ExecuteResultAsync(ActionContext context) { - return ExecuteResultAsync(context, _func); + return ExecuteResultAsync(context, _func, true); } } @@ -865,9 +872,10 @@ public WebApiPatch(ControllerBase controller, JToken value, Func> getFu /// /// The . /// The The function to invoke. + /// Indicates whether a should be converted to an . /// A that represents the asynchronous execute operation. [DebuggerStepThrough()] - protected override Task ExecuteResultAsync(ActionContext context, Func func) + protected override Task ExecuteResultAsync(ActionContext context, Func func, bool convertNotfoundToNoContent) { throw new NotSupportedException(); } diff --git a/src/Beef.Core/Beef.Core.csproj b/src/Beef.Core/Beef.Core.csproj index cfa783807..605df8735 100644 --- a/src/Beef.Core/Beef.Core.csproj +++ b/src/Beef.Core/Beef.Core.csproj @@ -3,7 +3,7 @@ netstandard2.1 Beef - 4.1.1 + 4.1.2 true Beef Developers Avanade diff --git a/src/Beef.Core/CHANGELOG.md b/src/Beef.Core/CHANGELOG.md index 702707477..25ded7f69 100644 --- a/src/Beef.Core/CHANGELOG.md +++ b/src/Beef.Core/CHANGELOG.md @@ -2,6 +2,11 @@ Represents the **NuGet** versions. +## v4.1.2 +- *Enhancement:* The `CodeGen` namespace has been moved to `Beef.CodeGen.Core`. A new `StringConversion` now provides access to the existing string conversion functions (e.g. `ToSentenceCase`). The is the first stage of the custom code-gen capability retirement; to be replaced by [`Handlebars.Net`](https://github.com/rexm/Handlebars.Net) as the code-generation engine. +- *Enhancement:* The `ColoredConsoleLogger` was update to write using `Console.Error` where the `LogLevel` is either `Error` or `Critical`; otherwise, use `Console.Out`. +- *Fixed:* `PropertyMapper` and `PropertySrceMapper` were not correctly updating the destination value(s) where the source was `null`. + ## v4.1.1 - *Enhancement:* Introduction of Dependency Injection support. diff --git a/src/Beef.Core/CodeGen/CodeGenerator.cs b/src/Beef.Core/CodeGen/CodeGenerator.cs deleted file mode 100644 index c712fa8e5..000000000 --- a/src/Beef.Core/CodeGen/CodeGenerator.cs +++ /dev/null @@ -1,405 +0,0 @@ -// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Xml.Linq; - -namespace Beef.CodeGen -{ - /// - /// The code generator. - /// - public class CodeGenerator - { - private static Func? _pluralizer; - - /// - /// The expression pattern for split strings into words. - /// - public const string WordSplitPattern = "([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))"; - - /// - /// The reserved system name. - /// - public const string SystemConfigName = "System"; - - /// - /// Gets or sets the function. - /// - /// The pluralizer. - public static void SetPluralizer(Func pluralizer) - { - _pluralizer = pluralizer ?? throw new ArgumentNullException(nameof(pluralizer)); - } - - /// - /// Pluralizes the . - /// - /// The text. - /// The pluralized text. - public static string? ToPlural(string? text) - { - if (string.IsNullOrEmpty(text)) - return text; - - if (_pluralizer == null) - throw new InvalidOperationException("SetPluralizer must be used to set the Pluralizer before it can be invoked."); - - return _pluralizer(text); - } - - /// - /// Converts to camelCase (e.g. 'SomeValue' would return 'someValue'). - /// - /// The text. - /// The converted text. - public static string? ToCamelCase(string? text) - { - if (string.IsNullOrEmpty(text)) - return text; - - if (text.StartsWith("OData", StringComparison.InvariantCultureIgnoreCase)) - return $"{char.ToLower(text[0], CultureInfo.InvariantCulture)}{char.ToLower(text[1], CultureInfo.InvariantCulture)}{text.Substring(2)}"; - else - return char.ToLower(text[0], CultureInfo.InvariantCulture) + text.Substring(1); - } - - /// - /// Converts to _camelCase (e.g. 'SomeValue' would return '_someValue'). - /// - /// The text. - /// The converted text. - public static string? ToPrivateCase(string? text) - { - if (string.IsNullOrEmpty(text)) - return text; - - return "_" + ToCamelCase(text); - } - - /// - /// Converts to PascalCase (e.g. 'someValue' would return 'SomeValue'). - /// - /// The text. - /// The converted text. - public static string? ToPascalCase(string? text) - { - if (string.IsNullOrEmpty(text)) - return text; - - if (text.StartsWith("OData", StringComparison.InvariantCultureIgnoreCase)) - return $"{char.ToUpper(text[0], CultureInfo.InvariantCulture)}{char.ToUpper(text[1], CultureInfo.InvariantCulture)}{text.Substring(2)}"; - else - return char.ToUpper(text[0], CultureInfo.InvariantCulture) + text.Substring(1); - } - - /// - /// Converts to a Sentence Case ('someValueXML' would return 'Some Value XML'); splits on capitals and attempts to keep acronyms. - /// - /// The text. - /// The converted text. - public static string? ToSentenceCase(string? text) - { - if (string.IsNullOrEmpty(text)) - return text; - - var s = Regex.Replace(text, WordSplitPattern, "$1 "); // Split the string into words. - s = SpecialCaseHandling(s); - return char.ToUpper(s[0], CultureInfo.InvariantCulture) + s.Substring(1); // Make sure the first character is always upper case. - } - - /// - /// Converts to a Snake Case ('someValueXML' would return 'some_value_xml'); splits on capitals and attempts to keep acronyms. - /// - /// The text. - /// The converted text. - public static string? ToSnakeCase(string? text) - { - if (string.IsNullOrEmpty(text)) - return text; - - var s = Regex.Replace(text, WordSplitPattern, "$1 "); // Split the string into words. - s = SpecialCaseHandling(s); -#pragma warning disable CA1308 // Normalize strings to uppercase; lowercase is correct! - return s.Replace(" ", "_", StringComparison.InvariantCulture).ToLowerInvariant(); // Replace space with _ and make lowercase. -#pragma warning restore CA1308 - } - - /// - /// Converts to a Kebab Case ('someValueXML' would return 'some-value-xml'); splits on capitals and attempts to keep acronyms. - /// - /// The text. - /// The converted text. - public static string? ToKebabCase(string? text) - { - if (string.IsNullOrEmpty(text)) - return text; - - var s = Regex.Replace(text, WordSplitPattern, "$1 "); // Split the string into words. - s = SpecialCaseHandling(s); -#pragma warning disable CA1308 // Normalize strings to uppercase; lowercase is correct! - return s.Replace(" ", "-", StringComparison.InvariantCulture).ToLowerInvariant(); // Replace space with _ and make lowercase. -#pragma warning restore CA1308 - } - - /// - /// Special cas handling. - /// - /// - /// - private static string SpecialCaseHandling(string text) - { - if (string.IsNullOrEmpty(text)) - return text; - - var s = text.Replace("E Tag", "ETag", StringComparison.InvariantCulture); // Special case where we will put back together. - return s.Replace("O Data", "OData", StringComparison.InvariantCulture); // Special case where we will put back together. - } - - /// - /// Converts to c# 'see cref=' Comments ('List<int>' would become 'List{int}' respectively). - /// - /// The text. - /// The converted text. - private static string? ReplaceGenericsBracketWithCommentsBracket(string? text) - { - if (string.IsNullOrEmpty(text)) - return text; - - var s = text.Replace("<", "{", StringComparison.InvariantCulture); - s = s.Replace(">", "}", StringComparison.InvariantCulture); - return s; - } - - /// - /// Converts to c# Comments ('{{xyx}}' would become 'see cref=' XML, and any <> within the xyz would become {} respectively). - /// - /// The text. - /// The converted text. - public static string? ToComments(string? text) - { - if (string.IsNullOrEmpty(text)) - return text; - - var s = text; - while (true) - { - var start = s.IndexOf("{{", StringComparison.InvariantCultureIgnoreCase); - var end = s.IndexOf("}}", StringComparison.InvariantCultureIgnoreCase); - - if (start < 0 && end < 0) - break; - - if (start < 0 || end < 0 || end < start) - throw new CodeGenException("Start and End {{ }} parameter mismatch.", text); - - string sub = s.Substring(start, end - start + 2); - string? mid = ReplaceGenericsBracketWithCommentsBracket(sub[2..^2]); - - s = s.Replace(sub, string.Format(CultureInfo.InvariantCulture, "", mid), StringComparison.InvariantCulture); - } - - return s; - } - - /// - /// Converts to c# 'see cref=' comments ('List<int>' would become '<see cref="List{int}/>' respectively). - /// - /// The text. - /// The converted text. - public static string? ToSeeComments(string? text) - { - if (string.IsNullOrEmpty(text)) - return text; - - return $""; - } - - /// - /// Converts a text to past tense (english). - /// - /// The text. - /// The converted text. - public static string? ToPastTense(string? text) - { - if (string.IsNullOrEmpty(text) || text.Length < 3 || text.EndsWith("ed", StringComparison.InvariantCultureIgnoreCase)) - return text; - - // Ends with the letter e, then remove the final e and add the -ed suffix: tie->tied, like->liked, agree->agreed. - if (text[^1] == 'e') - return text + "d"; - - // Ends with the letter y preceded by a consonant, then change the y to an i and add the -ed suffix: apply->applied, pry->pried, study->studied. - if (text[^1] == 'y' && !IsVowel(text[^2])) - return text[0..^2] + "ied"; - - // Ends with a single consonant other than w or y preceded by a single vowel, then double the final consonant and add the -ed suffix: drop->dropped, admit->admitted, concur->concured. - if (!IsVowel(text[^1]) && text[^1] != 'w' && text[^1] != 'y' && IsVowel(text[^2]) && !IsVowel(text[^3])) - return text + text[^1] + "ed"; - - // Ends with the letter c, then add the letter k followed by the -ed suffix: frolic->frolicked, picnic->picnicked. - if (text[^1] == 'c') - return text + "ked"; - - // Add the -ed suffix. - return text + "ed"; - } - - /// - /// Determine whether the character is a vowel. - /// - private static bool IsVowel(char c) => char.IsLetter(c) && (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'); - - /// - /// Creates a new . - /// - /// The configuration . - /// The dictionary. - /// A . - public static CodeGenerator Create(XElement configXml, IEnumerable? loaders = null) - { - if (configXml == null) - throw new ArgumentNullException(nameof(configXml)); - - // Create the code generator. - var cg = new CodeGenerator(configXml); - - // Load the Loaders making sure it does not contain 'System'. - if (loaders != null) - { - foreach (var l in loaders) - { - if (l.Name == SystemConfigName) - throw new ArgumentException($"A ConfigLoader with the Name of '{SystemConfigName}' is reserved for internal use only.", nameof(loaders)); - - if (cg.Loaders.ContainsKey(l.Name)) - throw new ArgumentException($"A ConfigLoader with the Name of '{l.Name}' has already been defined (must be unique).", nameof(loaders)); - - cg.Loaders.Add(l.Name, l); - } - } - - return cg; - } - - /// - /// Private constructor. - /// - private CodeGenerator(XElement configXml) => ConfigXml = configXml; - - /// - /// Gets or sets the root (top-most) . - /// - internal CodeGenConfig? Root { get; set; } - - /// - /// Gets the System . - /// - internal CodeGenConfig System { get; private set; } = new CodeGenConfig(SystemConfigName, null); - - /// - /// Gets the configuration . - /// - public XElement ConfigXml { get; private set; } - - /// - /// Gets the loaders. - /// - internal Dictionary Loaders { get; } = new Dictionary(); - - /// - /// Gets the parameter overrides. - /// - public Dictionary Parameters { get; internal set; } = new Dictionary(); - - /// - /// Copies the into . - /// - /// The parameters to copy. - public void CopyParameters(Dictionary parameters) - { - if (parameters == null) - return; - - foreach (var p in parameters) - { - Parameters.Add(p.Key, p.Value); - } - } - - /// - /// Clears the current . - /// - public void ClearParameters() - { - Parameters.Clear(); - } - - /// - /// Generates the output. - /// - /// The template . - public async Task GenerateAsync(XElement xmlTemplate) - { - if (xmlTemplate == null) - throw new ArgumentNullException(nameof(xmlTemplate)); - - // Ready the 'System' configuration. - System = new CodeGenConfig(SystemConfigName, null); - System.AttributeAdd("Index", "0"); - - // Creates the root configuration. - await CodeGenConfig.CreateAsync(this).ConfigureAwait(false); - - using var t = new CodeGenTemplate(this, xmlTemplate); - t.Execute(); - } - - /// - /// Raises the event. - /// - internal void RaiseCodeGenerated(CodeGeneratorEventArgs e) - { - CodeGenerated?.Invoke(this, e); - } - - /// - /// Occurs when a output has been successfully generated. - /// - public event EventHandler? CodeGenerated; - } - - /// - /// The event arguments. - /// - public class CodeGeneratorEventArgs : EventArgs - { - /// - /// Gets the generated directory name. - /// - public string? OutputGenDirName { get; internal set; } - - /// - /// Gets the optional directory name. - /// - public string? OutputDirName { get; internal set; } - - /// - /// Gets the generated file name. - /// - public string? OutputFileName { get; internal set; } - - /// - /// Indicates whether the file is only output when new; i.e. does not already exist. - /// - public bool IsOutputNewOnly { get; internal set; } - - /// - /// Gets the generated output content. - /// - public string? Content { get; internal set; } - } -} diff --git a/src/Beef.Core/Diagnostics/ColoredConsoleLogger.cs b/src/Beef.Core/Diagnostics/ColoredConsoleLogger.cs index dcabe0009..786ee29a8 100644 --- a/src/Beef.Core/Diagnostics/ColoredConsoleLogger.cs +++ b/src/Beef.Core/Diagnostics/ColoredConsoleLogger.cs @@ -71,23 +71,23 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except _ => ConsoleColor.Cyan }; - ConsoleWriteLine(text, color); + ConsoleWriteLine(text, color, logLevel); } /// /// Writes the specified text to the console. /// - /// The text. - /// The foreground . - private static void ConsoleWriteLine(string? text = null, ConsoleColor? foregroundColor = null) + private static void ConsoleWriteLine(string? text, ConsoleColor? foregroundColor, LogLevel logLevel) { + var c = logLevel == LogLevel.Error || logLevel == LogLevel.Critical ? Console.Error : Console.Out; + if (string.IsNullOrEmpty(text)) - Console.WriteLine(); + c.WriteLine(); else { var currColor = Console.ForegroundColor; Console.ForegroundColor = foregroundColor ?? currColor; - Console.WriteLine(text); + c.WriteLine(text); Console.ForegroundColor = currColor; } } diff --git a/src/Beef.Core/Mapper/PropertyMapper.cs b/src/Beef.Core/Mapper/PropertyMapper.cs index 2943c9d69..79852b31d 100644 --- a/src/Beef.Core/Mapper/PropertyMapper.cs +++ b/src/Beef.Core/Mapper/PropertyMapper.cs @@ -568,9 +568,6 @@ void IPropertySrceMapper.MapToDest(TSrce sourceEntity, object destination /// The single being performed to enable selection. public virtual void MapToDest(TSrce sourceEntity, TDest destinationEntity, OperationTypes operationType) { - if (sourceEntity == null || destinationEntity == null) - return; - if (!OperationTypes.HasFlag(operationType)) return; @@ -581,7 +578,7 @@ public virtual void MapToDest(TSrce sourceEntity, TDest destinationEntity, Opera CreateAutoMapperIfRequired(); - if (Converter == null && Mapper != null && (IsSrceComplexType && !SrceComplexTypeReflector!.IsCollection) && (IsDestComplexType && !DestComplexTypeReflector!.IsCollection)) + if (val != null && Converter == null && Mapper != null && (IsSrceComplexType && !SrceComplexTypeReflector!.IsCollection) && (IsDestComplexType && !DestComplexTypeReflector!.IsCollection)) { TDestProperty dval = GetDestValue(destinationEntity, operationType); if (dval != null) diff --git a/src/Beef.Core/Mapper/PropertyMapperCustomBase.cs b/src/Beef.Core/Mapper/PropertyMapperCustomBase.cs index c7f9380dc..ccc15bdfc 100644 --- a/src/Beef.Core/Mapper/PropertyMapperCustomBase.cs +++ b/src/Beef.Core/Mapper/PropertyMapperCustomBase.cs @@ -227,7 +227,7 @@ public PropertyMapperCustomBase MapSrceToDestWhen(Functrue indicates that the mapping should occur; otherwise, false. bool IPropertySrceMapper.MapSrceToDestWhen(TSrce entity) { - return (_mapSrceToDestWhen == null) ? true : _mapSrceToDestWhen.Invoke(entity); + return (_mapSrceToDestWhen == null) || _mapSrceToDestWhen.Invoke(entity); } /// diff --git a/src/Beef.Core/Mapper/PropertySrceMapper.cs b/src/Beef.Core/Mapper/PropertySrceMapper.cs index 04c5133e7..3f5dd5f70 100644 --- a/src/Beef.Core/Mapper/PropertySrceMapper.cs +++ b/src/Beef.Core/Mapper/PropertySrceMapper.cs @@ -390,13 +390,10 @@ public void MapToDest(TSrce sourceEntity, TDest destinationEntity, OperationType return; TSrceProperty val = GetSrceValue(sourceEntity, operationType)!; - if (val != null) + foreach (var pm in Mapper.Mappings) { - foreach (var pm in Mapper.Mappings) - { - if (pm.OperationTypes.HasFlag(operationType) && ((IPropertySrceMapper)pm).MapSrceToDestWhen(val)) - ((IPropertySrceMapper)pm).MapToDest(val, destinationEntity, operationType); - } + if (pm.OperationTypes.HasFlag(operationType) && ((IPropertySrceMapper)pm).MapSrceToDestWhen(val)) + ((IPropertySrceMapper)pm).MapToDest(val, destinationEntity, operationType); } } diff --git a/src/Beef.Core/RefData/ReferenceDataBase.cs b/src/Beef.Core/RefData/ReferenceDataBase.cs index c39eae19a..ecb05ab44 100644 --- a/src/Beef.Core/RefData/ReferenceDataBase.cs +++ b/src/Beef.Core/RefData/ReferenceDataBase.cs @@ -82,7 +82,7 @@ public override int GetHashCode() private DateTime? _endDate; private string? _etag; private ChangeLog? _changeLog; - private Dictionary? _mappings; + private Dictionary? _mappings; /// /// Validates the identifier (see ) . @@ -293,12 +293,12 @@ public ChangeLog? ChangeLog /// /// Gets the mapping dictionary. /// - internal Dictionary Mappings + internal Dictionary Mappings { get { if (_mappings == null) - _mappings = new Dictionary(); + _mappings = new Dictionary(); return _mappings; } @@ -341,7 +341,7 @@ public virtual bool IsValid /// The mapping name. /// The mapping value. /// A with the default value will not be set; assumed in this case that no mapping exists. - protected internal void SetMapping(string name, T value) where T : IComparable + protected internal void SetMapping(string name, T value) { if (Comparer.Default.Compare(value, default!) == 0) return; @@ -358,12 +358,12 @@ protected internal void SetMapping(string name, T value) where T : IComparabl /// The value . /// The mapping name. /// The mapping value where found; otherwise, the corresponding default value. - public T GetMapping(string name) where T : IComparable + public T GetMapping(string name) { - if (!HasMappings || !Mappings.TryGetValue(name, out IComparable value)) + if (!HasMappings || !Mappings.TryGetValue(name, out var value)) return default!; - return (T)value; + return (T)value!; } /// @@ -373,14 +373,13 @@ public T GetMapping(string name) where T : IComparable /// The mapping name. /// The mapping value. /// true indicates that the name exists; otherwise, false. - public bool TryGetMapping(string name, out T value) where T : IComparable + public bool TryGetMapping(string name, out T value) { - IComparable val = default(T)!; - value = (T)val; - if (!HasMappings || !Mappings.TryGetValue(name, out val)) + value = default!; + if (!HasMappings || !Mappings.TryGetValue(name, out var val)) return false; - value = (T)val; + value = (T)val!; return true; } diff --git a/src/Beef.Core/RefData/ReferenceDataCollectionBase.cs b/src/Beef.Core/RefData/ReferenceDataCollectionBase.cs index d982bbd39..caa2b91f8 100644 --- a/src/Beef.Core/RefData/ReferenceDataCollectionBase.cs +++ b/src/Beef.Core/RefData/ReferenceDataCollectionBase.cs @@ -110,7 +110,7 @@ public ReferenceDataCodeCollection(ReferenceDataCollectionBase owner) private struct MappingsKey { public string Name; - public IComparable Value; + public object? Value; } #endregion diff --git a/src/Beef.Core/Reflection/PropertyExpression.cs b/src/Beef.Core/Reflection/PropertyExpression.cs index 9da2d8fc8..ecf2a3742 100644 --- a/src/Beef.Core/Reflection/PropertyExpression.cs +++ b/src/Beef.Core/Reflection/PropertyExpression.cs @@ -145,7 +145,7 @@ internal static PropertyExpression CreateInternal(Expression } // Create expression (with compilation also). - var pe = new PropertyExpression(name, jpa == null ? name : jpa.PropertyName!, ca == null ? Beef.CodeGen.CodeGenerator.ToSentenceCase(me.Member.Name)! : ca.Name, propertyExpression.Compile()) + var pe = new PropertyExpression(name, jpa == null ? name : jpa.PropertyName!, ca == null ? StringConversion.ToSentenceCase(me.Member.Name)! : ca.Name, propertyExpression.Compile()) { JsonPropertyAttribute = jpa }; diff --git a/src/Beef.Core/StringConversion.cs b/src/Beef.Core/StringConversion.cs new file mode 100644 index 000000000..df9d15cb2 --- /dev/null +++ b/src/Beef.Core/StringConversion.cs @@ -0,0 +1,173 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using System; +using System.Globalization; +using System.Text.RegularExpressions; + +namespace Beef +{ + /// + /// Provides special case string conversions. + /// + public static class StringConversion + { + /// + /// The expression pattern for splitting strings into words. + /// + public const string WordSplitPattern = "([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))"; + + /// + /// Pluralizes the (English). + /// + /// The text. + /// The pluralized text. + public static string? ToPlural(string? text) + { + if (string.IsNullOrEmpty(text)) + return text; + + throw new NotSupportedException(); + } + + /// + /// Converts to camelCase (e.g. 'SomeValue' would return 'someValue'). + /// + /// The text. + /// The converted text. + public static string? ToCamelCase(string? text) + { + if (string.IsNullOrEmpty(text)) + return text; + + if (text.StartsWith("OData", StringComparison.InvariantCultureIgnoreCase)) + return $"{char.ToLower(text[0], CultureInfo.InvariantCulture)}{char.ToLower(text[1], CultureInfo.InvariantCulture)}{text.Substring(2)}"; + else + return char.ToLower(text[0], CultureInfo.InvariantCulture) + text.Substring(1); + } + + /// + /// Converts to _camelCase (e.g. 'SomeValue' would return '_someValue'). + /// + /// The text. + /// The converted text. + public static string? ToPrivateCase(string? text) + { + if (string.IsNullOrEmpty(text)) + return text; + + return "_" + ToCamelCase(text); + } + + /// + /// Converts to PascalCase (e.g. 'someValue' would return 'SomeValue'). + /// + /// The text. + /// The converted text. + public static string? ToPascalCase(string? text) + { + if (string.IsNullOrEmpty(text)) + return text; + + if (text.StartsWith("OData", StringComparison.InvariantCultureIgnoreCase)) + return $"{char.ToUpper(text[0], CultureInfo.InvariantCulture)}{char.ToUpper(text[1], CultureInfo.InvariantCulture)}{text.Substring(2)}"; + else + return char.ToUpper(text[0], CultureInfo.InvariantCulture) + text.Substring(1); + } + + /// + /// Converts to a Sentence Case ('someValueXML' would return 'Some Value XML'); splits on capitals and attempts to keep acronyms. + /// + /// The text. + /// The converted text. + public static string? ToSentenceCase(string? text) + { + if (string.IsNullOrEmpty(text)) + return text; + + var s = Regex.Replace(text, WordSplitPattern, "$1 "); // Split the string into words. + s = SpecialCaseHandling(s); + return char.ToUpper(s[0], CultureInfo.InvariantCulture) + s.Substring(1); // Make sure the first character is always upper case. + } + + /// + /// Converts to a Snake Case ('someValueXML' would return 'some_value_xml'); splits on capitals and attempts to keep acronyms. + /// + /// The text. + /// The converted text. + public static string? ToSnakeCase(string? text) + { + if (string.IsNullOrEmpty(text)) + return text; + + var s = Regex.Replace(text, WordSplitPattern, "$1 "); // Split the string into words. + s = SpecialCaseHandling(s); +#pragma warning disable CA1308 // Normalize strings to uppercase; lowercase is correct! + return s.Replace(" ", "_", StringComparison.InvariantCulture).ToLowerInvariant(); // Replace space with _ and make lowercase. +#pragma warning restore CA1308 + } + + /// + /// Converts to a Kebab Case ('someValueXML' would return 'some-value-xml'); splits on capitals and attempts to keep acronyms. + /// + /// The text. + /// The converted text. + public static string? ToKebabCase(string? text) + { + if (string.IsNullOrEmpty(text)) + return text; + + var s = Regex.Replace(text, WordSplitPattern, "$1 "); // Split the string into words. + s = SpecialCaseHandling(s); +#pragma warning disable CA1308 // Normalize strings to uppercase; lowercase is correct! + return s.Replace(" ", "-", StringComparison.InvariantCulture).ToLowerInvariant(); // Replace space with - and make lowercase. +#pragma warning restore CA1308 + } + + /// + /// Special case handling. + /// + private static string SpecialCaseHandling(string text) + { + if (string.IsNullOrEmpty(text)) + return text; + + var s = text.Replace("E Tag", "ETag", StringComparison.InvariantCulture); // Special case where we will put back together. + return s.Replace("O Data", "OData", StringComparison.InvariantCulture); // Special case where we will put back together. + } + + /// + /// Converts a text to past tense (English only). + /// + /// The text. + /// The converted text. + public static string? ToPastTense(string? text) + { + if (string.IsNullOrEmpty(text) || text.Length < 3 || text.EndsWith("ed", StringComparison.InvariantCultureIgnoreCase)) + return text; + + // Ends with the letter e, then remove the final e and add the -ed suffix: tie->tied, like->liked, agree->agreed. + if (text[^1] == 'e') + return text + "d"; + + // Ends with the letter y preceded by a consonant, then change the y to an i and add the -ed suffix: apply->applied, pry->pried, study->studied. + if (text[^1] == 'y' && !IsVowel(text[^2])) + return text[0..^2] + "ied"; + + // Ends with a single consonant other than w or y preceded by a single vowel, then double the final consonant and add the -ed suffix: drop->dropped, admit->admitted, concur->concured. + if (!IsVowel(text[^1]) && text[^1] != 'w' && text[^1] != 'y' && IsVowel(text[^2]) && !IsVowel(text[^3])) + return text + text[^1] + "ed"; + + // Ends with the letter c, then add the letter k followed by the -ed suffix: frolic->frolicked, picnic->picnicked. + if (text[^1] == 'c') + return text + "ked"; + + // Add the -ed suffix. + return text + "ed"; + } + + /// + /// Determine whether the character is a vowel (English only). + /// + public static bool IsVowel(char c) => char.IsLetter(c) && (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'); + } +} diff --git a/src/Beef.Core/Validation/PropertyContext.cs b/src/Beef.Core/Validation/PropertyContext.cs index 8c2b0c28f..4eb0eec53 100644 --- a/src/Beef.Core/Validation/PropertyContext.cs +++ b/src/Beef.Core/Validation/PropertyContext.cs @@ -1,6 +1,7 @@ // Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef using Beef.Entities; +using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.Linq; @@ -162,6 +163,19 @@ public PropertyContext(ValidationContext context, TProperty value, stri /// public bool HasError { get; internal set; } + /// + /// Gets the from the . + /// + public IServiceProvider ServiceProvider => (ExecutionContext.HasCurrent ? ExecutionContext.Current.ServiceProvider : null) ?? throw new InvalidOperationException("There is either no ExecutionContext.Current or the ExecutionContext.ServiceProvider has not been configured."); + + /// + /// Gets service of type from the . + /// + /// The service . + /// Indicates whether to throw an where the underlying returns null. + /// The specified service where found; + public TService GetService(bool throwExceptionOnNull = true) where TService : class => ExecutionContext.GetService(throwExceptionOnNull); + /// /// Enables the underlying value to be overridden (updated). /// diff --git a/src/Beef.Core/Validation/PropertyRule.cs b/src/Beef.Core/Validation/PropertyRule.cs index 31d9ae412..c2fbc774a 100644 --- a/src/Beef.Core/Validation/PropertyRule.cs +++ b/src/Beef.Core/Validation/PropertyRule.cs @@ -37,12 +37,12 @@ public abstract class PropertyRuleBase where TEntity : class /// Initializes a new instance of the class. /// /// The property name. - /// The friendly text name used in validation messages (defaults to as ). + /// The friendly text name used in validation messages (defaults to as ). /// The JSON property name (defaults to ). protected PropertyRuleBase(string name, LText? text = null, string? jsonName = null) { Name = Check.NotEmpty(name, nameof(name)); - Text = text ?? Beef.CodeGen.CodeGenerator.ToSentenceCase(Name)!; + Text = text ?? StringConversion.ToSentenceCase(Name)!; JsonName = string.IsNullOrEmpty(jsonName) ? Name : jsonName; } diff --git a/src/Beef.Core/Validation/ValidationContext.cs b/src/Beef.Core/Validation/ValidationContext.cs index 6583c0109..4fa44ee51 100644 --- a/src/Beef.Core/Validation/ValidationContext.cs +++ b/src/Beef.Core/Validation/ValidationContext.cs @@ -2,6 +2,7 @@ using Beef.Entities; using Beef.Reflection; +using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.Linq; @@ -10,9 +11,9 @@ namespace Beef.Validation { /// - /// Provides the base validation context properties for an entity. + /// Provides the base validation context properties. /// - public interface IValidationContext + public interface IValidationContextBase { /// /// Gets the entity value. @@ -28,7 +29,13 @@ public interface IValidationContext /// Indicates whether there has been a validation error. /// bool HasErrors { get; } + } + /// + /// Provides the validation context properties for an entity. + /// + public interface IValidationContext : IValidationContextBase + { /// /// Gets the entity prefix used for fully qualified entity.property naming (null represents the root). /// @@ -111,7 +118,7 @@ private void Messages_CollectionChanged(object sender, System.Collections.Specia /// /// Gets the entity value. /// - object? IValidationContext.Value => Value; + object? IValidationContextBase.Value => Value; /// /// Gets the entity value. @@ -158,6 +165,14 @@ private void Messages_CollectionChanged(object sender, System.Collections.Specia /// public IServiceProvider ServiceProvider => (ExecutionContext.HasCurrent ? ExecutionContext.Current.ServiceProvider : null) ?? throw new InvalidOperationException("There is either no ExecutionContext.Current or the ExecutionContext.ServiceProvider has not been configured."); + /// + /// Gets service of type from the . + /// + /// The service . + /// Indicates whether to throw an where the underlying returns null. + /// The specified service where found; + public TService GetService(bool throwExceptionOnNull = true) where TService : class => ExecutionContext.GetService(throwExceptionOnNull); + /// /// Throws a where an error was found (and optionally if warnings). /// diff --git a/src/Beef.Core/Validation/ValidationExtensions.cs b/src/Beef.Core/Validation/ValidationExtensions.cs index c32733210..1b7b127fd 100644 --- a/src/Beef.Core/Validation/ValidationExtensions.cs +++ b/src/Beef.Core/Validation/ValidationExtensions.cs @@ -772,7 +772,7 @@ public static ValueValidator Validate(this T value, string? name = null, L /// For example a value of 'VarNameDB' would return 'Var Name DB'. public static string? ToSentenceCase(this string? value) { - return Beef.CodeGen.CodeGenerator.ToSentenceCase(value); + return StringConversion.ToSentenceCase(value); } #endregion diff --git a/src/Beef.Core/Validation/ValueValidatorResult.cs b/src/Beef.Core/Validation/ValueValidatorResult.cs index 29c057acb..0155589ac 100644 --- a/src/Beef.Core/Validation/ValueValidatorResult.cs +++ b/src/Beef.Core/Validation/ValueValidatorResult.cs @@ -10,7 +10,7 @@ namespace Beef.Validation /// /// The entity . /// The property . - public class ValueValidatorResult where TEntity : class + public sealed class ValueValidatorResult : IValidationContextBase where TEntity : class { private readonly PropertyContext _context; @@ -23,6 +23,11 @@ public ValueValidatorResult(PropertyContext context) _context = context ?? throw new ArgumentNullException(nameof(context)); } + /// + /// Gets the value. + /// + object? IValidationContextBase.Value => Value; + /// /// Gets the value. /// @@ -31,6 +36,11 @@ public TProperty Value get { return _context.Value; } } + /// + /// Indicates whether there has been a validation error. + /// + bool IValidationContextBase.HasErrors => HasError; + /// /// Indicates whether there has been a validation error. /// diff --git a/src/Beef.Data.Cosmos/Beef.Data.Cosmos.csproj b/src/Beef.Data.Cosmos/Beef.Data.Cosmos.csproj index fd6ed3f3a..466601e6e 100644 --- a/src/Beef.Data.Cosmos/Beef.Data.Cosmos.csproj +++ b/src/Beef.Data.Cosmos/Beef.Data.Cosmos.csproj @@ -2,7 +2,7 @@ netcoreapp3.1 - 4.1.1 + 4.1.2 Beef Developers Avanade Business Entity Execution Framework (Beef) CosmosDB / DocumentDB Core extensions. diff --git a/src/Beef.Data.Cosmos/CHANGELOG.md b/src/Beef.Data.Cosmos/CHANGELOG.md index 8f72b193b..c522a6f34 100644 --- a/src/Beef.Data.Cosmos/CHANGELOG.md +++ b/src/Beef.Data.Cosmos/CHANGELOG.md @@ -2,6 +2,9 @@ Represents the **NuGet** versions. +## v4.1.2 +- *Fixed:* A `NotFoundException` will be thrown on a delete if it does not exist; otherwise, the application will assume it deleted successfully and the likes of a related event could be raised incorrectly. + ## v4.1.1 - *Enhancement:* Introduction of Dependency Injection support. diff --git a/src/Beef.Data.Cosmos/CosmosDbContainer.cs b/src/Beef.Data.Cosmos/CosmosDbContainer.cs index 1a92645aa..27dc2e11c 100644 --- a/src/Beef.Data.Cosmos/CosmosDbContainer.cs +++ b/src/Beef.Data.Cosmos/CosmosDbContainer.cs @@ -242,7 +242,7 @@ await CosmosDb.Invoker.InvokeAsync(this, async () => var ro = CosmosDb.GetItemRequestOptions(DbArgs); var resp = await Container.ReadItemAsync(key, DbArgs.PartitionKey ?? PartitionKey.None, ro).ConfigureAwait(false); if (resp?.Resource == null) - return; + throw new NotFoundException(); CheckAuthorized(resp.Resource); await Container.DeleteItemAsync(key, DbArgs.PartitionKey ?? PartitionKey.None, CosmosDb.GetItemRequestOptions(DbArgs)).ConfigureAwait(false); @@ -250,7 +250,7 @@ await CosmosDb.Invoker.InvokeAsync(this, async () => catch (CosmosException cex) { if (cex.StatusCode == System.Net.HttpStatusCode.NotFound) - return; + throw new NotFoundException(); throw; } diff --git a/src/Beef.Data.Cosmos/CosmosDbQuery.cs b/src/Beef.Data.Cosmos/CosmosDbQuery.cs index e5dda0276..adcc7ffa1 100644 --- a/src/Beef.Data.Cosmos/CosmosDbQuery.cs +++ b/src/Beef.Data.Cosmos/CosmosDbQuery.cs @@ -133,7 +133,7 @@ public T SelectSingle() /// Selects a single item or default. /// /// The single item or default. - public T SelectSingleOrDefault() + public T? SelectSingleOrDefault() { return _container.GetValue(ExecuteQuery(q => { @@ -159,7 +159,7 @@ public T SelectFirst() /// Selects first item or default. /// /// The single item or default. - public T SelectFirstOrDefault() + public T? SelectFirstOrDefault() { return _container.GetValue(ExecuteQuery(q => { diff --git a/src/Beef.Data.Cosmos/CosmosDbValueContainer.cs b/src/Beef.Data.Cosmos/CosmosDbValueContainer.cs index 4a3f01f34..d8922dc6c 100644 --- a/src/Beef.Data.Cosmos/CosmosDbValueContainer.cs +++ b/src/Beef.Data.Cosmos/CosmosDbValueContainer.cs @@ -239,7 +239,7 @@ await CosmosDb.Invoker.InvokeAsync(this, async () => var ro = CosmosDb.GetItemRequestOptions(DbArgs); var resp = await Container.ReadItemAsync>(key, DbArgs.PartitionKey ?? PartitionKey.None, ro).ConfigureAwait(false); if (resp?.Resource == null || resp.Resource.Type != _typeName) - return; + throw new NotFoundException(); CheckAuthorized(resp.Resource); ro.SessionToken = resp.Headers?.Session; @@ -249,7 +249,7 @@ await CosmosDb.Invoker.InvokeAsync(this, async () => catch (CosmosException cex) { if (cex.StatusCode == System.Net.HttpStatusCode.NotFound) - return; + throw new NotFoundException(); throw; } diff --git a/src/Beef.Data.Cosmos/CosmosDbValueQuery.cs b/src/Beef.Data.Cosmos/CosmosDbValueQuery.cs index 9ced9a279..c8d3e5220 100644 --- a/src/Beef.Data.Cosmos/CosmosDbValueQuery.cs +++ b/src/Beef.Data.Cosmos/CosmosDbValueQuery.cs @@ -124,7 +124,7 @@ public T SelectSingle() /// Selects a single item or default. /// /// The single item or default. - public T SelectSingleOrDefault() + public T? SelectSingleOrDefault() { return _container.GetValue(ExecuteQuery(q => { @@ -150,7 +150,7 @@ public T SelectFirst() /// Selects first item or default. /// /// The single item or default. - public T SelectFirstOrDefault() + public T? SelectFirstOrDefault() { return _container.GetValue(ExecuteQuery(q => { diff --git a/src/Beef.Data.Database/Beef.Data.Database.csproj b/src/Beef.Data.Database/Beef.Data.Database.csproj index 8962af0d5..2ebeebe7f 100644 --- a/src/Beef.Data.Database/Beef.Data.Database.csproj +++ b/src/Beef.Data.Database/Beef.Data.Database.csproj @@ -2,7 +2,7 @@ netstandard2.1 - 4.1.1 + 4.1.2 false Beef Developers Avanade diff --git a/src/Beef.Data.Database/CHANGELOG.md b/src/Beef.Data.Database/CHANGELOG.md index d381a04d7..b285c7871 100644 --- a/src/Beef.Data.Database/CHANGELOG.md +++ b/src/Beef.Data.Database/CHANGELOG.md @@ -2,6 +2,10 @@ Represents the **NuGet** versions. +## v4.1.2 +- *Fixed:* The `DatabasePropertyMapper.MapToDb()` where mapping to a sub-property (via a `DatabaseMapper` was mapping each sub-property where the overarching property value was `null`. This resulted in each `DbParameter` being set to its default value (from a .NET perspective) which did not account for database nullability. The underlying `DatabaseMapper` will now _not_ be invoked where `null` and the properties should default as per the invoked stored procedure definition. +- *Fixed:* A `NotFoundException` will be thrown on a delete if it does not exist; otherwise, the application will assume it deleted successfully and the likes of a related event could be raised incorrectly. + ## v4.1.1 - *Enhancement:* Introduction of Dependency Injection support. diff --git a/src/Beef.Data.Database/DatabaseBase.cs b/src/Beef.Data.Database/DatabaseBase.cs index ca5e71172..5d8f5c478 100644 --- a/src/Beef.Data.Database/DatabaseBase.cs +++ b/src/Beef.Data.Database/DatabaseBase.cs @@ -337,12 +337,14 @@ public virtual void OnConnectionOpen(DbConnection dbConnection) { } /// The value . /// The . /// The key values. - public Task DeleteAsync(DatabaseArgs saveArgs, params IComparable[] keys) where T : class, new() + public async Task DeleteAsync(DatabaseArgs saveArgs, params IComparable[] keys) where T : class, new() { if (saveArgs == null) throw new ArgumentNullException(nameof(saveArgs)); - return StoredProcedure(saveArgs.StoredProcedure).Params((p) => saveArgs.Mapper.GetKeyParams(p, OperationTypes.Delete, keys)).NonQueryAsync(); + var rowsAffected = await StoredProcedure(saveArgs.StoredProcedure).Params((p) => saveArgs.Mapper.GetKeyParams(p, OperationTypes.Delete, keys)).NonQueryAsync().ConfigureAwait(false); + if (rowsAffected == 0) + throw new NotFoundException(); } /// diff --git a/src/Beef.Data.Database/DatabasePropertyMapper.cs b/src/Beef.Data.Database/DatabasePropertyMapper.cs index 50d628721..9adabcf8a 100644 --- a/src/Beef.Data.Database/DatabasePropertyMapper.cs +++ b/src/Beef.Data.Database/DatabasePropertyMapper.cs @@ -171,7 +171,7 @@ public DatabasePropertyMapper MapDestToSrceWhen(Functrue indicates that the mapping should occur; otherwise, false. public bool MapDestToSrceWhen(DatabaseRecord dr) { - return (_mapDestToSrceWhen == null) ? true : _mapDestToSrceWhen.Invoke(dr); + return (_mapDestToSrceWhen == null) || _mapDestToSrceWhen.Invoke(dr); } /// @@ -288,8 +288,11 @@ public void SetDestValue(TSrce value, DatabaseParameters parameters, OperationTy var val = GetSrceValue(value, operationType)!; if (Mapper != null) { - var em = (IDatabaseMapper)Mapper; - em.MapToDb(val, parameters, operationType, this); + if (val != null) + { + var em = (IDatabaseMapper)Mapper; + em.MapToDb(val, parameters, operationType, this); + } } else { diff --git a/src/Beef.Data.EntityFrameworkCore/Beef.Data.EntityFrameworkCore.csproj b/src/Beef.Data.EntityFrameworkCore/Beef.Data.EntityFrameworkCore.csproj index 1caf95666..a3d4398b9 100644 --- a/src/Beef.Data.EntityFrameworkCore/Beef.Data.EntityFrameworkCore.csproj +++ b/src/Beef.Data.EntityFrameworkCore/Beef.Data.EntityFrameworkCore.csproj @@ -2,7 +2,7 @@ netcoreapp3.1 - 4.1.1 + 4.1.2 false Business Entity Execution Framework (Beef) Entity Framework (EF) Core extensions. Beef Developers diff --git a/src/Beef.Data.EntityFrameworkCore/CHANGELOG.md b/src/Beef.Data.EntityFrameworkCore/CHANGELOG.md index cedc61585..62d9f1a2f 100644 --- a/src/Beef.Data.EntityFrameworkCore/CHANGELOG.md +++ b/src/Beef.Data.EntityFrameworkCore/CHANGELOG.md @@ -2,6 +2,9 @@ Represents the **NuGet** versions. +## v4.1.2 +- *Fixed:* A `NotFoundException` will be thrown on a delete if it does not exist; otherwise, the application will assume it deleted successfully and the likes of a related event could be raised incorrectly. + ## v4.1.1 - *Enhancement:* Introduction of Dependency Injection support. diff --git a/src/Beef.Data.EntityFrameworkCore/EfDbBase.cs b/src/Beef.Data.EntityFrameworkCore/EfDbBase.cs index eaeec1188..237a3d756 100644 --- a/src/Beef.Data.EntityFrameworkCore/EfDbBase.cs +++ b/src/Beef.Data.EntityFrameworkCore/EfDbBase.cs @@ -79,13 +79,6 @@ public EfDbBase(TDbContext dbContext, EfDbInvoker? invoker = null) /// public EfDbInvoker Invoker { get; private set; } - /// - /// Indicates whether a pre-read is performed on an to confirm existence and throw a corresponding - /// where not found. Otherwise, where not found a will be thrown. The pre-read requires an additional - /// database keyed-read and therefore has a minor performance impact as a result. - /// - public bool OnUpdatePreReadForNotFound { get; set; } = false; - /// /// Gets or sets the to enable wildcard replacement. /// @@ -229,26 +222,23 @@ public void With(T with, Action action) return await Invoker.InvokeAsync(this, async () => { - if (OnUpdatePreReadForNotFound) + // Check (find) if the entity exists. + var efKeys = new object[saveArgs.Mapper.UniqueKey.Count]; + for (int i = 0; i < saveArgs.Mapper.UniqueKey.Count; i++) { - // Check (find) if the entity exists. - var efKeys = new object[saveArgs.Mapper.UniqueKey.Count]; - for (int i = 0; i < saveArgs.Mapper.UniqueKey.Count; i++) - { - var v = saveArgs.Mapper.UniqueKey[i].GetSrceValue(value, Mapper.OperationTypes.Unspecified); - efKeys[i] = saveArgs.Mapper.UniqueKey[i].ConvertToDestValue(v, Mapper.OperationTypes.Unspecified)!; - } - - var em = (TModel)await DbContext.FindAsync(typeof(TModel), efKeys).ConfigureAwait(false); - if (em == null) - throw new NotFoundException(); - - // Remove the entity from the tracker before we attempt to update; otherwise, will use existing rowversion and concurrency will not work as expected. - DbContext.Remove(em); - DbContext.ChangeTracker.AcceptAllChanges(); + var v = saveArgs.Mapper.UniqueKey[i].GetSrceValue(value, Mapper.OperationTypes.Unspecified); + efKeys[i] = saveArgs.Mapper.UniqueKey[i].ConvertToDestValue(v, Mapper.OperationTypes.Unspecified)!; } - var model = saveArgs.Mapper.MapToDest(value, Mapper.OperationTypes.Update) ?? throw new InvalidOperationException("Mapping to the EF entity must not result in a null value."); + var model = (TModel)await DbContext.FindAsync(typeof(TModel), efKeys).ConfigureAwait(false); + if (model == null) + throw new NotFoundException(); + + // Remove the entity from the tracker before we attempt to update; otherwise, will use existing rowversion and concurrency will not work as expected. + DbContext.Remove(model); + DbContext.ChangeTracker.AcceptAllChanges(); + + saveArgs.Mapper.MapToDest(value, model, Mapper.OperationTypes.Update); DbContext.Update(model); if (saveArgs.SaveChanges) @@ -280,7 +270,7 @@ await Invoker.InvokeAsync(this, async () => // A pre-read is required to get the row version for concurrency. var em = (TModel)await DbContext.FindAsync(typeof(TModel), efKeys).ConfigureAwait(false); if (em == null) - return; + throw new NotFoundException(); DbContext.Remove(em); diff --git a/src/Beef.Data.EntityFrameworkCore/EfDbQuery.cs b/src/Beef.Data.EntityFrameworkCore/EfDbQuery.cs index 2e14420f3..9284c956a 100644 --- a/src/Beef.Data.EntityFrameworkCore/EfDbQuery.cs +++ b/src/Beef.Data.EntityFrameworkCore/EfDbQuery.cs @@ -25,7 +25,7 @@ namespace Beef.Data.EntityFrameworkCore /// Selects a single item or default. /// /// The single item or default. - T SelectSingleOrDefault(); + T? SelectSingleOrDefault(); /// /// Selects first item. @@ -37,7 +37,7 @@ namespace Beef.Data.EntityFrameworkCore /// Selects first item or default. /// /// The single item or default. - T SelectFirstOrDefault(); + T? SelectFirstOrDefault(); /// /// Executes the query command creating a resultant collection. @@ -137,9 +137,9 @@ public T SelectSingle() /// Selects a single item or default. /// /// The single item or default. - public T SelectSingleOrDefault() + public T? SelectSingleOrDefault() { - return QueryArgs.Mapper.MapToSrce(ExecuteQuery(q => q.SingleOrDefault()), Mapper.OperationTypes.Get)!; + return QueryArgs.Mapper.MapToSrce(ExecuteQuery(q => q.SingleOrDefault()), Mapper.OperationTypes.Get); } /// @@ -155,9 +155,9 @@ public T SelectFirst() /// Selects first item or default. /// /// The single item or default. - public T SelectFirstOrDefault() + public T? SelectFirstOrDefault() { - return QueryArgs.Mapper.MapToSrce(ExecuteQuery(q => q.FirstOrDefault()), Mapper.OperationTypes.Get)!; + return QueryArgs.Mapper.MapToSrce(ExecuteQuery(q => q.FirstOrDefault()), Mapper.OperationTypes.Get); } #endregion diff --git a/src/Beef.Data.OData/Beef.Data.OData.csproj b/src/Beef.Data.OData/Beef.Data.OData.csproj index ec9368a07..00a50f83a 100644 --- a/src/Beef.Data.OData/Beef.Data.OData.csproj +++ b/src/Beef.Data.OData/Beef.Data.OData.csproj @@ -2,7 +2,7 @@ netstandard2.1 - 4.1.1 + 4.1.2 true strong-name-key.snk false diff --git a/src/Beef.Data.OData/CHANGELOG.md b/src/Beef.Data.OData/CHANGELOG.md index 6ed1e49b2..98367efef 100644 --- a/src/Beef.Data.OData/CHANGELOG.md +++ b/src/Beef.Data.OData/CHANGELOG.md @@ -2,6 +2,9 @@ Represents the **NuGet** versions. +## v4.1.2 +- *Fixed:* A `NotFoundException` will be thrown on a delete if it does not exist; otherwise, the application will assume it deleted successfully and the likes of a related event could be raised incorrectly. + ## v4.1.1 - *Enhancement:* Introduction of Dependency Injection support. diff --git a/src/Beef.Data.OData/ODataBase.cs b/src/Beef.Data.OData/ODataBase.cs index 894e7d4a4..ee56bf754 100644 --- a/src/Beef.Data.OData/ODataBase.cs +++ b/src/Beef.Data.OData/ODataBase.cs @@ -106,8 +106,8 @@ protected ODataBase(Soc.ODataClientSettings clientSettings, ODataInvoker? invoke { try { - var val = await Client.For(getArgs.CollectionName).Key(okeys).FindEntryAsync().ConfigureAwait(false); - return GetValue(getArgs, val); + var model = await GetModelAsync(getArgs, okeys).ConfigureAwait(false); + return GetValue(getArgs, model); } catch (WebRequestException odex) { @@ -119,6 +119,24 @@ protected ODataBase(Soc.ODataClientSettings clientSettings, ODataInvoker? invoke }, this).ConfigureAwait(false); } + /// + /// Gets the model. + /// + private async Task GetModelAsync(ODataArgs getArgs, object[]? keys) where T : class, new() where TModel : class, new() + { + try + { + return await Client.For(getArgs.CollectionName).Key(keys).FindEntryAsync().ConfigureAwait(false); + } + catch (WebRequestException odex) + { + if (odex.Code == System.Net.HttpStatusCode.NotFound && getArgs.NullOnNotFoundResponse) + return null; + + throw; + } + } + /// /// Creates the entity (mapping from to ) asynchronously. /// @@ -141,7 +159,7 @@ protected ODataBase(Soc.ODataClientSettings clientSettings, ODataInvoker? invoke { var model = saveArgs.Mapper.MapToDest(value, Mapper.OperationTypes.Create) ?? throw new InvalidOperationException("Mapping to the OData model must not result in a null value."); var created = await Client.For(saveArgs.CollectionName).Set(model).InsertEntryAsync(true).ConfigureAwait(false); - return GetValue(saveArgs, created); + return GetValue(saveArgs, created)!; }, this).ConfigureAwait(false); } @@ -166,12 +184,16 @@ protected ODataBase(Soc.ODataClientSettings clientSettings, ODataInvoker? invoke return await Invoker.InvokeAsync(this, async () => { var okeys = saveArgs.GetODataKeys(value); - var model = saveArgs.Mapper.MapToDest(value, Mapper.OperationTypes.Create) ?? throw new InvalidOperationException("Mapping to the OData model must not result in a null value."); + var model = await GetModelAsync(saveArgs, okeys).ConfigureAwait(false); + if (model == null) + throw new NotFoundException(); + + saveArgs.Mapper.MapToDest(value, model, Mapper.OperationTypes.Update); var updated = await Client.For(saveArgs.CollectionName).Key(okeys).Set(model).UpdateEntryAsync(true).ConfigureAwait(false); if (updated == null) throw new NotFoundException(); - return GetValue(saveArgs, updated); + return GetValue(saveArgs, updated)!; }, this).ConfigureAwait(false); } @@ -198,7 +220,7 @@ await Invoker.InvokeAsync(this, async () => catch (WebRequestException odex) { if (odex.Code == System.Net.HttpStatusCode.NotFound) - return; + throw new NotFoundException(); throw; } @@ -208,10 +230,10 @@ await Invoker.InvokeAsync(this, async () => /// /// Gets the corresponding entity value from the model value. /// - internal static T GetValue(ODataArgs args, TModel model) where T : class, new() where TModel : class, new() + internal static T? GetValue(ODataArgs args, TModel? model) where T : class, new() where TModel : class, new() { - if (model == default) - return default!; + if (model == null) + return null; else return args.Mapper.MapToSrce(model, Mapper.OperationTypes.Get) ?? throw new InvalidOperationException("Mapping from the OData model must not result in a null value."); } diff --git a/src/Beef.Data.OData/ODataQuery.cs b/src/Beef.Data.OData/ODataQuery.cs index 238d73c92..fa351cd59 100644 --- a/src/Beef.Data.OData/ODataQuery.cs +++ b/src/Beef.Data.OData/ODataQuery.cs @@ -72,14 +72,14 @@ public T SelectSingle() { var coll = q.Skip(0).Top(2).FindEntriesAsync().GetAwaiter().GetResult(); return coll.Single(); - })); + }))!; } /// /// Selects a single item or default. /// /// The single item or default. - public T SelectSingleOrDefault() + public T? SelectSingleOrDefault() { return ODataBase.GetValue(QueryArgs, ExecuteQuery(q => { @@ -98,14 +98,14 @@ public T SelectFirst() { var coll = q.Skip(0).Top(1).FindEntriesAsync().GetAwaiter().GetResult(); return coll.First(); - })); + }))!; } /// /// Selects first item or default. /// /// The single item or default. - public T SelectFirstOrDefault() + public T? SelectFirstOrDefault() { return ODataBase.GetValue(QueryArgs, ExecuteQuery(q => { @@ -152,7 +152,7 @@ public void SelectQuery(TColl coll) where TColl : ICollection foreach (var item in q.FindEntriesAsync(ann).GetAwaiter().GetResult()) { - coll.Add(ODataBase.GetValue(QueryArgs, item)); + coll.Add(ODataBase.GetValue(QueryArgs, item)!); } if (ann != null) diff --git a/src/Beef.Events/Beef.Events.csproj b/src/Beef.Events/Beef.Events.csproj index 05abbfcd6..3109394a0 100644 --- a/src/Beef.Events/Beef.Events.csproj +++ b/src/Beef.Events/Beef.Events.csproj @@ -2,7 +2,7 @@ netstandard2.1 - 4.1.1 + 4.1.2 Beef Developers Avanade Business Entity Execution Framework (Beef) Events framework. diff --git a/src/Beef.Events/CHANGELOG.md b/src/Beef.Events/CHANGELOG.md index 64d7433cf..e609c83f8 100644 --- a/src/Beef.Events/CHANGELOG.md +++ b/src/Beef.Events/CHANGELOG.md @@ -2,6 +2,9 @@ Represents the **NuGet** versions. +## v4.1.2 +- *Enhancement:* Moved all subscriber host arguments to `EventSubscriberHostArgs` to centralize and enable simple configuration via DI. + ## v4.1.1 - *Enhancement:* Introduction of Dependency Injection support. diff --git a/src/Beef.Events/EventExtensions.cs b/src/Beef.Events/EventExtensions.cs index ae8c821dc..71b2c0dce 100644 --- a/src/Beef.Events/EventExtensions.cs +++ b/src/Beef.Events/EventExtensions.cs @@ -39,33 +39,26 @@ public static IWebJobsBuilder AddBeefResilientEventHubs(this IWebJobsBuilder bui } /// - /// Adds a scoped service to instantiate a new instance with subscriber auto-discovery. + /// Adds a scoped service to instantiate a new instance using the specified . /// - /// The startup class whereby all the subscribers reside. /// The . - /// Indicates whether to add all the subscriber types (auto-discovered within ) as transient services. + /// The . + /// Indicates whether to add all the as transient services (defaults to true). /// The for fluent-style method-chaining. - public static IServiceCollection AddBeefEventHubSubscriberHost(this IServiceCollection services, bool addSubscriberTypeServices = true) where TStartup : class - => AddBeefEventHubSubscriberHost(services, addSubscriberTypeServices, EventSubscriberHostArgs.GetSubscriberTypes(typeof(TStartup).Assembly)); - - /// - /// Adds a scoped service to instantiate a new instance using the specified subscriber types. - /// - /// The . - /// Indicates whether to add all the as transient services. - /// One or more types. - /// The for fluent-style method-chaining. - public static IServiceCollection AddBeefEventHubSubscriberHost(this IServiceCollection services, bool addSubscriberTypeServices = true, params Type[] eventSubscriberTypes) + public static IServiceCollection AddBeefEventHubSubscriberHost(this IServiceCollection services, EventSubscriberHostArgs args, bool addSubscriberTypeServices = true) { if (services == null) throw new ArgumentNullException(nameof(services)); - services.AddScoped(sp => EventSubscriberHostArgs.Create(sp, eventSubscriberTypes)) + if (args == null) + throw new ArgumentNullException(nameof(args)); + + services.AddScoped(sp => args.UseServiceProvider(sp)) .AddScoped(); if (addSubscriberTypeServices) { - foreach (var type in eventSubscriberTypes) + foreach (var type in args.GetSubscriberTypes()) { services.AddTransient(type); } diff --git a/src/Beef.Events/Subscribe/EventDataSubscriberHost.cs b/src/Beef.Events/Subscribe/EventDataSubscriberHost.cs index 6cfb521d7..330fd6b57 100644 --- a/src/Beef.Events/Subscribe/EventDataSubscriberHost.cs +++ b/src/Beef.Events/Subscribe/EventDataSubscriberHost.cs @@ -1,9 +1,6 @@ // Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef -using Microsoft.Extensions.Logging; -using System; using System.Linq; -using System.Reflection; using System.Threading.Tasks; namespace Beef.Events.Subscribe @@ -19,49 +16,6 @@ public class EventDataSubscriberHost : EventSubscriberHost /// The . public EventDataSubscriberHost(EventSubscriberHostArgs args) : base(args) { } - /// - /// Indicates that multiple messages () can be processed. - /// - /// The instance (to support fluent-style method chaining). - public EventDataSubscriberHost AllowMultipleMessages() - { - AreMultipleMessagesSupported = true; - return this; - } - - /// - /// Sets the value. - /// - /// The value. - /// The instance (to support fluent-style method chaining). - public EventDataSubscriberHost NotSubscribed(ResultHandling handling) - { - NotSubscribedHandling = handling; - return this; - } - - /// - /// Sets the value. - /// - /// The value. - /// The instance (to support fluent-style method chaining). - public EventDataSubscriberHost DataNotFound(ResultHandling handling) - { - DataNotFoundHandling = handling; - return this; - } - - /// - /// Sets the value. - /// - /// The value. - /// The instance (to support fluent-style method chaining). - public EventDataSubscriberHost InvalidData(ResultHandling handling) - { - InvalidDataHandling = handling; - return this; - } - /// /// Performs the receive processing for one or more instances. /// @@ -71,11 +25,11 @@ public async Task ReceiveAsync(params EventData[] events) if (events == null || events.Length == 0) return; - if (events.Length != 1 && !AreMultipleMessagesSupported) - throw new EventSubscriberException($"The {nameof(EventDataSubscriberHost)} does not AllowMultipleMessages; there were {events.Length} event messages."); + if (events.Length != 1 && !Args.AreMultipleMessagesSupported) + throw new EventSubscriberException($"The '{nameof(EventDataSubscriberHost)}' does not AllowMultipleMessages; there were {events.Length} event messages."); if (events.Any(x => string.IsNullOrEmpty(x.Subject))) - throw new EventSubscriberException($"The {nameof(EventDataSubscriberHost)} does not allow event messages where the `Subject` is not specified."); + throw new EventSubscriberException($"The '{nameof(EventDataSubscriberHost)}' does not allow event messages where the 'Subject' is not specified."); foreach (var @event in events) { diff --git a/src/Beef.Events/Subscribe/EventHubSubscriberHost.cs b/src/Beef.Events/Subscribe/EventHubSubscriberHost.cs index f4d52de90..748cfe97a 100644 --- a/src/Beef.Events/Subscribe/EventHubSubscriberHost.cs +++ b/src/Beef.Events/Subscribe/EventHubSubscriberHost.cs @@ -11,8 +11,6 @@ namespace Beef.Events.Subscribe /// public class EventHubSubscriberHost : EventSubscriberHost { - private Action? _updateExecutionContext; - /// /// Initializes a new instance of the with the specified . /// @@ -26,76 +24,6 @@ public EventHubSubscriberHost(EventSubscriberHostArgs args) : base(args) args.AuditWriter = (result) => throw new EventSubscriberStopException(result); } - /// - /// Sets the value. - /// - /// The value. - /// The instance (to support fluent-style method chaining). - public EventHubSubscriberHost InvalidEventData(ResultHandling handling) - { - InvalidEventDataHandling = handling; - return this; - } - - /// - /// Sets the value. - /// - /// The value. - /// The instance (to support fluent-style method chaining). - public EventHubSubscriberHost NotSubscribed(ResultHandling handling) - { - NotSubscribedHandling = handling; - return this; - } - - /// - /// Sets the value. - /// - /// The value. - /// The instance (to support fluent-style method chaining). - public EventHubSubscriberHost DataNotFound(ResultHandling handling) - { - DataNotFoundHandling = handling; - return this; - } - - /// - /// Sets the value. - /// - /// The value. - /// The instance (to support fluent-style method chaining). - public EventHubSubscriberHost InvalidData(ResultHandling handling) - { - InvalidDataHandling = handling; - return this; - } - - /// - /// Provides a means to override the update of the . - /// - /// The action to update the . - /// The instance to support fluent-style method chaining. - public EventHubSubscriberHost ExecutionContext(Action updateExecutionContext) - { - _updateExecutionContext = updateExecutionContext; - return this; - } - - /// - /// Overrides the . - /// - /// The to update. - /// The that will process the message. - /// The . - /// The . - protected override void UpdateExecutionContext(ExecutionContext executionContext, IEventSubscriber subscriber, EventData @event) - { - if (_updateExecutionContext == null) - base.UpdateExecutionContext(executionContext, subscriber, @event); - else - _updateExecutionContext(executionContext, subscriber, @event); - } - /// /// Performs the receive processing for an instance. /// @@ -105,7 +33,7 @@ public async Task ReceiveAsync(EventHubs.EventData @event) if (@event == null) return; - // Convert EventHubs.EventData to Beef.EventData.. + // Convert EventHubs.EventData to Beef.EventData. var (subject, action, _) = @event.GetBeefMetadata(); await ReceiveAsync(subject, action, (subscriber) => { diff --git a/src/Beef.Events/Subscribe/EventSubscriber.cs b/src/Beef.Events/Subscribe/EventSubscriber.cs index f941063db..849a4092f 100644 --- a/src/Beef.Events/Subscribe/EventSubscriber.cs +++ b/src/Beef.Events/Subscribe/EventSubscriber.cs @@ -22,17 +22,17 @@ public abstract class EventSubscriberBase : IEventSubscriber public UnhandledExceptionHandling UnhandledExceptionHandling { get; protected set; } = UnhandledExceptionHandling.Stop; /// - /// Gets or sets the for a with a status (overrides ). + /// Gets or sets the for a with a status (overrides ). /// public ResultHandling? InvalidEventDataHandling { get; set; } /// - /// Gets or sets the for a with a status (overrides ). + /// Gets or sets the for a with a status (overrides ). /// public ResultHandling? DataNotFoundHandling { get; set; } /// - /// Gets or sets the the for a with a status (overrides ). + /// Gets or sets the the for a with a status (overrides ). /// public ResultHandling? InvalidDataHandling { get; set; } diff --git a/src/Beef.Events/Subscribe/EventSubscriberHost.cs b/src/Beef.Events/Subscribe/EventSubscriberHost.cs index 737d05cb6..1ee635613 100644 --- a/src/Beef.Events/Subscribe/EventSubscriberHost.cs +++ b/src/Beef.Events/Subscribe/EventSubscriberHost.cs @@ -38,41 +38,6 @@ protected static string GetUsername(IEventSubscriber subscriber, EventData @even /// public EventSubscriberHostArgs Args { get; private set; } - /// - /// Indicates whether multiple messages () can be processed; default is false. - /// - public bool AreMultipleMessagesSupported { get; set; } - - /// - /// Gets the for a with a status (can be overriden by an ). Defaults to . - /// - public ResultHandling NotSubscribedHandling { get; set; } = ResultHandling.ContinueSilent; - - /// - /// Gets the for a with a status (can be overriden by an ). Defaults to . - /// - public ResultHandling DataNotFoundHandling { get; set; } = ResultHandling.Stop; - - /// - /// Gets the for a with a status (can be overriden by an ). Defaults to . - /// - public ResultHandling InvalidEventDataHandling { get; set; } = ResultHandling.Stop; - - /// - /// Gets the for a with a status (can be overriden by an ). Defaults to . - /// - public ResultHandling InvalidDataHandling { get; set; } = ResultHandling.Stop; - - /// - /// Gets or sets the subject path seperator (see ). - /// - public string SubjectPathSeparator { get; set; } = "."; - - /// - /// Gets or sets the subject template wildcard (see ). - /// - public string SubjectTemplateWildcard { get; set; } = "*"; - /// /// Receives the message and processes when the and has been subscribed. /// @@ -92,7 +57,7 @@ protected async Task ReceiveAsync(string? subject, string? action, Func< return CheckResult(Result.InvalidEventData(null, "EventData is invalid; Subject is required."), null, null, null); // Match a subscriber to the subject + template supplied. - var subscriber = Args.CreateEventSubscriber(SubjectTemplateWildcard, SubjectPathSeparator, subject, action); + var subscriber = Args.CreateEventSubscriber(Args.SubjectTemplateWildcard, Args.SubjectPathSeparator, subject, action); if (subscriber == null) return CheckResult(Result.NotSubscribed(), subject, action, null); @@ -120,7 +85,11 @@ protected async Task ReceiveAsync(string? subject, string? action, Func< // Create and set the execution context for the event. ExecutionContext.Reset(); var ec = Args.ServiceProvider.GetService(); - UpdateExecutionContext(ec, subscriber, @event); + if (Args.UpdateExecutionContext == null) + UpdateExecutionContext(ec, subscriber, @event); + else + Args.UpdateExecutionContext(ec, subscriber, @event); + ec.ServiceProvider = Args.ServiceProvider; ec.CorrelationId = @event.CorrelationId; ExecutionContext.SetCurrent(ec); @@ -161,19 +130,19 @@ private Result CheckResult(Result result, string? subject, string? action, IEven break; case SubscriberStatus.InvalidEventData: - HandleTheHandling(result, result.ResultHandling ?? subscriber?.InvalidEventDataHandling ?? InvalidEventDataHandling); + HandleTheHandling(result, result.ResultHandling ?? subscriber?.InvalidEventDataHandling ?? Args.InvalidEventDataHandling); break; case SubscriberStatus.NotSubscribed: - HandleTheHandling(result, result.ResultHandling ?? NotSubscribedHandling); + HandleTheHandling(result, result.ResultHandling ?? Args.NotSubscribedHandling); break; case SubscriberStatus.DataNotFound: - HandleTheHandling(result, result.ResultHandling ?? subscriber?.DataNotFoundHandling ?? DataNotFoundHandling); + HandleTheHandling(result, result.ResultHandling ?? subscriber?.DataNotFoundHandling ?? Args.DataNotFoundHandling); break; case SubscriberStatus.InvalidData: - HandleTheHandling(result, result.ResultHandling ?? subscriber?.InvalidDataHandling ?? InvalidDataHandling); + HandleTheHandling(result, result.ResultHandling ?? subscriber?.InvalidDataHandling ?? Args.InvalidDataHandling); break; case SubscriberStatus.ExceptionContinue: @@ -214,10 +183,8 @@ private void HandleTheHandling(Result result, ResultHandling handling) /// The to update. /// The that will process the message. /// The . - /// When overridding it is the responsibility of the overridder to honour the selection. -#pragma warning disable CA1716 // Identifiers should not match keywords; by-design, is the best name. - protected virtual void UpdateExecutionContext(ExecutionContext executionContext, IEventSubscriber subscriber, EventData @event) -#pragma warning restore CA1716 + /// + private static void UpdateExecutionContext(ExecutionContext executionContext, IEventSubscriber subscriber, EventData @event) { if (executionContext == null) throw new ArgumentNullException(nameof(executionContext)); diff --git a/src/Beef.Events/Subscribe/EventSubscriberHostArgs.cs b/src/Beef.Events/Subscribe/EventSubscriberHostArgs.cs index 76942d0c1..9b72dda7a 100644 --- a/src/Beef.Events/Subscribe/EventSubscriberHostArgs.cs +++ b/src/Beef.Events/Subscribe/EventSubscriberHostArgs.cs @@ -18,27 +18,25 @@ public class EventSubscriberHostArgs private ILogger? _logger; /// - /// Creates an using the specified and . + /// Creates an using the specified (to infer the underlying subscribers ). /// - /// The . - /// The where the types are defined. + /// The startup class whereby all the subscribers reside. /// A . - public static EventSubscriberHostArgs Create(IServiceProvider serviceProvider, Assembly subscribersAssembly) => new EventSubscriberHostArgs(serviceProvider, subscribersAssembly); + public static EventSubscriberHostArgs Create() where TStartup : class => Create(typeof(TStartup).Assembly); /// - /// Creates an using the specified and . + /// Creates an using the specified . /// - /// The . - /// One or more types. + /// The where the types are defined. /// A . - public static EventSubscriberHostArgs Create(IServiceProvider serviceProvider, params Type[] eventSubscriberTypes) => new EventSubscriberHostArgs(serviceProvider, eventSubscriberTypes); + public static EventSubscriberHostArgs Create(Assembly subscribersAssembly) => new EventSubscriberHostArgs(subscribersAssembly); /// - /// Gets all the subscriber types from the specified . + /// Creates an using the specified . /// - /// The where the types are defined. - /// An array of subscriber types. - public static Type[] GetSubscriberTypes(Assembly subscribersAssembly) => GetSubscriberConfig(subscribersAssembly ?? throw new ArgumentNullException(nameof(subscribersAssembly))).Select(x => x.EventSubscriberType).ToArray(); + /// One or more types. + /// A . + public static EventSubscriberHostArgs Create(params Type[] eventSubscriberTypes) => new EventSubscriberHostArgs(eventSubscriberTypes); /// /// Gets all the subscriber configuration from the specified assembly. @@ -62,11 +60,9 @@ private static List GetSubscriberConfig(Assembly subscrib /// /// Initializes a new instance of the with a specified . /// - /// The . /// The where the types are defined. - private EventSubscriberHostArgs(IServiceProvider serviceProvider, Assembly subscribersAssembly) + private EventSubscriberHostArgs(Assembly subscribersAssembly) { - ServiceProvider = Check.NotNull(serviceProvider, nameof(serviceProvider)); _subscribers = GetSubscriberConfig(subscribersAssembly ?? throw new ArgumentNullException(nameof(subscribersAssembly))); if (_subscribers.Count == 0) @@ -76,11 +72,9 @@ private EventSubscriberHostArgs(IServiceProvider serviceProvider, Assembly subsc /// /// Initializes a new instance of the with a specified . /// - /// The . /// One or more types. - private EventSubscriberHostArgs(IServiceProvider serviceProvider, params Type[] eventSubscriberTypes) + private EventSubscriberHostArgs(params Type[] eventSubscriberTypes) { - ServiceProvider = Check.NotNull(serviceProvider, nameof(serviceProvider)); if (eventSubscriberTypes == null || eventSubscriberTypes.Length == 0) throw new ArgumentException($"At least one event {nameof(IEventSubscriber)} must be specified to enable execution.", nameof(eventSubscriberTypes)); @@ -100,10 +94,130 @@ private EventSubscriberHostArgs(IServiceProvider serviceProvider, params Type[] } } + /// + /// Uses (sets) the (this will be automatically set by Beef). + /// + /// The . + /// he instance (for fluent-style method chaining). + public EventSubscriberHostArgs UseServiceProvider(IServiceProvider serviceProvider) + { + ServiceProvider = Check.NotNull(serviceProvider, nameof(serviceProvider)); + return this; + } + /// /// Gets the . /// - public IServiceProvider ServiceProvider { get; private set; } + public IServiceProvider? ServiceProvider { get; private set; } + + /// + /// Indicates whether multiple messages () can be processed (where supported); default is false. + /// + public bool AreMultipleMessagesSupported { get; set; } = false; + + /// + /// Gets the for a with a status (can be overriden by an ). Defaults to . + /// + public ResultHandling NotSubscribedHandling { get; set; } = ResultHandling.ContinueSilent; + + /// + /// Gets the for a with a status (can be overriden by an ). Defaults to . + /// + public ResultHandling DataNotFoundHandling { get; set; } = ResultHandling.Stop; + + /// + /// Gets the for a with a status (can be overriden by an ). Defaults to . + /// + public ResultHandling InvalidEventDataHandling { get; set; } = ResultHandling.Stop; + + /// + /// Gets the for a with a status (can be overriden by an ). Defaults to . + /// + public ResultHandling InvalidDataHandling { get; set; } = ResultHandling.Stop; + + /// + /// Gets or sets the subject path seperator (see ). + /// + public string SubjectPathSeparator { get; set; } = "."; + + /// + /// Gets or sets the subject template wildcard (see ). + /// + public string SubjectTemplateWildcard { get; set; } = "*"; + + /// + /// Gets the action that overrides the update of the . + /// + internal Action? UpdateExecutionContext { get; private set; } + + /// + /// Sets the value to true. + /// + /// The instance (to support fluent-style method chaining). + public EventSubscriberHostArgs AllowMultipleMessages() + { + AreMultipleMessagesSupported = true; + return this; + } + + /// + /// Sets the value. + /// + /// The value. + /// The instance (to support fluent-style method chaining). + public EventSubscriberHostArgs InvalidEventData(ResultHandling handling) + { + InvalidEventDataHandling = handling; + return this; + } + + /// + /// Sets the value. + /// + /// The value. + /// The instance (to support fluent-style method chaining). + public EventSubscriberHostArgs NotSubscribed(ResultHandling handling) + { + NotSubscribedHandling = handling; + return this; + } + + /// + /// Sets the value. + /// + /// The value. + /// The instance (to support fluent-style method chaining). + public EventSubscriberHostArgs DataNotFound(ResultHandling handling) + { + DataNotFoundHandling = handling; + return this; + } + + /// + /// Sets the value. + /// + /// The value. + /// The instance (to support fluent-style method chaining). + public EventSubscriberHostArgs InvalidData(ResultHandling handling) + { + InvalidDataHandling = handling; + return this; + } + + /// + /// Provides a means to override the update of the (is created by using the as instantiated by + /// ). The default will update the , + /// and . + /// + /// The action to update the . + /// The instance to support fluent-style method chaining. + /// Note: when overridding it is the responsibility of the overridder to honour the selection. + /// Note: that the and are set directly by Beef. + public EventSubscriberHostArgs ExecutionContext(Action updateExecutionContext) + { + UpdateExecutionContext = Check.NotNull(updateExecutionContext, nameof(updateExecutionContext)); + return this; + } /// /// Gets the . @@ -116,7 +230,7 @@ private EventSubscriberHostArgs(IServiceProvider serviceProvider, params Type[] public Action? AuditWriter { get; set; } /// - /// Uses (sets) the to write the audit information to the ; this should only be used in testing situations. + /// Uses (sets) the to write the audit information to the ; note: this should only be used in testing situations. /// /// The instance (for fluent-style method chaining). public EventSubscriberHostArgs UseLoggerForAuditing() @@ -125,6 +239,12 @@ public EventSubscriberHostArgs UseLoggerForAuditing() return this; } + /// + /// Gets a list (array) of all the subscriber s. + /// + /// A list (array) of all the subscriber s. + public Type[] GetSubscriberTypes() => _subscribers.Select(x => x.EventSubscriberType).ToArray(); + /// /// Finds and creates the where found for the and . /// @@ -146,8 +266,8 @@ public EventSubscriberHostArgs UseLoggerForAuditing() if (type == null) return null; - return (IEventSubscriber)ServiceProvider.GetService(type) - ?? throw new InvalidOperationException($"Subscriber {type.Name} was unable to be instantiated through the ServiceProvider; please ensure correctly configured."); + return (IEventSubscriber)(ServiceProvider?.GetService(type) + ?? throw new InvalidOperationException($"Subscriber {type.Name} was unable to be instantiated through the ServiceProvider; please ensure correctly configured.")); } /// diff --git a/src/Beef.Events/Subscribe/IEventSubscriber.cs b/src/Beef.Events/Subscribe/IEventSubscriber.cs index beda06fa6..d5a135623 100644 --- a/src/Beef.Events/Subscribe/IEventSubscriber.cs +++ b/src/Beef.Events/Subscribe/IEventSubscriber.cs @@ -21,17 +21,17 @@ public interface IEventSubscriber UnhandledExceptionHandling UnhandledExceptionHandling { get; } /// - /// Gets the for a with a status (overrides ). + /// Gets the for a with a status (overrides ). /// ResultHandling? InvalidEventDataHandling { get; } /// - /// Gets the for a with a status (overrides ). + /// Gets the for a with a status (overrides ). /// ResultHandling? DataNotFoundHandling { get; } /// - /// Gets the for a with a status (overrides ). + /// Gets the for a with a status (overrides ). /// ResultHandling? InvalidDataHandling { get; } diff --git a/src/Beef.Grpc/Beef.Grpc.csproj b/src/Beef.Grpc/Beef.Grpc.csproj index 049971ba7..6f512dff2 100644 --- a/src/Beef.Grpc/Beef.Grpc.csproj +++ b/src/Beef.Grpc/Beef.Grpc.csproj @@ -2,7 +2,7 @@ netstandard2.1 - 4.1.1 + 4.1.2 Beef Developers Avanade Business Entity Execution Framework (Beef) gRPC framework. diff --git a/src/Beef.Grpc/CHANGELOG.md b/src/Beef.Grpc/CHANGELOG.md index 93de6a0d8..a2c7780ca 100644 --- a/src/Beef.Grpc/CHANGELOG.md +++ b/src/Beef.Grpc/CHANGELOG.md @@ -2,6 +2,9 @@ Represents the **NuGet** versions. +## v4.1.2 +- *Fixed:* An HTTP Delete will now catch a `NotFoundException` and return an HTTP Status Code 204 (no content) as a delete is considered idempotent. + ## v4.1.1 - *Enhancement:* Introduction of Dependency Injection support. diff --git a/src/Beef.Grpc/GrpcService.cs b/src/Beef.Grpc/GrpcService.cs index 0392e0806..9cce6d167 100644 --- a/src/Beef.Grpc/GrpcService.cs +++ b/src/Beef.Grpc/GrpcService.cs @@ -222,6 +222,9 @@ private async Task ExecuteResultAsyncInternal(Func func) catch (grpc.RpcException) { throw; } catch (Exception ex) { + if (ex is NotFoundException && OperationType == OperationType.Delete) + HandleResponseStatus(HttpStatusCode.NoContent); + ExceptionHandler(this, ex); throw; } @@ -230,7 +233,7 @@ private async Task ExecuteResultAsyncInternal(Func func) /// /// Does the actual execution of the asynchronously where there is a . /// - [DebuggerStepThrough()] + //[DebuggerStepThrough()] private async Task ExecuteResultAsyncInternal(Func> func) { try @@ -253,6 +256,12 @@ private async Task ExecuteResultAsyncInternal(Func + diff --git a/templates/Beef.Template.Solution/CHANGELOG.md b/templates/Beef.Template.Solution/CHANGELOG.md index 631d09c91..24743b3eb 100644 --- a/templates/Beef.Template.Solution/CHANGELOG.md +++ b/templates/Beef.Template.Solution/CHANGELOG.md @@ -2,6 +2,9 @@ Represents the **NuGet** versions. +## v4.1.2 +- *Fixed:* Updated referenced *Beef* NuGet references to latest. + ## v4.1.1 - *Enhancement:* Introduction of Dependency Injection support. diff --git a/templates/Beef.Template.Solution/content/.template.config/template.json b/templates/Beef.Template.Solution/content/.template.config/template.json index 13386fe75..869368304 100644 --- a/templates/Beef.Template.Solution/content/.template.config/template.json +++ b/templates/Beef.Template.Solution/content/.template.config/template.json @@ -68,6 +68,14 @@ "implement_none": { "type": "computed", "value": "(datasource == \"None\")" + }, + "created_date": { + "type": "generated", + "generator": "now", + "parameters": { + "format": "yyyyMMdd" + }, + "replaces": "20190101" } }, "sources": [ diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Api/Company.AppName.Api.csproj b/templates/Beef.Template.Solution/content/Company.AppName.Api/Company.AppName.Api.csproj index 7ff688264..9b9f22a59 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Api/Company.AppName.Api.csproj +++ b/templates/Beef.Template.Solution/content/Company.AppName.Api/Company.AppName.Api.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Business/Company.AppName.Business.csproj b/templates/Beef.Template.Solution/content/Company.AppName.Business/Company.AppName.Business.csproj index f880e83f6..2bf63115c 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Business/Company.AppName.Business.csproj +++ b/templates/Beef.Template.Solution/content/Company.AppName.Business/Company.AppName.Business.csproj @@ -5,13 +5,13 @@ - + - + - + diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/AppNameEfDb.cs b/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/AppNameEfDb.cs index a30260645..e44910b7f 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/AppNameEfDb.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/AppNameEfDb.cs @@ -11,6 +11,6 @@ public class AppNameEfDb : EfDbBase /// Initializes a new instance of the class. /// /// The entity framework database context. - public AppNameEfDb(AppNameEfDbContext dbContext) : base(dbContext) => OnUpdatePreReadForNotFound = true; + public AppNameEfDb(AppNameEfDbContext dbContext) : base(dbContext) { } } } \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/PersonData.cs b/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/PersonData.cs index 73790e9bb..1a449b649 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/PersonData.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/PersonData.cs @@ -46,11 +46,11 @@ private void GetByArgsOnQuery(DatabaseParameters p, PersonArgs? args, IDatabaseA } #endif #if (implement_cosmos) - private IQueryable GetByArgsOnQuery(IQueryable q, PersonArgs? args, ICosmosDbArgs dbArgs) + private IQueryable GetByArgsOnQuery(IQueryable q, PersonArgs? args, ICosmosDbArgs dbArgs) { q = q.WhereWildcard(x => x.FirstName, args?.FirstName); q = q.WhereWildcard(x => x.LastName, args?.LastName); - q = q.WhereWith(args?.Genders, x => args!.Genders!.ToCodeList().Contains(x.GenderSid)); + q = q.WhereWith(args?.Genders, x => args!.Genders!.ToCodeList().Contains(x.Gender)); return q.OrderBy(x => x.LastName); } #endif diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Business/Validation/PersonValidator.cs b/templates/Beef.Template.Solution/content/Company.AppName.Business/Validation/PersonValidator.cs index 82fbb96c2..6d7f36ff7 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Business/Validation/PersonValidator.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Business/Validation/PersonValidator.cs @@ -17,7 +17,7 @@ public PersonValidator() Property(x => x.FirstName).Mandatory().String(100); Property(x => x.LastName).Mandatory().String(100); Property(x => x.Gender).Mandatory().IsValid(); - Property(x => x.Birthday).Mandatory().CompareValue(CompareOperator.LessThanEqual, DateTime.Now, "Today"); + Property(x => x.Birthday).Mandatory().CompareValue(CompareOperator.LessThanEqual, _ => DateTime.UtcNow, _ => "Today"); } } } \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/Company.AppName.CodeGen.csproj b/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/Company.AppName.CodeGen.csproj index 725ffd254..8420ce21c 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/Company.AppName.CodeGen.csproj +++ b/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/Company.AppName.CodeGen.csproj @@ -5,6 +5,6 @@ enable - + \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/Company.AppName.xml b/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/Company.AppName.xml index 1f3fe1483..ecd22ba0f 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/Company.AppName.xml +++ b/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/Company.AppName.xml @@ -1,6 +1,6 @@  - + @@ -12,7 +12,7 @@ - + @@ -24,9 +24,9 @@ - + - + diff --git a/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/Company.RefData.xml b/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/Company.RefData.xml index d24fde485..4b781be51 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/Company.RefData.xml +++ b/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/Company.RefData.xml @@ -1,17 +1,17 @@  - + - + - + - + - + \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Common/Company.AppName.Common.csproj b/templates/Beef.Template.Solution/content/Company.AppName.Common/Company.AppName.Common.csproj index 1656cf511..223859723 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Common/Company.AppName.Common.csproj +++ b/templates/Beef.Template.Solution/content/Company.AppName.Common/Company.AppName.Common.csproj @@ -4,7 +4,7 @@ enable - + diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Database/Company.AppName.Database.csproj b/templates/Beef.Template.Solution/content/Company.AppName.Database/Company.AppName.Database.csproj index 790c7a28b..3cbea4747 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Database/Company.AppName.Database.csproj +++ b/templates/Beef.Template.Solution/content/Company.AppName.Database/Company.AppName.Database.csproj @@ -7,11 +7,11 @@ - + - + diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Database/Company.AppName.Database.xml b/templates/Beef.Template.Solution/content/Company.AppName.Database/Company.AppName.Database.xml index d56f1084b..e76318621 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Database/Company.AppName.Database.xml +++ b/templates/Beef.Template.Solution/content/Company.AppName.Database/Company.AppName.Database.xml @@ -1,5 +1,5 @@  - +
diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Test/Company.AppName.Test.csproj b/templates/Beef.Template.Solution/content/Company.AppName.Test/Company.AppName.Test.csproj index bbd5ef6c4..7c26c24f9 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Test/Company.AppName.Test.csproj +++ b/templates/Beef.Template.Solution/content/Company.AppName.Test/Company.AppName.Test.csproj @@ -18,7 +18,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Test/Data/Person.yaml b/templates/Beef.Template.Solution/content/Company.AppName.Test/Data/Data.yaml similarity index 100% rename from templates/Beef.Template.Solution/content/Company.AppName.Test/Data/Person.yaml rename to templates/Beef.Template.Solution/content/Company.AppName.Test/Data/Data.yaml diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Test/FixtureSetup.cs b/templates/Beef.Template.Solution/content/Company.AppName.Test/FixtureSetup.cs index 098a1e9c8..8a7ba9736 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Test/FixtureSetup.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Test/FixtureSetup.cs @@ -37,10 +37,9 @@ public void OneTimeSetUp() TestSetUp.RegisterSetUp(async (count, _) => { - return await DatabaseExecutor.RunAsync( - count == 0 ? DatabaseExecutorCommand.ResetAndDatabase : DatabaseExecutorCommand.ResetAndData, - config["ConnectionStrings:Database"], useBeefDbo: true, - typeof(Database.Program).Assembly, Assembly.GetExecutingAssembly()).ConfigureAwait(false) == 0; + return await DatabaseExecutor.RunAsync(new DatabaseExecutorArgs( + count == 0 ? DatabaseExecutorCommand.ResetAndDatabase : DatabaseExecutorCommand.ResetAndData, config["ConnectionStrings:Database"], + typeof(Database.Program).Assembly, Assembly.GetExecutingAssembly()) { UseBeefDbo = true } ).ConfigureAwait(false) == 0; }); } #endif diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Test/PersonTest.cs b/templates/Beef.Template.Solution/content/Company.AppName.Test/PersonTest.cs index 3bb33e76a..0328eedc1 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Test/PersonTest.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Test/PersonTest.cs @@ -16,51 +16,24 @@ namespace Company.AppName.Test [TestFixture, NonParallelizable] public class PersonTest : UsingAgentTesterServer { - #region Validators - - [Test, TestSetUp] - public void A110_Validation_Empty() - { - ExpectValidationException.Throws( - () => PersonValidator.Default.Validate(new Person()).ThrowOnError(), - "First Name is required.", - "Last Name is required.", - "Gender is required.", - "Birthday is required."); - } - - [Test, TestSetUp] - public void A120_Validation_Invalid() - { - ExpectValidationException.Throws( - () => PersonValidator.Default.Validate(new Person { FirstName = 'x'.ToLongString(), LastName = 'x'.ToLongString(), GenderSid = "X", Birthday = DateTime.Now.AddDays(1) }).ThrowOnError(), - "First Name must not exceed 100 characters in length.", - "Last Name must not exceed 100 characters in length.", - "Gender is invalid.", - "Birthday must be less than or equal to Today."); - } - - #endregion - #region Get [Test, TestSetUp] - public void B110_Get_NotFound() + public void A110_Get_NotFound() { - AgentTester.Test() + AgentTester.Test() .ExpectStatusCode(HttpStatusCode.NotFound) - .ExpectErrorType(Beef.ErrorType.NotFoundError) .Run(a => a.GetAsync(404.ToGuid())); } [Test, TestSetUp] - public void B120_Get_Found() + public void A120_Get_Found() { - AgentTester.Test() + AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) .IgnoreChangeLog() .IgnoreETag() - .ExpectValue((t) => new Person + .ExpectValue(_ => new Person { Id = 1.ToGuid(), FirstName = "Wendy", @@ -72,15 +45,15 @@ public void B120_Get_Found() } [Test, TestSetUp] - public void B120_Get_Modified_NotModified() + public void A120_Get_Modified_NotModified() { - var v = AgentTester.Test() + var v = AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetAsync(1.ToGuid(), new WebApiRequestOptions { ETag = TestSetUp.ConcurrencyErrorETag })).Value; + .Run(a => a.GetAsync(1.ToGuid(), new WebApiRequestOptions { ETag = TestSetUp.ConcurrencyErrorETag })).Value!; Assert.NotNull(v); - AgentTester.Test() + AgentTester.Test() .ExpectStatusCode(HttpStatusCode.NotModified) .Run(a => a.GetAsync(1.ToGuid(), new WebApiRequestOptions { ETag = v.ETag })); } @@ -90,11 +63,11 @@ public void B120_Get_Modified_NotModified() #region GetByArgs [Test, TestSetUp] - public void B210_GetByArgs_All() + public void A210_GetByArgs_All() { var v = AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetByArgsAsync(null)).Value; + .Run(a => a.GetByArgsAsync(null)).Value!; Assert.IsNotNull(v); Assert.IsNotNull(v.Result); @@ -103,11 +76,11 @@ public void B210_GetByArgs_All() } [Test, TestSetUp] - public void B220_GetByArgs_FirstName() + public void A220_GetByArgs_FirstName() { var v = AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetByArgsAsync(new PersonArgs { FirstName = "*a*" })).Value; + .Run(a => a.GetByArgsAsync(new PersonArgs { FirstName = "*a*" })).Value!; Assert.IsNotNull(v); Assert.IsNotNull(v.Result); @@ -116,11 +89,11 @@ public void B220_GetByArgs_FirstName() } [Test, TestSetUp] - public void B230_GetByArgs_LastName() + public void A230_GetByArgs_LastName() { var v = AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetByArgsAsync(new PersonArgs { LastName = "s*" })).Value; + .Run(a => a.GetByArgsAsync(new PersonArgs { LastName = "s*" })).Value!; Assert.IsNotNull(v); Assert.IsNotNull(v.Result); @@ -129,11 +102,11 @@ public void B230_GetByArgs_LastName() } [Test, TestSetUp] - public void B240_GetByArgs_Gender() + public void A240_GetByArgs_Gender() { var v = AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetByArgsAsync(new PersonArgs { GendersSids = new List { "F" } })).Value; + .Run(a => a.GetByArgsAsync(new PersonArgs { GendersSids = new List { "F" } })).Value!; Assert.IsNotNull(v); Assert.IsNotNull(v.Result); @@ -142,11 +115,11 @@ public void B240_GetByArgs_Gender() } [Test, TestSetUp] - public void B250_GetByArgs_Empty() + public void A250_GetByArgs_Empty() { var v = AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetByArgsAsync(new PersonArgs { LastName = "s*", FirstName = "b*", GendersSids = new List { "F" } })).Value; + .Run(a => a.GetByArgsAsync(new PersonArgs { LastName = "s*", FirstName = "b*", GendersSids = new List { "F" } })).Value!; Assert.IsNotNull(v); Assert.IsNotNull(v.Result); @@ -154,7 +127,7 @@ public void B250_GetByArgs_Empty() } [Test, TestSetUp] - public void B260_GetByArgs_FieldSelection() + public void A260_GetByArgs_FieldSelection() { var r = AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) @@ -169,7 +142,7 @@ public void B260_GetByArgs_FieldSelection() } [Test, TestSetUp] - public void B270_GetByArgs_RefDataText() + public void A270_GetByArgs_RefDataText() { var r = AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) @@ -188,7 +161,7 @@ public void B270_GetByArgs_RefDataText() #region Create [Test, TestSetUp] - public void C110_Create() + public void B110_Create() { var v = new Person { @@ -204,14 +177,14 @@ public void C110_Create() .ExpectChangeLogCreated() .ExpectETag() .ExpectUniqueKey() - .ExpectValue((t) => v) - .ExpectEvent("Company.AppName.Person.*", "Create") - .Run(a => a.CreateAsync(v)).Value; + .ExpectValue(_ => v) + .ExpectEvent("Company.AppName.Person.*", "Created") + .Run(a => a.CreateAsync(v)).Value!; // Check the value was created properly. - AgentTester.Test() + AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .ExpectValue((t) => v) + .ExpectValue(_ => v) .Run(a => a.GetAsync(v.Id)); } @@ -220,51 +193,48 @@ public void C110_Create() #region Update [Test, TestSetUp] - public void D110_Update_NotFound() + public void C110_Update_NotFound() { // Get an existing value. - var v = AgentTester.Test() + var v = AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetAsync(2.ToGuid())).Value; + .Run(a => a.GetAsync(2.ToGuid())).Value!; // Try updating with an invalid identifier. AgentTester.Test() .ExpectStatusCode(HttpStatusCode.NotFound) - .ExpectErrorType(ErrorType.NotFoundError) .Run(a => a.UpdateAsync(v, 404.ToGuid())); } [Test, TestSetUp] - public void D120_Update_Concurrency() + public void C120_Update_Concurrency() { // Get an existing value. var id = 2.ToGuid(); - var v = AgentTester.Test() + var v = AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetAsync(id)).Value; + .Run(a => a.GetAsync(id)).Value!; // Try updating the value with an invalid eTag (if-match). AgentTester.Test() .ExpectStatusCode(HttpStatusCode.PreconditionFailed) - .ExpectErrorType(ErrorType.ConcurrencyError) .Run(a => a.UpdateAsync(v, id, new WebApiRequestOptions { ETag = TestSetUp.ConcurrencyErrorETag })); // Try updating the value with an invalid eTag. v.ETag = TestSetUp.ConcurrencyErrorETag; AgentTester.Test() .ExpectStatusCode(HttpStatusCode.PreconditionFailed) - .ExpectErrorType(ErrorType.ConcurrencyError) .Run(a => a.UpdateAsync(v, id)); } [Test, TestSetUp] - public void D130_Update() + public void C130_Update() { // Get an existing value. var id = 2.ToGuid(); - var v = AgentTester.Test() + var v = AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetAsync(id)).Value; + .Run(a => a.GetAsync(id)).Value!; // Make some changes to the data. v.FirstName += "X"; @@ -276,14 +246,14 @@ public void D130_Update() .ExpectChangeLogUpdated() .ExpectETag(v.ETag) .ExpectUniqueKey() - .ExpectValue((t) => v) - .ExpectEvent($"Company.AppName.Person.{id}", "Update") - .Run(a => a.UpdateAsync(v, id)).Value; + .ExpectValue(_ => v) + .ExpectEvent($"Company.AppName.Person.{id}", "Updated") + .Run(a => a.UpdateAsync(v, id)).Value!; // Check the value was updated properly. - AgentTester.Test() + AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .ExpectValue((t) => v) + .ExpectValue(_ => v) .Run(a => a.GetAsync(id)); } @@ -292,51 +262,48 @@ public void D130_Update() #region Patch [Test, TestSetUp] - public void E110_Patch_NotFound() + public void D110_Patch_NotFound() { // Get an existing value. - var v = AgentTester.Test() + var v = AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetAsync(2.ToGuid())).Value; + .Run(a => a.GetAsync(2.ToGuid())).Value!; // Try patching with an invalid identifier. AgentTester.Test() .ExpectStatusCode(HttpStatusCode.NotFound) - .ExpectErrorType(ErrorType.NotFoundError) .Run(a => a.PatchAsync(WebApiPatchOption.MergePatch, "{ \"lastName\": \"Smithers\" }", 404.ToGuid())); } [Test, TestSetUp] - public void E120_Patch_Concurrency() + public void D120_Patch_Concurrency() { // Get an existing value. var id = 2.ToGuid(); - var v = AgentTester.Test() + var v = AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetAsync(id)).Value; + .Run(a => a.GetAsync(id)).Value!; // Try updating the value with an invalid eTag (if-match). AgentTester.Test() .ExpectStatusCode(HttpStatusCode.PreconditionFailed) - .ExpectErrorType(ErrorType.ConcurrencyError) .Run(a => a.PatchAsync(WebApiPatchOption.MergePatch, "{ \"lastName\": \"Smithers\" }", id, new WebApiRequestOptions { ETag = TestSetUp.ConcurrencyErrorETag })); // Try updating the value with an eTag header (json payload eTag is ignored). v.ETag = TestSetUp.ConcurrencyErrorETag; AgentTester.Test() .ExpectStatusCode(HttpStatusCode.PreconditionFailed) - .ExpectErrorType(ErrorType.ConcurrencyError) .Run(a => a.PatchAsync(WebApiPatchOption.MergePatch, "{{ \"lastName\": \"Smithers\", \"etag\": {TestSetUp.ConcurrencyErrorETag} }}", id)); } [Test, TestSetUp] - public void E130_Patch() + public void D130_Patch() { // Get an existing value. var id = 2.ToGuid(); - var v = AgentTester.Test() + var v = AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .Run(a => a.GetAsync(id)).Value; + .Run(a => a.GetAsync(id)).Value!; // Make some changes to the data. v.LastName = "Smithers"; @@ -347,14 +314,14 @@ public void E130_Patch() .ExpectChangeLogUpdated() .ExpectETag(v.ETag) .ExpectUniqueKey() - .ExpectValue((t) => v) - .ExpectEvent($"Company.AppName.Person.{id}", "Update") - .Run(a => a.PatchAsync(WebApiPatchOption.MergePatch, $"{{ \"lastName\": \"{v.LastName}\" }}", id, new WebApiRequestOptions { ETag = v.ETag })).Value; + .ExpectValue(_ => v) + .ExpectEvent($"Company.AppName.Person.{id}", "Updated") + .Run(a => a.PatchAsync(WebApiPatchOption.MergePatch, $"{{ \"lastName\": \"{v.LastName}\" }}", id, new WebApiRequestOptions { ETag = v.ETag })).Value!; // Check the value was updated properly. - AgentTester.Test() + AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) - .ExpectValue((t) => v) + .ExpectValue(_ => v) .Run(a => a.GetAsync(id)); } @@ -363,30 +330,28 @@ public void E130_Patch() #region Delete [Test, TestSetUp] - public void F110_Delete() + public void E110_Delete() { // Check value exists. var id = 4.ToGuid(); - AgentTester.Test() + AgentTester.Test() .ExpectStatusCode(HttpStatusCode.OK) .Run(a => a.GetAsync(id)); // Delete value. AgentTester.Test() .ExpectStatusCode(HttpStatusCode.NoContent) - .ExpectEvent($"Company.AppName.Person.{id}", "Delete") + .ExpectEvent($"Company.AppName.Person.{id}", "Deleted") .Run(a => a.DeleteAsync(id)); // Check value no longer exists. - AgentTester.Test() + AgentTester.Test() .ExpectStatusCode(HttpStatusCode.NotFound) - .ExpectErrorType(Beef.ErrorType.NotFoundError) .Run(a => a.GetAsync(id)); - // Delete again (should still be successful as a Delete is idempotent). + // Delete again (should still be successful as a Delete is idempotent); but no event should be raised. AgentTester.Test() .ExpectStatusCode(HttpStatusCode.NoContent) - .ExpectEvent($"Company.AppName.Person.{id}", "Delete") .Run(a => a.DeleteAsync(id)); } diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Test/Validators/PersonValidatorTest.cs b/templates/Beef.Template.Solution/content/Company.AppName.Test/Validators/PersonValidatorTest.cs new file mode 100644 index 000000000..bb3ed102f --- /dev/null +++ b/templates/Beef.Template.Solution/content/Company.AppName.Test/Validators/PersonValidatorTest.cs @@ -0,0 +1,71 @@ +using Beef.Test.NUnit; +using Company.AppName.Business.Validation; +using Company.AppName.Common.Agents; +using Company.AppName.Common.Entities; +using Moq; +using NUnit.Framework; +using System; + +namespace Company.AppName.Test.Validators +{ + [TestFixture] + public class PersonValidatorTest + { + private readonly Mock _referenceData = new Mock(); + + [OneTimeSetUp] + public void OneTimeSetUp() + { + _referenceData.Setup(x => x.GenderGetAllAsync(null, null)).ReturnsWebApiAgentResultAsync(new GenderCollection { new Gender { Id = Guid.NewGuid(), Code = "F" } }); + } + + [Test, TestSetUp] + public void A110_Validation_Empty() + { + ValidationTester.Test() + .ExpectMessages( + "First Name is required.", + "Last Name is required.", + "Gender is required.", + "Birthday is required.") + .Run(() => PersonValidator.Default.Validate(new Person())); + } + + [Test, TestSetUp] + public void A120_Validation_Invalid() + { + var p = new Person + { + FirstName = 'x'.ToLongString(), + LastName = 'x'.ToLongString(), + GenderSid = "X", + Birthday = DateTime.UtcNow.AddDays(1) + }; + + ValidationTester.Test() + .AddScopedService(_referenceData) + .ExpectMessages( + "First Name must not exceed 100 characters in length.", + "Last Name must not exceed 100 characters in length.", + "Gender is invalid.", + "Birthday must be less than or equal to Today.") + .Run(() => PersonValidator.Default.Validate(p)); + } + + [Test, TestSetUp] + public void A130_Validation_OK() + { + var p = new Person + { + FirstName = "Sam", + LastName = "Reilly", + GenderSid = "F", + Birthday = DateTime.UtcNow.AddYears(-18) + }; + + ValidationTester.Test() + .AddScopedService(_referenceData) + .Run(() => PersonValidator.Default.Validate(p)); + } + } +} \ No newline at end of file diff --git a/tests/Beef.Core.UnitTest/Mapper/EntityMapperTest.cs b/tests/Beef.Core.UnitTest/Mapper/EntityMapperTest.cs index d409d117c..dbd11e0bf 100644 --- a/tests/Beef.Core.UnitTest/Mapper/EntityMapperTest.cs +++ b/tests/Beef.Core.UnitTest/Mapper/EntityMapperTest.cs @@ -167,6 +167,34 @@ public void MapToDest_EntityValue_PropMapper() Assert.AreEqual("AAA", r.AddressX.City); } + [Test] + public void MapToDest_EntityValue_PropMapper2() + { + var r = new PersonB { AddressX = new AddressX { City = "123", Street = "456" } }; + + EntityMapper.Create() + .HasProperty(s => s.Address, d => d.AddressX, p => p.SetMapper(EntityMapper.Create().HasProperty(sa => sa.Street, da => da.City))) + .MapToDest(new PersonA { Address = new Address { Street = "AAA", City = "BBB" } }, r); + + Assert.IsNotNull(r); + Assert.IsNotNull(r.AddressX); + Assert.AreEqual("456", r.AddressX.Street); + Assert.AreEqual("AAA", r.AddressX.City); + } + + [Test] + public void MapToDest_EntityValue_PropMapper3() + { + var r = new PersonB { AddressX = new AddressX { City = "123", Street = "456" } }; + + EntityMapper.Create() + .HasProperty(s => s.Address, d => d.AddressX, p => p.SetMapper(EntityMapper.Create().HasProperty(sa => sa.Street, da => da.City))) + .MapToDest(new PersonA(), r); + + Assert.IsNotNull(r); + Assert.IsNull(r.AddressX); + } + [Test] public void MapToDest_CollNull() { diff --git a/tests/Beef.Data.Cosmos.UnitTest/CosmosDbContainerTest.cs b/tests/Beef.Data.Cosmos.UnitTest/CosmosDbContainerTest.cs index 69a87aaea..b46185b62 100644 --- a/tests/Beef.Data.Cosmos.UnitTest/CosmosDbContainerTest.cs +++ b/tests/Beef.Data.Cosmos.UnitTest/CosmosDbContainerTest.cs @@ -204,8 +204,8 @@ public async Task Delete1Async() ExpectException.Throws("*", () => _db.Persons1.DeleteAsync()); ExpectException.Throws("Only a single key value is currently supported.", () => _db.Persons1.DeleteAsync(1, 2)); - await _db.Persons1.DeleteAsync(404.ToGuid()); - await _db.Persons1.DeleteAsync(100.ToGuid()); + ExpectException.Throws("*", () => _db.Persons1.DeleteAsync(404.ToGuid())); + ExpectException.Throws("*", () => _db.Persons1.DeleteAsync(100.ToGuid())); await _db.Persons1.DeleteAsync(4.ToGuid()); using (var r = await _db.Persons1.Container.ReadItemStreamAsync(4.ToGuid().ToString(), Microsoft.Azure.Cosmos.PartitionKey.None)) @@ -221,8 +221,8 @@ public async Task Delete2Async() ExpectException.Throws("*", () => _db.Persons2.DeleteAsync()); ExpectException.Throws("Only a single key value is currently supported.", () => _db.Persons2.DeleteAsync(1, 2)); - await _db.Persons2.DeleteAsync(404.ToGuid()); - await _db.Persons2.DeleteAsync(100.ToGuid()); + ExpectException.Throws("*", () => _db.Persons2.DeleteAsync(404.ToGuid())); + ExpectException.Throws("*", () => _db.Persons2.DeleteAsync(100.ToGuid())); await _db.Persons2.DeleteAsync(4.ToGuid()); using (var r = await _db.Persons2.Container.ReadItemStreamAsync(4.ToGuid().ToString(), Microsoft.Azure.Cosmos.PartitionKey.None)) @@ -238,8 +238,8 @@ public async Task Delete3Async() ExpectException.Throws("*", () => _db.Persons3.DeleteAsync()); ExpectException.Throws("Only a single key value is currently supported.", () => _db.Persons3.DeleteAsync(1, 2)); - await _db.Persons3.DeleteAsync(404.ToGuid()); - await _db.Persons3.DeleteAsync(100.ToGuid()); + ExpectException.Throws("*", () => _db.Persons3.DeleteAsync(404.ToGuid())); + ExpectException.Throws("*", () => _db.Persons3.DeleteAsync(100.ToGuid())); await _db.Persons3.DeleteAsync(4.ToGuid()); using (var r = await _db.Persons3.Container.ReadItemStreamAsync(4.ToGuid().ToString(), Microsoft.Azure.Cosmos.PartitionKey.None)) diff --git a/tests/Beef.Events.UnitTest/Subscribe/EventDataSubscriberHostTest.cs b/tests/Beef.Events.UnitTest/Subscribe/EventDataSubscriberHostTest.cs index 859dc062b..439a29942 100644 --- a/tests/Beef.Events.UnitTest/Subscribe/EventDataSubscriberHostTest.cs +++ b/tests/Beef.Events.UnitTest/Subscribe/EventDataSubscriberHostTest.cs @@ -76,7 +76,7 @@ public override Task ReceiveAsync(EventData eventData) private EventDataSubscriberHost CreateTestHost(Func create) where T : class { var sp = TestSetUp.CreateServiceProvider(sc => sc.AddTransient(_ => create())); - return new EventDataSubscriberHost(EventSubscriberHostArgs.Create(sp, typeof(T)).UseLoggerForAuditing()); + return new EventDataSubscriberHost(EventSubscriberHostArgs.Create(typeof(T)).UseServiceProvider(sp).UseLoggerForAuditing()); } [Test] @@ -239,7 +239,7 @@ public void C110_TooManySubjectSubscribers() ExpectException.Throws( "There are 2 IEventSubscriber instances subscribing to Subject 'Test.Blah.123' and Action 'CREATE'; there must be only a single subscriber.", - async () => await new EventDataSubscriberHost(EventSubscriberHostArgs.Create(sp, typeof(TestSub), typeof(TestSubS)).UseLoggerForAuditing()).ReceiveAsync(ed)); + async () => await new EventDataSubscriberHost(EventSubscriberHostArgs.Create(typeof(TestSub), typeof(TestSubS)).UseServiceProvider(sp).UseLoggerForAuditing()).ReceiveAsync(ed)); } [Test] @@ -248,8 +248,8 @@ public void C120_DoNotAllowMultipleMessages() var sp = TestSetUp.CreateServiceProvider(); ExpectException.Throws( - "The EventDataSubscriberHost does not AllowMultipleMessages; there were 2 event messages.", - async () => await new EventDataSubscriberHost(EventSubscriberHostArgs.Create(sp, typeof(TestSub)).UseLoggerForAuditing()).ReceiveAsync(new EventData(), new EventData())); + "The 'EventDataSubscriberHost' does not AllowMultipleMessages; there were 2 event messages.", + async () => await new EventDataSubscriberHost(EventSubscriberHostArgs.Create(typeof(TestSub)).UseServiceProvider(sp).UseLoggerForAuditing()).ReceiveAsync(new EventData(), new EventData())); } [Test] @@ -257,7 +257,7 @@ public async Task C130_AllowMultipleMessages() { var sp = TestSetUp.CreateServiceProvider(); - await new EventDataSubscriberHost(EventSubscriberHostArgs.Create(sp, typeof(TestSub)).UseLoggerForAuditing()).AllowMultipleMessages().ReceiveAsync(new EventData { Subject = "X" }, new EventData { Subject = "X" }); + await new EventDataSubscriberHost(EventSubscriberHostArgs.Create(typeof(TestSub)).UseServiceProvider(sp).UseLoggerForAuditing().AllowMultipleMessages()).ReceiveAsync(new EventData { Subject = "X" }, new EventData { Subject = "X" }); } } } \ No newline at end of file diff --git a/tests/Beef.Events.UnitTest/Subscribe/EventSubscriberHostArgsTest.cs b/tests/Beef.Events.UnitTest/Subscribe/EventSubscriberHostArgsTest.cs index 29dd8f90c..2bece7cac 100644 --- a/tests/Beef.Events.UnitTest/Subscribe/EventSubscriberHostArgsTest.cs +++ b/tests/Beef.Events.UnitTest/Subscribe/EventSubscriberHostArgsTest.cs @@ -12,23 +12,22 @@ public class EventSubscriberHostArgsTest [Test] public void Ctor_NoIEventSubscriber() { - var sp = TestSetUp.CreateServiceProvider(); - ExpectException.Throws("*", () => EventSubscriberHostArgs.Create(sp)); - ExpectException.Throws("*", () => EventSubscriberHostArgs.Create(sp, (Type[])null)); - ExpectException.Throws("*", () => EventSubscriberHostArgs.Create(sp, new Type[] { })); + ExpectException.Throws("*", () => EventSubscriberHostArgs.Create()); + ExpectException.Throws("*", () => EventSubscriberHostArgs.Create((Type[])null)); + ExpectException.Throws("*", () => EventSubscriberHostArgs.Create(new Type[] { })); } [Test] public void Ctor_NoSubscribersInAssembly() { - var sp = TestSetUp.CreateServiceProvider(); - ExpectException.Throws("*", () => EventSubscriberHostArgs.Create(sp, typeof(TestAttribute).Assembly)); + ExpectException.Throws("*", () => EventSubscriberHostArgs.Create(typeof(TestAttribute).Assembly)); } [Test] public void Ctor_SubscribersInAssembly() { - var ts = EventSubscriberHostArgs.GetSubscriberTypes(this.GetType().Assembly); + var args = EventSubscriberHostArgs.Create(GetType().Assembly); + var ts = args.GetSubscriberTypes(); Assert.AreEqual(2, ts.Length); } } diff --git a/tools/Beef.CodeGen.Core/Beef.CodeGen.Core.csproj b/tools/Beef.CodeGen.Core/Beef.CodeGen.Core.csproj index 05261ac02..7953c5147 100644 --- a/tools/Beef.CodeGen.Core/Beef.CodeGen.Core.csproj +++ b/tools/Beef.CodeGen.Core/Beef.CodeGen.Core.csproj @@ -4,7 +4,7 @@ Exe netcoreapp3.1 Beef.CodeGen - 4.1.1 + 4.1.2 false @@ -36,13 +36,33 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -71,35 +91,37 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + all diff --git a/tools/Beef.CodeGen.Core/CHANGELOG.md b/tools/Beef.CodeGen.Core/CHANGELOG.md index ff3b5a006..8a4227615 100644 --- a/tools/Beef.CodeGen.Core/CHANGELOG.md +++ b/tools/Beef.CodeGen.Core/CHANGELOG.md @@ -2,6 +2,11 @@ Represents the **NuGet** versions. +## v4.1.2 +- *Enhancement:* The is the first stage of the custom code-gen capability retirement; to be replaced by [`Handlebars.Net`](https://github.com/rexm/Handlebars.Net) as the code-generation engine. All the _entity_ and _reference date_ related code-generation templates (and related) have been ported. The _database_ related code-generation is still using the legacy custom. +- *Enhancement:* The `CodeGen` namespace from `Beef.Core` has been moved relocated. A new `StringConversion` now provides access to the existing string conversion functions (e.g. `ToSentenceCase`). +- *Fixed:* Issue [71](https://github.com/Avanade/Beef/issues/71) has been resolved. A runtime error will now correctly result in a return code of `-1`. + ## v4.1.1 - *Enhancement:* Introduction of Dependency Injection support. diff --git a/src/Beef.Core/CodeGen/CodeGenConfig.cs b/tools/Beef.CodeGen.Core/CodeGenConfig.cs similarity index 98% rename from src/Beef.Core/CodeGen/CodeGenConfig.cs rename to tools/Beef.CodeGen.Core/CodeGenConfig.cs index 310ac9d76..233e62b9b 100644 --- a/src/Beef.Core/CodeGen/CodeGenConfig.cs +++ b/tools/Beef.CodeGen.Core/CodeGenConfig.cs @@ -182,10 +182,10 @@ internal static T GetXmlVal(XElement xml, string name, T defaultVal, bool man Check.NotNull(config, nameof(config)); Check.NotNull(name, nameof(name)); - if (config.Name == name) + if (config?.Name == name) return config; - if (config.Parent == null) + if (config?.Parent == null) return null; else return FindConfig(config.Parent, name); @@ -203,9 +203,9 @@ internal static T GetXmlVal(XElement xml, string name, T defaultVal, bool man Check.NotNull(name, nameof(name)); // Search children for named list. - if (config.Children != null) + if (config?.Children != null) { - foreach (KeyValuePair> kvp in config.Children) + foreach (KeyValuePair> kvp in config!.Children) { if (kvp.Key == name) return kvp.Value; @@ -213,7 +213,7 @@ internal static T GetXmlVal(XElement xml, string name, T defaultVal, bool man } // Search the parent for named list. - if (config.Parent == null) + if (config?.Parent == null) return null; else return FindConfigList(config.Parent, name); @@ -232,7 +232,7 @@ public static List FindConfigAll(CodeGenConfig config, string nam List list = new List(); - if (config.Children != null) + if (config?.Children != null) { foreach (KeyValuePair> kvp in config.Children) { diff --git a/tools/Beef.CodeGen.Core/CodeGenConsole.cs b/tools/Beef.CodeGen.Core/CodeGenConsole.cs index fc79a04e5..0692220c4 100644 --- a/tools/Beef.CodeGen.Core/CodeGenConsole.cs +++ b/tools/Beef.CodeGen.Core/CodeGenConsole.cs @@ -149,11 +149,11 @@ private async Task RunRunAwayAsync() /* Inspired by https://www.youtube.com var cge = new CodeGenExecutor(args); var sw = Stopwatch.StartNew(); - await cge.RunAsync().ConfigureAwait(false); + var result = await cge.RunAsync().ConfigureAwait(false); sw.Stop(); WriteFooter(sw); - return 0; + return result ? 0 : -1; } /// diff --git a/src/Beef.Core/CodeGen/CodeGenException.cs b/tools/Beef.CodeGen.Core/CodeGenException.cs similarity index 100% rename from src/Beef.Core/CodeGen/CodeGenException.cs rename to tools/Beef.CodeGen.Core/CodeGenException.cs diff --git a/tools/Beef.CodeGen.Core/CodeGenExecutor.cs b/tools/Beef.CodeGen.Core/CodeGenExecutor.cs index e75f158b5..0ad490e4b 100644 --- a/tools/Beef.CodeGen.Core/CodeGenExecutor.cs +++ b/tools/Beef.CodeGen.Core/CodeGenExecutor.cs @@ -1,7 +1,10 @@ // Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef -using Beef.Diagnostics; +using Beef.CodeGen.Config; +using Beef.CodeGen.Converters; +using Beef.CodeGen.Generators; using Microsoft.Extensions.Logging; +using Newtonsoft.Json; using System; using System.Collections.Generic; using System.IO; @@ -10,6 +13,7 @@ using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; +using YamlDotNet.Serialization; namespace Beef.CodeGen { @@ -76,6 +80,58 @@ public CodeGenExecutorArgs(ILogger logger, IEnumerable? assemblies = n public bool ExpectNoChange { get; internal set; } } + /// + /// The script arguments. + /// + public class CodeGenScriptArgs + { + /// + /// Gets or sets the code-gen . + /// + public string? GenType { get; set; } + + /// + /// Gets or sets the file name. + /// + public string? FileName { get; set; } + + /// + /// Gets or sets the template. + /// + public string? Template { get; set; } + + /// + /// Gets or sets the output directory. + /// + public string? OutDir { get; set; } + + /// + /// Gets or sets the help text. + /// + public string? HelpText { get; set; } + + /// + /// Gets the other parameters (not specifically defined). + /// + public Dictionary OtherParameters { get; } = new Dictionary(); + } + + /// + /// Provides the configuration type selection. + /// + public enum ConfigType + { + /// + /// Represents the Entity configuration. + /// + Entity, + + /// + /// Represents the Database configuration. + /// + Database, + } + /// /// Represents the code generation executor. /// @@ -98,85 +154,71 @@ public CodeGenExecutor(CodeGenExecutorArgs args) /// /// Executes the selected code generation. /// - public async Task RunAsync() + public async Task RunAsync() { var overallCreatedCount = 0; var overallUpdatedCount = 0; try { - XElement? xmlScript; - if (_args.ScriptFile!.Exists) - { - using var fs = File.OpenText(_args.ScriptFile.FullName); - xmlScript = await XElement.LoadAsync(fs, LoadOptions.None, CancellationToken.None).ConfigureAwait(false); - } - else + // Load the script configuration. + var (scripts, loaders, configType) = await LoadScriptConfigAsync().ConfigureAwait(false); + + ConfigBase? cfg = null; + IRootConfig? rcfg = null; + if (configType == ConfigType.Entity) { - xmlScript = await ResourceManager.GetScriptContentXmlAsync(_args.ScriptFile.Name, _args.Assemblies.ToArray()).ConfigureAwait(false); + cfg = await LoadConfigFileAsync(configType).ConfigureAwait(false); + rcfg = (IRootConfig)cfg; + rcfg.ReplaceRuntimeParameters(_args.Parameters); + cfg.Prepare(cfg, cfg); } - if (xmlScript?.Name != "Script") - throw new CodeGenException("The Script XML file must have a root element named 'Script'."); - - if (!xmlScript.Elements("Generate").Any()) - throw new CodeGenException("The Script XML file must have at least a single 'Generate' element."); - - // Create the code generator instance. + // Create the "legacy" code generator instance. string? outputDir = null; using var cfs = File.OpenText(_args.ConfigFile!.FullName); - var gen = CodeGenerator.Create(await XElement.LoadAsync(cfs, LoadOptions.None, CancellationToken.None).ConfigureAwait(false), GetLoaders(xmlScript)); + var gen = CodeGenerator.Create(await XElement.LoadAsync(cfs, LoadOptions.None, CancellationToken.None).ConfigureAwait(false), loaders); gen.CodeGenerated += (sender, e) => { CodeGenerated(outputDir!, e); }; // Execute each of the script instructions. - foreach (var scriptEle in xmlScript.Elements("Generate")) + foreach (var script in scripts) { - string? template = null; - string? outDir = null; - string? helpText = null; - var otherParameters = new Dictionary(); - foreach (var att in scriptEle.Attributes()) - { - switch (att.Name.LocalName) - { - case "Template": template = att.Value; break; - case "OutDir": outDir = att.Value; break; - case "HelpText": helpText = att.Value; break; - default: otherParameters.Add(att.Name.LocalName, att.Value); break; - } - } - - // Manage the parameters. - gen.ClearParameters(); - gen.CopyParameters(_args.Parameters); - gen.CopyParameters(otherParameters); - - // Log progress. - CreatedCount = 0; - UpdatedCount = 0; NotChangedCount = 0; - _args.Logger.LogInformation(" Template: {0} {1}", template!, helpText == null ? string.Empty : $"({helpText})"); + UpdatedCount = 0; + CreatedCount = 0; + + // Log progress. + _args.Logger.LogInformation(" Template: {0} {1}", script.Template!, script.HelpText == null ? string.Empty : $"({script.HelpText})"); - XElement xmlTemplate; - if (_args.TemplatePath != null) - { - var fi = new FileInfo(Path.Combine(_args.TemplatePath.FullName, template!)); - if (!fi.Exists) - throw new CodeGenException($"The Template XML file '{fi.FullName}' does not exist."); + // Get the template contents. + var template = await GetTemplateContentsAsync(script).ConfigureAwait(false); - using var fs = File.OpenText(fi.FullName); - xmlTemplate = await XElement.LoadAsync(fs, LoadOptions.None, CancellationToken.None).ConfigureAwait(false); + if (script.GenType != null) + { + // Execute the new+improved handlebars-based code-gen. + rcfg!.ResetRuntimeParameters(); + rcfg!.ReplaceRuntimeParameters(_args.Parameters); + rcfg!.ReplaceRuntimeParameters(script.OtherParameters); + + var gt = Type.GetType(script.GenType) ?? throw new CodeGenException($"GenType '{script.GenType}' was unable to be loaded."); + var cg = (CodeGeneratorBase)(Activator.CreateInstance(gt) ?? throw new CodeGenException($"GenType '{script.GenType}' was unable to be instantiated.")); + cg.OutputFileName = script.FileName; + cg.OutputDirName = script.OutDir; + cg.Generate(template, cfg!, e => CodeGenerated(_args.OutputPath!.FullName, e)); } else { - xmlTemplate = await ResourceManager.GetTemplateContentXmlAsync(template!, _args.Assemblies.ToArray()).ConfigureAwait(false) ?? - throw new CodeGenException($"The Template XML resource '{template}' does not exist."); - } + // Execute the legacy custom code-gen (being slowly deprecated). + gen.ClearParameters(); + gen.CopyParameters(_args.Parameters); + gen.CopyParameters(script.OtherParameters); - // Execute the code generation itself. - outputDir = Path.Combine(_args.OutputPath!.FullName, SubstituteOutputDir(outDir!)); - await gen.GenerateAsync(xmlTemplate).ConfigureAwait(false); + outputDir = Path.Combine(_args.OutputPath!.FullName, SubstituteOutputDir(script.OutDir!)); + + using var sr = new StringReader(template); + await gen.GenerateAsync(XElement.Load(sr, LoadOptions.None)).ConfigureAwait(false); + } // Provide statistics. _args.Logger.LogInformation(" [Files: Unchanged = {0}, Updated = {1}, Created = {2}]", NotChangedCount, UpdatedCount, CreatedCount); @@ -190,30 +232,71 @@ public async Task RunAsync() { _args.Logger.LogError(gcex.Message); _args.Logger.LogInformation(string.Empty); + return false; } if (_args.ExpectNoChange && (overallCreatedCount != 0 || overallUpdatedCount != 0)) { _args.Logger.LogError("Unexpected changes detected; one or more files were created and/or updated."); - throw new CodeGenException("Unexpected changes detected; one or more files were created and/or updated."); + return false; } + + return true; } /// - /// Handle the output directory substitution. + /// Load the code-gen scripts configuration. /// - private string SubstituteOutputDir(string outputDir) + private async Task<(List, List, ConfigType)> LoadScriptConfigAsync() { - var dir = outputDir; - foreach (var p in _args.Parameters) + var list = new List(); + + // Load the script XML content. + XElement xmlScript; + if (_args.ScriptFile!.Exists) { - dir = dir.Replace("{{" + p.Key + "}}", p.Value, StringComparison.InvariantCultureIgnoreCase); + using var fs = File.OpenText(_args.ScriptFile.FullName); + xmlScript = await XElement.LoadAsync(fs, LoadOptions.None, CancellationToken.None).ConfigureAwait(false); } + else + { + var c = await ResourceManager.GetScriptContentAsync(_args.ScriptFile.Name, _args.Assemblies.ToArray()).ConfigureAwait(false); + if (c == null) + throw new CodeGenException("The Script XML does not exist."); - if (dir.Contains("{{", StringComparison.InvariantCultureIgnoreCase) || dir.Contains("}}", StringComparison.InvariantCultureIgnoreCase)) - throw new CodeGenException($"Unhandled substitution characters have been found in the Script OutDir {dir}."); + xmlScript = XElement.Parse(c); + } - return dir; + if (xmlScript?.Name != "Script") + throw new CodeGenException("The Script XML file must have a root element named 'Script'."); + + if (!xmlScript.Elements("Generate").Any()) + throw new CodeGenException("The Script XML file must have at least a single 'Generate' element."); + + foreach (var scriptEle in xmlScript.Elements("Generate")) + { + var args = new CodeGenScriptArgs(); + foreach (var att in scriptEle.Attributes()) + { + switch (att.Name.LocalName) + { + case "GenType": args.GenType = att.Value; break; + case "FileName": args.FileName = att.Value; break; + case "Template": args.Template = att.Value; break; + case "OutDir": args.OutDir = att.Value; break; + case "HelpText": args.HelpText = att.Value; break; + default: args.OtherParameters.Add(att.Name.LocalName, att.Value); break; + } + } + + list.Add(args); + } + + var ct = xmlScript.Attribute("ConfigType")?.Value ?? throw new CodeGenException("The Script XML file must have an attribute named 'ConfigType' within the root 'Script' element."); + if (Enum.TryParse(ct, true, out var configType)) + return (list, GetLoaders(xmlScript), configType); + + throw new CodeGenException($"The Script XML file attribute named 'ConfigType' has an invalid value '{ct}'."); } /// @@ -240,6 +323,78 @@ private static List GetLoaders(XElement xmlScript) throw new CodeGenException($"The Script XML 'LoaderType' does not implement 'ICodeGenConfigGetLoaders': {lt}."); } + /// + /// Loads the configuration file. + /// + private async Task LoadConfigFileAsync(ConfigType configType) + { + try + { + switch (_args.ConfigFile!.Extension.ToUpperInvariant()) + { + case ".XML": + using (var xfs = _args.ConfigFile!.OpenRead()) + { + var xml = await XDocument.LoadAsync(xfs, LoadOptions.None, CancellationToken.None).ConfigureAwait(false); + var yaml = configType == ConfigType.Entity ? XmlToYamlConverter.ConvertEntityXmlToYaml(xml) : throw new NotImplementedException(); + + using var sr = new StringReader(yaml); + var yml = new DeserializerBuilder().Build().Deserialize(sr); + var json = new SerializerBuilder().JsonCompatible().Build().Serialize(yml!); + + using var jsr = new StringReader(json); + using var jr = new JsonTextReader(jsr); + var js = JsonSerializer.Create(); + return CheckNotNull(js.Deserialize(jr, configType == ConfigType.Entity ? typeof(Config.Entity.CodeGenConfig) : throw new NotImplementedException())); + } + + case ".YAML": throw new NotImplementedException(); + case ".JSON": throw new NotImplementedException(); + default: throw new CodeGenException($"The Config file '{_args.ConfigFile!.FullName}' is not a supported file type."); + } + } + catch (CodeGenException) { throw; } + catch (Exception ex) { throw new CodeGenException($"The contents of the Config File {_args.ConfigFile!.Name} are not valid.", ex); } + } + + /// + /// Checks not null and converts type. + /// + private ConfigBase CheckNotNull(object? val) => (ConfigBase)(val ?? throw new CodeGenException($"The contents of the Config File {_args.ConfigFile!.Name} are not valid.")); + + /// + /// Gets the template contents. + /// + private async Task GetTemplateContentsAsync(CodeGenScriptArgs script) + { + if (_args.TemplatePath == null) + return await ResourceManager.GetTemplateContentAsync(script.Template!, _args.Assemblies.ToArray()).ConfigureAwait(false) ?? + throw new CodeGenException($"The Template embedded resource '{script.Template}' does not exist."); + + var fi = new FileInfo(Path.Combine(_args.TemplatePath.FullName, script.Template!)); + if (!fi.Exists) + throw new CodeGenException($"The Template file '{fi.FullName}' does not exist."); + + return await File.ReadAllTextAsync(fi.FullName, CancellationToken.None).ConfigureAwait(false); + } + + /// + /// Handle the output directory substitution. + /// + private string SubstituteOutputDir(string outputDir) + { + var dir = outputDir; + foreach (var p in _args.Parameters) + { + dir = dir.Replace("{{" + p.Key + "}}", p.Value, StringComparison.InvariantCultureIgnoreCase); + } + + if (dir.Contains("{{", StringComparison.InvariantCultureIgnoreCase) || dir.Contains("}}", StringComparison.InvariantCultureIgnoreCase)) + throw new CodeGenException($"Unhandled substitution characters have been found in the Script OutDir {dir}."); + + return dir; + } + /// /// Handle the generation of an output file. /// diff --git a/src/Beef.Core/CodeGen/CodeGenTemplate.cs b/tools/Beef.CodeGen.Core/CodeGenTemplate.cs similarity index 94% rename from src/Beef.Core/CodeGen/CodeGenTemplate.cs rename to tools/Beef.CodeGen.Core/CodeGenTemplate.cs index fa9105ca5..ce73bf77e 100644 --- a/src/Beef.Core/CodeGen/CodeGenTemplate.cs +++ b/tools/Beef.CodeGen.Core/CodeGenTemplate.cs @@ -141,7 +141,7 @@ private void ExecuteXml(XElement xmlNode, CodeGenConfig config) break; default: - throw new CodeGenException($"Unexpected XML Node Type '{xml.NodeType}' encountered: '{xml.ToString()}' - '{xml.Parent.ToString()}'."); + throw new CodeGenException($"Unexpected XML Node Type '{xml.NodeType}' encountered: '{xml}' - '{xml.Parent}'."); } } @@ -178,7 +178,7 @@ private bool GetOutputFileName(XElement xml, CodeGenConfig config) { var val = GetValue(isOutputNewOnly, config); if (val != null) - _eventArgs.IsOutputNewOnly = val is bool ? (bool)val : throw new CodeGenException($"'IsOutputNewOnly' attribute value '{isOutputNewOnly}' does not result in a boolean value."); + _eventArgs.IsOutputNewOnly = val is bool boolean ? boolean : throw new CodeGenException($"'IsOutputNewOnly' attribute value '{isOutputNewOnly}' does not result in a boolean value."); } _eventArgs.IsOutputNewOnly = false; @@ -257,7 +257,7 @@ private void ExecuteIf(XElement xmlCon, CodeGenConfig config) ExecuteXml(elseXml, config); } catch (CodeGenException) { throw; } - catch (Exception ex) { throw new CodeGenException($"If/Then/Else element is invalid ({ex.Message}): {xmlCon.ToString()}."); } + catch (Exception ex) { throw new CodeGenException($"If/Then/Else element is invalid ({ex.Message}): {xmlCon}."); } } /// @@ -342,8 +342,8 @@ private bool ExecuteIfConditionStmt(List stmt, CodeGenConfig config, str object? lVal = GetValue(stmt[0], config); if (stmt.Count == 1) { - if (lVal is bool) - return (bool)lVal; + if (lVal is bool blval) + return blval; if (lVal is string slVal) { @@ -376,13 +376,13 @@ private bool ExecuteIfConditionStmt(List stmt, CodeGenConfig config, str if (lVal != null || rVal != null) { if (lVal is bool || rVal is bool) - res = Comparer.Default.Compare((bool)Convert.ChangeType(lVal, typeof(bool), CultureInfo.InvariantCulture), (bool)Convert.ChangeType(rVal, typeof(bool), CultureInfo.InvariantCulture)); + res = Comparer.Default.Compare((bool)Convert.ChangeType(lVal, typeof(bool), CultureInfo.InvariantCulture)!, (bool)Convert.ChangeType(rVal, typeof(bool), CultureInfo.InvariantCulture)!); else if (lVal is decimal || rVal is decimal) - res = Comparer.Default.Compare((decimal)Convert.ChangeType(lVal, typeof(decimal), CultureInfo.InvariantCulture), (decimal)Convert.ChangeType(rVal, typeof(decimal), CultureInfo.InvariantCulture)); + res = Comparer.Default.Compare((decimal)Convert.ChangeType(lVal, typeof(decimal), CultureInfo.InvariantCulture)!, (decimal)Convert.ChangeType(rVal, typeof(decimal), CultureInfo.InvariantCulture)!); else if (lVal is int || rVal is int) - res = Comparer.Default.Compare((int)Convert.ChangeType(lVal, typeof(int), CultureInfo.InvariantCulture), (int)Convert.ChangeType(rVal, typeof(int), CultureInfo.InvariantCulture)); + res = Comparer.Default.Compare((int)Convert.ChangeType(lVal, typeof(int), CultureInfo.InvariantCulture)!, (int)Convert.ChangeType(rVal, typeof(int), CultureInfo.InvariantCulture)!); else - res = Comparer.Default.Compare((string)Convert.ChangeType(lVal, typeof(string), CultureInfo.InvariantCulture), (string)Convert.ChangeType(rVal, typeof(string), CultureInfo.InvariantCulture)); + res = Comparer.Default.Compare((string)Convert.ChangeType(lVal, typeof(string), CultureInfo.InvariantCulture)!, (string)Convert.ChangeType(rVal, typeof(string), CultureInfo.InvariantCulture)!); } switch (stmt[1].ToUpperInvariant()) @@ -522,15 +522,15 @@ private void ExecuteSet(XElement xml, CodeGenConfig config) "TOLOWERCASE" => value.ToLowerInvariant(), #pragma warning restore CA1308 "TOUPPERCASE" => value.ToUpperInvariant(), - "TOPRIVATECASE" => CodeGenerator.ToPrivateCase(value), - "TOARGUMENTCASE" => CodeGenerator.ToCamelCase(value), - "TOSENTENCECASE" => CodeGenerator.ToSentenceCase(value), - "TOPASCALCASE" => CodeGenerator.ToPascalCase(value), - "TOCAMELCASE" => CodeGenerator.ToCamelCase(value), - "TOSNAKECASE" => CodeGenerator.ToSnakeCase(value), - "TOKEBABCASE" => CodeGenerator.ToKebabCase(value), - "TOPASTTENSE" => CodeGenerator.ToPastTense(value), - "TOPLURAL" => CodeGenerator.ToPlural(value), + "TOPRIVATECASE" => StringConversion.ToPrivateCase(value), + "TOARGUMENTCASE" => StringConversion.ToCamelCase(value), + "TOSENTENCECASE" => StringConversion.ToSentenceCase(value), + "TOPASCALCASE" => StringConversion.ToPascalCase(value), + "TOCAMELCASE" => StringConversion.ToCamelCase(value), + "TOSNAKECASE" => StringConversion.ToSnakeCase(value), + "TOKEBABCASE" => StringConversion.ToKebabCase(value), + "TOPASTTENSE" => StringConversion.ToPastTense(value), + "TOPLURAL" => StringConversion.ToPlural(value), "TOCOMMENTS" => CodeGenerator.ToComments(value), "TOSEECOMMENTS" => CodeGenerator.ToSeeComments(value), _ => TransformCommand(transform!, value!, config) @@ -573,9 +573,9 @@ private void ExecuteIncrement(XElement xml, CodeGenConfig config) decimal dlval = 0m; if (lval != null) { - if (lval is decimal) - dlval = (decimal)lval; - else if (!(lval is string) || !decimal.TryParse((string)lval, out dlval)) + if (lval is decimal dlvalx) + dlval = dlvalx; + else if (!(lval is string slval) || !decimal.TryParse(slval, out dlval)) throw new CodeGenException($"Increment 'Name' attribute value '{lval}' is not a valid decimal", xml.ToString()); } @@ -586,9 +586,9 @@ private void ExecuteIncrement(XElement xml, CodeGenConfig config) object? rval = GetValue(value, config); if (rval != null) { - if (rval is decimal) - drval = (decimal)rval; - else if (!(rval is string) || !decimal.TryParse((string)rval, out drval)) + if (rval is decimal drvalx) + drval = drvalx; + else if (!(rval is string srval) || !decimal.TryParse(srval, out drval)) throw new CodeGenException($"Increment 'Value' attribute value '{rval}' is not a valid decimal", xml.ToString()); } } @@ -713,7 +713,7 @@ private void SetConfigValue(string name, CodeGenConfig config, string? value) { CodeGenConfig val = GetConfig(name, config, out string propertyName); var oval = GetValue(value, config); - string? sval = (oval == null) ? null : ((oval is bool) ? ((bool)oval ? "true" : "false") : oval.ToString()); + string? sval = (oval == null) ? null : ((oval is bool boval) ? (boval ? "true" : "false") : oval.ToString()); val.AttributeUpdate(propertyName, TemplateReplace(sval, config)); } diff --git a/tools/Beef.CodeGen.Core/CodeGenerator.cs b/tools/Beef.CodeGen.Core/CodeGenerator.cs new file mode 100644 index 000000000..60b2de989 --- /dev/null +++ b/tools/Beef.CodeGen.Core/CodeGenerator.cs @@ -0,0 +1,230 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Beef.CodeGen +{ + /// + /// The code generator. + /// + public class CodeGenerator + { + /// + /// The reserved system name. + /// + public const string SystemConfigName = "System"; + + /// + /// Converts to c# 'see cref=' Comments ('List<int>' would become 'List{int}' respectively). + /// + /// The text. + /// The converted text. + private static string? ReplaceGenericsBracketWithCommentsBracket(string? text) + { + if (string.IsNullOrEmpty(text)) + return text; + + var s = text.Replace("<", "{", StringComparison.InvariantCulture); + s = s.Replace(">", "}", StringComparison.InvariantCulture); + return s; + } + + /// + /// Converts to c# Comments ('{{xyx}}' would become 'see cref=' XML, and any <> within the xyz would become {} respectively). + /// + /// The text. + /// The converted text. + public static string? ToComments(string? text) + { + if (string.IsNullOrEmpty(text)) + return text; + + var s = text; + while (true) + { + var start = s.IndexOf("{{", StringComparison.InvariantCultureIgnoreCase); + var end = s.IndexOf("}}", StringComparison.InvariantCultureIgnoreCase); + + if (start < 0 && end < 0) + break; + + if (start < 0 || end < 0 || end < start) + throw new CodeGenException("Start and End {{ }} parameter mismatch.", text); + + string sub = s.Substring(start, end - start + 2); + string? mid = ReplaceGenericsBracketWithCommentsBracket(sub[2..^2]); + + s = s.Replace(sub, string.Format(CultureInfo.InvariantCulture, "", mid), StringComparison.InvariantCulture); + } + + return s; + } + + /// + /// Converts to c# 'see cref=' comments ('List<int>' would become '<see cref="List{int}/>' respectively). + /// + /// The text. + /// The converted text. + public static string? ToSeeComments(string? text) + { + if (string.IsNullOrEmpty(text)) + return text; + + return $""; + } + + /// + /// Creates a new . + /// + /// The configuration . + /// The dictionary. + /// A . + public static CodeGenerator Create(XElement configXml, IEnumerable? loaders = null) + { + if (configXml == null) + throw new ArgumentNullException(nameof(configXml)); + + // Create the code generator. + var cg = new CodeGenerator(configXml); + + // Load the Loaders making sure it does not contain 'System'. + if (loaders != null) + { + foreach (var l in loaders) + { + if (l.Name == SystemConfigName) + throw new ArgumentException($"A ConfigLoader with the Name of '{SystemConfigName}' is reserved for internal use only.", nameof(loaders)); + + if (cg.Loaders.ContainsKey(l.Name)) + throw new ArgumentException($"A ConfigLoader with the Name of '{l.Name}' has already been defined (must be unique).", nameof(loaders)); + + cg.Loaders.Add(l.Name, l); + } + } + + return cg; + } + + /// + /// Private constructor. + /// + private CodeGenerator(XElement configXml) => ConfigXml = configXml; + + /// + /// Gets or sets the root (top-most) . + /// + internal CodeGenConfig? Root { get; set; } + + /// + /// Gets the System . + /// + internal CodeGenConfig System { get; private set; } = new CodeGenConfig(SystemConfigName, null); + + /// + /// Gets the configuration . + /// + public XElement ConfigXml { get; private set; } + + /// + /// Gets the loaders. + /// + internal Dictionary Loaders { get; } = new Dictionary(); + + /// + /// Gets the parameter overrides. + /// + public Dictionary Parameters { get; internal set; } = new Dictionary(); + + /// + /// Copies the into . + /// + /// The parameters to copy. + public void CopyParameters(Dictionary parameters) + { + if (parameters == null) + return; + + foreach (var p in parameters) + { + Parameters.Add(p.Key, p.Value); + } + } + + /// + /// Clears the current . + /// + public void ClearParameters() + { + Parameters.Clear(); + } + + /// + /// Generates the output. + /// + /// The template . + public async Task GenerateAsync(XElement xmlTemplate) + { + if (xmlTemplate == null) + throw new ArgumentNullException(nameof(xmlTemplate)); + + // Ready the 'System' configuration. + System = new CodeGenConfig(SystemConfigName, null); + System.AttributeAdd("Index", "0"); + + // Creates the root configuration. + await CodeGenConfig.CreateAsync(this).ConfigureAwait(false); + + using var t = new CodeGenTemplate(this, xmlTemplate); + t.Execute(); + } + + /// + /// Raises the event. + /// + internal void RaiseCodeGenerated(CodeGeneratorEventArgs e) + { + CodeGenerated?.Invoke(this, e); + } + + /// + /// Occurs when a output has been successfully generated. + /// + public event EventHandler? CodeGenerated; + } + + /// + /// The event arguments. + /// + public class CodeGeneratorEventArgs : EventArgs + { + /// + /// Gets or sets the generated directory name. + /// + public string? OutputGenDirName { get; set; } + + /// + /// Gets or sets the optional directory name. + /// + public string? OutputDirName { get; set; } + + /// + /// Gets or sets the generated file name. + /// + public string? OutputFileName { get; set; } + + /// + /// Indicates whether the file is only output when new; i.e. does not already exist. + /// + public bool IsOutputNewOnly { get; set; } + + /// + /// Gets or sets the generated output content. + /// + public string? Content { get; set; } + } +} diff --git a/tools/Beef.CodeGen.Core/Config/ConfigBase.cs b/tools/Beef.CodeGen.Core/Config/ConfigBase.cs index 04a2e8ba4..fb121231b 100644 --- a/tools/Beef.CodeGen.Core/Config/ConfigBase.cs +++ b/tools/Beef.CodeGen.Core/Config/ConfigBase.cs @@ -1,9 +1,32 @@ // Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef using System; +using System.Collections.Generic; namespace Beef.CodeGen.Config { + /// + /// Enables the root configuration capabilities. + /// + public interface IRootConfig + { + /// + /// Gets the parameter overrides. + /// + Dictionary RuntimeParameters { get; } + + /// + /// Replaces the with the specified (copies values). + /// + /// The parameters to copy. + void ReplaceRuntimeParameters(Dictionary parameters); + + /// + /// Resets the runtime parameters. + /// + void ResetRuntimeParameters(); + } + /// /// Provides base configuration capabilities. /// @@ -54,25 +77,47 @@ public abstract class ConfigBase /// The value to compare to. /// true where equal; otherwise, false. public static bool CompareValue(bool? propertyValue, bool compareTo) => propertyValue != null && propertyValue == compareTo; + + /// + /// Prepares the configuration properties in advance of the code-generation execution (Internal use!). + /// + /// The root . + /// The parent . + protected internal abstract void Prepare(object root, object parent); } /// - /// Provides the base configuration capabilities. + /// Provides the base configuration capabilities. /// + /// The root . /// The parent . - public abstract class ConfigBase : ConfigBase where TParent : class + public abstract class ConfigBase : ConfigBase where TRoot : ConfigBase where TParent : ConfigBase { /// - /// Gets the Parent (set via execution). + /// Gets the Root (set via execution). + /// + public TRoot? Root { get; private set; } + + /// + /// Gets the Parent (set via execution). /// public TParent? Parent { get; private set; } + /// + /// Prepares the configuration properties in advance of the code-generation execution (Internal use!). + /// + /// The root . + /// The parent . + protected internal override void Prepare(object root, object parent) => Prepare((TRoot)root, (TParent)parent); + /// /// Prepares the configuration properties in advance of the code-generation execution. /// - /// The parent obkect. - public void Prepare(TParent parent) + /// The root . + /// The parent . + public void Prepare(TRoot root, TParent parent) { + Root = root; Parent = parent; Prepare(); } diff --git a/tools/Beef.CodeGen.Core/Config/Database/CodeGenConfig.cs b/tools/Beef.CodeGen.Core/Config/Database/CodeGenConfig.cs new file mode 100644 index 000000000..387771175 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Config/Database/CodeGenConfig.cs @@ -0,0 +1,188 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Entities; +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Beef.CodeGen.Config.Database +{ + /// + /// Represents the global database code-generation configuration. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + [ClassSchema("CodeGeneration", Title = "The **CodeGeneration** is used as the global configuration for driving the underlying code-generation.", Description = "", Markdown = "")] + [CategorySchema("RefData", Title = "Provides the **Reference Data** configuration.")] + [CategorySchema("Infer", Title = "Provides the **Column Name inference** configuration.")] + public class CodeGenConfig : ConfigBase, IRootConfig + { + #region RefData + + /// + /// Gets or sets the schema name to identify the Reference Data related tables. + /// + [JsonProperty("refDatabaseSchema", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("RefData", Title = "The schema name to identify the Reference Data related tables.", IsImportant = true, + Description = "Defaults to `Ref`.")] + public string? RefDatabaseSchema { get; set; } + + #endregion + + #region Infer + + /// + /// Gets or sets the column name for the `IsDeleted` capability. + /// + [JsonProperty("columnNameIsDeleted", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Infer", Title = "The column name for the `IsDeleted` capability.", + Description = "Defaults to `IsDeleted`. To remove capability set to `None`.")] + public string? ColumnNameIsDeleted { get; set; } + + /// + /// Gets or sets the column name for the `TenantId` capability. + /// + [JsonProperty("columnNameTenantId", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Infer", Title = "The column name for the `TenantId` capability.", + Description = "Defaults to `TenantId`. To remove capability set to `None`.")] + public string? ColumnNameTenantId { get; set; } + + /// + /// Gets or sets the column name for the `OrgUnitId` capability. + /// + [JsonProperty("columnNameOrgUnitId", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Infer", Title = "The column name for the `OrgUnitId` capability.", + Description = "Defaults to `OrgUnitId`. To remove capability set to `None`.")] + public string? ColumnNameOrgUnitId { get; set; } + + /// + /// Gets or sets the column name for the `RowVersion` capability. + /// + [JsonProperty("columnNameRowVersion", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Infer", Title = "The column name for the `RowVersion` capability.", + Description = "Defaults to `RowVersion`. To remove capability set to `None`.")] + public string? ColumnNameRowVersion { get; set; } + + /// + /// Gets or sets the column name for the `CreatedBy` capability. + /// + [JsonProperty("columnNameCreatedBy", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Infer", Title = "The column name for the `CreatedBy` capability.", + Description = "Defaults to `CreatedBy`. To remove capability set to `None`.")] + public string? ColumnNameCreatedBy { get; set; } + + /// + /// Gets or sets the column name for the `CreatedDate` capability. + /// + [JsonProperty("columnNameCreatedDate", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Infer", Title = "The column name for the `CreatedDate` capability.", + Description = "Defaults to `CreatedDate`. To remove capability set to `None`.")] + public string? ColumnNameCreatedDate { get; set; } + + /// + /// Gets or sets the column name for the `UpdatedBy` capability. + /// + [JsonProperty("columnNameUpdatedBy", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Infer", Title = "The column name for the `UpdatedBy` capability.", + Description = "Defaults to `UpdatedBy`. To remove capability set to `None`.")] + public string? ColumnNameUpdatedBy { get; set; } + + /// + /// Gets or sets the column name for the `UpdatedDate` capability. + /// + [JsonProperty("columnNameUpdatedDate", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Infer", Title = "The column name for the `UpdatedDate` capability.", + Description = "Defaults to `UpdatedDate`. To remove capability set to `None`.")] + public string? ColumnNameUpdatedDate { get; set; } + + #endregion + + #region RuntimeParameters + + /// + /// Gets the parameter overrides. + /// + public Dictionary RuntimeParameters { get; internal set; } = new Dictionary(); + + /// + /// Replaces the with the specified (copies values). + /// + /// The parameters to copy. + public void ReplaceRuntimeParameters(Dictionary parameters) + { + if (parameters == null) + return; + + foreach (var p in parameters) + { + if (RuntimeParameters.ContainsKey(p.Key)) + RuntimeParameters[p.Key] = p.Value; + else + RuntimeParameters.Add(p.Key, p.Value); + } + } + + /// + /// Resets the runtime parameters. + /// + public void ResetRuntimeParameters() => RuntimeParameters.Clear(); + + /// + /// Gets the specified runtime parameter value. + /// + /// The parameter name. + /// Indicates whether the parameter is mandatory and therefore must exist and have non-null value. + /// The runtime parameter value. + internal string? GetRuntimeParameter(string name, bool isRequired = false) + { + if ((!RuntimeParameters.TryGetValue(name, out var value) && isRequired) || (isRequired && string.IsNullOrEmpty(value))) + throw new CodeGenException($"Runtime parameter '{name}' was not found or had no value; this is required to function."); + else + return value; + } + + /// + /// Gets the specified runtime parameter value as a . + /// + /// The parameter name. + /// The runtime parameter value. + internal bool GetRuntimeBoolParameter(string name) + { + var val = GetRuntimeParameter(name); + if (string.IsNullOrEmpty(val)) + return false; + + if (bool.TryParse(val, out var value)) + return value; + + throw new CodeGenException($"Runtime parameter '{name}' must be a boolean; value '{val}' is invalid."); + } + + #endregion + + /// + /// Sets the list of tables within the parent database. + /// + /// The list of tables. + public void SetDbTable(List
dbTables) => DbTables = dbTables; + + /// + /// Gets or sets the list of tables that exist within the database. + /// + public List
? DbTables { get; private set; } + + /// + /// + /// + protected override void Prepare() + { + RefDatabaseSchema = DefaultWhereNull(RefDatabaseSchema, () => "Ref"); + ColumnNameIsDeleted = DefaultWhereNull(ColumnNameIsDeleted, () => "IsDeleted"); + ColumnNameTenantId = DefaultWhereNull(ColumnNameTenantId, () => "TenantId"); + ColumnNameOrgUnitId = DefaultWhereNull(ColumnNameOrgUnitId, () => "OrgUnitId"); + ColumnNameRowVersion = DefaultWhereNull(ColumnNameRowVersion, () => "RowVersion"); + ColumnNameCreatedBy = DefaultWhereNull(ColumnNameCreatedBy, () => "CreatedBy"); + ColumnNameCreatedDate = DefaultWhereNull(ColumnNameCreatedDate, () => "CreatedDate"); + ColumnNameUpdatedBy = DefaultWhereNull(ColumnNameUpdatedBy, () => "UpdatedBy"); + ColumnNameUpdatedDate = DefaultWhereNull(ColumnNameUpdatedDate, () => "UpdatedDate"); + } + } +} diff --git a/tools/Beef.CodeGen.Core/Config/Database/ParameterConfig.cs b/tools/Beef.CodeGen.Core/Config/Database/ParameterConfig.cs new file mode 100644 index 000000000..be8fad011 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Config/Database/ParameterConfig.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json; +using System; +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using System.Collections.Generic; +using System.Text; + +namespace Beef.CodeGen.Config.Database +{ + /// + /// Represents the stored procedure parameter configuration. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + [ClassSchema("Parameter", Title = "The **Parameter** is used to define a Stored Procedure's Parameter and its charateristics.", Description = "", Markdown = "")] + [CategorySchema("Key", Title = "Provides the **key** configuration.")] + public class ParameterConfig : ConfigBase + { + + + /// + /// + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "Requirement is for lowercase.")] + protected override void Prepare() + { + + } + } +} diff --git a/tools/Beef.CodeGen.Core/Config/Database/StoredProcedureConfig.cs b/tools/Beef.CodeGen.Core/Config/Database/StoredProcedureConfig.cs new file mode 100644 index 000000000..71182ce75 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Config/Database/StoredProcedureConfig.cs @@ -0,0 +1,90 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace Beef.CodeGen.Config.Database +{ + /// + /// Represents the stored procedure configuration. + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + [ClassSchema("StoredProcedure", Title = "The **StoredProcedure** is used to identify a database `Stored Procedure` and define its code-generation characteristics.", Description = "", Markdown = "")] + [CategorySchema("Key", Title = "Provides the **key** configuration.")] + [CategorySchema("Auth", Title = "Provides the **Authorization** configuration.")] + public class StoredProcedureConfig : ConfigBase + { + /// + /// Gets or sets the name of the `StoredProcedure` in the database. + /// + [JsonProperty("name", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Key", Title = "The name of the `StoredProcedure` in the database.", IsMandatory = true, IsImportant = true)] + public string? Name { get; set; } + + /// + /// Gets or sets the stored procedure operation type. + /// + [JsonProperty("type", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Key", Title = "The stored procedure operation type.", IsImportant = true, + Options = new string[] { "Get", "GetColl", "Create", "Update", "Upsert", "Delete", "Merge" }, + Description = "Defaults to `GetColl`.")] + public string? Type { get; set; } + + /// + /// Indicates whether standardized paging support should be added. + /// + [JsonProperty("paging", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Key", Title = "Indicates whether standardized paging support should be added.", IsImportant = true, + Description = "This only applies where the stored procedure operation `Type` is `GetColl`.")] + public bool? Paging { get; set; } + + /// + /// Gets or sets the SQL statement to perform the reselect after a `Create`, `Update` or `Upsert` stored procedure operation `Type`. + /// + [JsonProperty("reselectStatement", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Key", Title = "The SQL statement to perform the reselect after a `Create`, `Update` or `Upsert` stored procedure operation `Type`.", + Description = "Defaults to `[{{Table.Schema}}].[sp{{Table.Name}}Get]` passing the primary key column(s).")] + public string? ReselectStatement { get; set; } + + /// + /// Indicates whether to select into a `#TempTable` to allow other statements to get access to the selected data. + /// + [JsonProperty("intoTempTable", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Key", Title = "Indicates whether to select into a `#TempTable` to allow other statements to get access to the selected data.", + Description = "A `Select * from #TempTable` is also performed where the stored procedure operation `Type` is `GetColl`.")] + public bool? IntoTempTable { get; set; } + + /// + /// Gets or sets the column names to be used in the `Merge` statement to determine whether to insert, update or delete. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "DTO.")] + [JsonProperty("mergeOverrideIdentityColumns", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Key", Title = "The column names to be used in the `Merge` statement to determine whether to insert, update or delete.", + Description = "This is used to override the default behaviour of using the identity column(s).")] + public List? MergeOverrideIdentityColumns { get; set; } + + /// + /// Gets or sets the table hints using the SQL Server `WITH()` statement; the value specified will be used as-is; e.g. `NOLOCK` will result in `WITH(NOLOCK)`. + /// + [JsonProperty("permission", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Key", Title = "the table hints using the SQL Server `WITH()` statement; the value specified will be used as-is; e.g. `NOLOCK` will result in `WITH(NOLOCK)`.")] + public string? WithHints { get; set; } + + /// + /// Gets or sets the permission (full name being `name.action`) override to be used for security permission checking. + /// + [JsonProperty("permission", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Auth", Title = "The name of the `StoredProcedure` in the database.")] + public string? Permission { get; set; } + + /// + /// + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "Requirement is for lowercase.")] + protected override void Prepare() + { + Type = DefaultWhereNull(Type, () => "GetColl"); + ReselectStatement = DefaultWhereNull(ReselectStatement, () => $"[{Parent!.Schema}].[sp{Parent.Name}Get]"); + } + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Config/Database/TableConfig.cs b/tools/Beef.CodeGen.Core/Config/Database/TableConfig.cs new file mode 100644 index 000000000..731c0e02c --- /dev/null +++ b/tools/Beef.CodeGen.Core/Config/Database/TableConfig.cs @@ -0,0 +1,206 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Entities; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Config.Database +{ + /// + /// + /// + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + [ClassSchema("Table", Title = "The **Table** is used to identify a database `Table` and define its code-generation characteristics.", Description = "", Markdown = "")] + [CategorySchema("Key", Title = "Provides the **key** configuration.")] + [CategorySchema("CodeGen", Title = "Provides the **CodeGen** configuration to select via shorthand the code-gen artefacts.")] + [CategorySchema("UDT", Title = "Provides the **UDT (user defined type)** configuration.")] + [CategorySchema("Auth", Title = "Provides the **Authorization** configuration.")] + public class TableConfig : ConfigBase + { + #region Key + + /// + /// Gets or sets the name of the `Table` in the database. + /// + [JsonProperty("name", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Key", Title = "The name of the `Table` in the database.", IsMandatory = true, IsImportant = true)] + public string? Name { get; set; } + + /// + /// Gets or sets the name of the `Schema` where the `Table` is defined in the database. + /// + [JsonProperty("schema", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Key", Title = "The name of the `Schema` where the `Table` is defined in the database.", IsMandatory = true, IsImportant = true)] + public string? Schema { get; set; } + + /// + /// Gets or sets the name of the `Schema` where the `Table` is defined in the database. + /// + [JsonProperty("alias", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Key", Title = "The name of the `Schema` where the `Table` is defined in the database.", + Description = "Will automatically default where not specified.")] + public string? Alias { get; set; } + + #endregion + + #region Columns + + /// + /// Gets or sets the list of `Column` names to be included in the underlying generated output. + /// + [JsonProperty("includeColumns", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Key", Title = "The list of `Column` names to be included in the underlying generated output.", IsImportant = true, + Description = "Where not specified this indicates that all `Columns` are to be included.")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "Is a DTO; therefore, OK.")] + public string[]? IncludeColumns { get; set; } + + /// + /// Gets or sets the list of `Column` names to be excluded from the underlying generated output. + /// + [JsonProperty("excludeColumns", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Key", Title = "The list of `Column` names to be excluded from the underlying generated output.", IsImportant = true, + Description = "Where not specified this indicates that no `Columns` are to be excluded.")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "Is a DTO; therefore, OK.")] + public string[]? ExcludeColumns { get; set; } + + #endregion + + #region CodeGen + + /// + /// Indicates that a `Get` stored procedure will be automatically generated where not otherwise explicitly specified. + /// + [JsonProperty("get", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("CodeGen", Title = "Indicates that a `Get` stored procedure will be automatically generated where not otherwise explicitly specified.")] + public bool? Get { get; set; } + + /// + /// Indicates that a `GetAll` stored procedure will be automatically generated where not otherwise explicitly specified. + /// + [JsonProperty("getAll", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("CodeGen", Title = "Indicates that a `GetAll` stored procedure will be automatically generated where not otherwise explicitly specified.")] + public bool? GetAll { get; set; } + + /// + /// Gets or sets the list of columns names (including sort order ASC/DESC) to be used as the GetAll query sort order (will automatically add `alias` where not specified for column). + /// + [JsonProperty("getAll", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("CodeGen", Title = "The list of columns names (including sort order ASC/DESC) to be used as the GetAll query sort order (will automatically add `alias` where not specified for column).")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "Is a DTO; therefore, OK.")] + public string[]? GetAllOrderBy { get; set; } + + /// + /// Indicates that a `Create` stored procedure will be automatically generated where not otherwise explicitly specified. + /// + [JsonProperty("create", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("CodeGen", Title = "Indicates that a `Create` stored procedure will be automatically generated where not otherwise explicitly specified.")] + public bool? Create { get; set; } + + /// + /// Indicates that a `Update` stored procedure will be automatically generated where not otherwise explicitly specified. + /// + [JsonProperty("update", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("CodeGen", Title = "Indicates that a `Update` stored procedure will be automatically generated where not otherwise explicitly specified.")] + public bool? Update { get; set; } + + /// + /// Indicates that a `Upsert` stored procedure will be automatically generated where not otherwise explicitly specified. + /// + [JsonProperty("upsert", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("CodeGen", Title = "Indicates that a `Upsert` stored procedure will be automatically generated where not otherwise explicitly specified.")] + public bool? Upsert { get; set; } + + /// + /// Indicates that a `Delete` stored procedure will be automatically generated where not otherwise explicitly specified. + /// + [JsonProperty("delete", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("CodeGen", Title = "Indicates that a `Delete` stored procedure will be automatically generated where not otherwise explicitly specified.")] + public bool? Delete { get; set; } + + /// + /// Indicates that a `View` will be automatically generated where not otherwise explicitly specified. + /// + [JsonProperty("view", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("CodeGen", Title = "Indicates that a `View` will be automatically generated where not otherwise explicitly specified (only applies for a `Table`).")] + public bool? View { get; set; } + + #endregion + + #region Udt + + /// + /// Indicates that a `User Defined Table (UDT)` type should be created. + /// + [JsonProperty("view", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Udt", Title = "Indicates that a `User Defined Table (UDT)` type should be created.", IsImportant = true)] + public bool? Udt { get; set; } + + /// + /// Gets or sets the list of `Column` names to be excluded from the `User Defined Table (UDT)`. + /// + [JsonProperty("excludeColumns", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Udt", Title = "The list of `Column` names to be excluded from the `User Defined Table (UDT)`.", + Description = "Where not specified this indicates that no `Columns` are to be excluded.")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "Is a DTO; therefore, OK.")] + public string[]? UdtExcludeColumns { get; set; } + + /// + /// Gets or sets the name of the .NET entity associated with the `Udt` so that it can be expressed (created) as a Table-Valued Parameter for usage within the corresponding `DbMapper`. + /// + [JsonProperty("excludeColumns", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Udt", Title = "The name of the .NET entity associated with the `Udt` so that it can be expressed (created) as a Table-Valued Parameter for usage within the corresponding `DbMapper`.", IsImportant = true)] + public string? Tvp { get; set; } + + /// + /// Indicates that a `Merge` (upsert of `Udt` list) stored procedure will be automatically generated where not otherwise explicitly specified. + /// + [JsonProperty("delete", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Udt", Title = "Indicates that a `Merge` (upsert of `Udt` list) stored procedure will be automatically generated where not otherwise explicitly specified.")] + public bool? Merge { get; set; } + + #endregion + + #region Auth + + /// + /// Gets or sets the permission (prefix) to be used for security permission checking (suffix defaults to `Read`, `Write` or `Delete` and can be overridden in the underlying stored procedure). + /// + [JsonProperty("excludeColumns", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Auth", Title = "The permission (prefix) to be used for security permission checking (suffix defaults to `Read`, `Write` or `Delete` and can be overridden in the underlying stored procedure).", IsImportant = true)] + public string? Permission { get; set; } + + #endregion + + /// + /// Gets the corresponding (actual) database table configuration. + /// + public Table? DbTable { get; private set; } + + /// + /// Gets the selected (actual) database column configurations. + /// + public List DbColumns { get; } = new List(); + + /// + /// + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "Requirement is for lowercase.")] + protected override void Prepare() + { + Schema = DefaultWhereNull(Schema, () => "dbo"); + DbTable = Root!.DbTables.Where(x => x.Name == Name && x.Schema == Schema).SingleOrDefault(); + if (DbTable == null) + throw new CodeGenException($"Specified Schema.Table '{Schema}.{Name}' not found in database."); + + Alias = DefaultWhereNull(Alias, () => new string(StringConversion.ToSentenceCase(Name)!.Split(' ').Select(x => x.Substring(0, 1).ToLower(System.Globalization.CultureInfo.InvariantCulture).ToCharArray()[0]).ToArray())); + + foreach (var c in DbTable.Columns) + { + if ((ExcludeColumns == null || !ExcludeColumns.Contains(c.Name)) && (IncludeColumns == null || IncludeColumns.Contains(c.Name))) + DbColumns.Add(c); + } + } + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Config/Entity/CodeGenConfig.cs b/tools/Beef.CodeGen.Core/Config/Entity/CodeGenConfig.cs index 9c0587028..6f1ef0fe8 100644 --- a/tools/Beef.CodeGen.Core/Config/Entity/CodeGenConfig.cs +++ b/tools/Beef.CodeGen.Core/Config/Entity/CodeGenConfig.cs @@ -2,8 +2,8 @@ using Beef.Entities; using Newtonsoft.Json; -using System; using System.Collections.Generic; +using System.Linq; namespace Beef.CodeGen.Config.Entity { @@ -18,7 +18,7 @@ namespace Beef.CodeGen.Config.Entity [CategorySchema("DataSvc", Title = "Provides the **Data Services-layer** configuration.")] [CategorySchema("Data", Title = "Provides the generic **Data-layer** configuration.")] [CategorySchema("Grpc", Title = "Provides the **gRPC** configuration.")] - public class CodeGenConfig : ConfigBase + public class CodeGenConfig : ConfigBase, IRootConfig { #region RefData @@ -26,22 +26,22 @@ public class CodeGenConfig : ConfigBase /// Gets or sets the namespace for the Reference Data entities (adds as a c# using statement) where the is `Common`. /// [JsonProperty("refDataNamespace", DefaultValueHandling = DefaultValueHandling.Ignore)] - [PropertySchema("Key", Title = "The namespace for the Reference Data entities (adds as a c# `using` statement) where the `Entity.EntityScope` property configuration is `Common`.", IsImportant = true)] + [PropertySchema("RefData", Title = "The namespace for the Reference Data entities (adds as a c# `using` statement) where the `Entity.EntityScope` property configuration is `Common`.", IsImportant = true)] public string? RefDataNamespace { get; set; } /// /// Gets or sets the namespace for the Reference Data entities (adds as a c# using statement) where the is `Business`. /// [JsonProperty("refDataBusNamespace", DefaultValueHandling = DefaultValueHandling.Ignore)] - [PropertySchema("Key", Title = "The namespace for the Reference Data entities (adds as a c# `using` statement) where the `Entity.EntityScope` property configuration is `Business`.")] + [PropertySchema("RefData", Title = "The namespace for the Reference Data entities (adds as a c# `using` statement) where the `Entity.EntityScope` property configuration is `Business`.")] public string? RefDataBusNamespace { get; set; } /// /// Gets or sets the RouteAtttribute for the Reference Data Web API controller required for named pre-fetching. /// - [JsonProperty("refDataWebApiRoutePrefix", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("refDataWebApiRoute", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertySchema("RefData", Title = "The `RouteAtttribute` for the Reference Data Web API controller required for named pre-fetching.", IsImportant = true)] - public string? RefDataWebApiRoutePrefix { get; set; } + public string? RefDataWebApiRoute { get; set; } /// /// Gets or sets the cache used for the ReferenceData providers. @@ -120,7 +120,7 @@ public class CodeGenConfig : ConfigBase /// [JsonProperty("webApiAuthorize", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertySchema("WebApi", Title = "Indicates whether the Web API controller should use `Authorize` (`true`); otherwise, `AllowAnonynous` (`false`).", - Description = "Defaults to the `AllowAnonynous`. This can be overidden within the `Entity`(s) and/or their corresponding `Operation`(s).")] + Description = "Defaults to the `AllowAnonynous` (`false`). This can be overidden within the `Entity`(s) and/or their corresponding `Operation`(s).")] public bool? WebApiAuthorize { get; set; } #endregion @@ -176,7 +176,7 @@ public class CodeGenConfig : ConfigBase /// [JsonProperty("refDataDefaultMapperConverter", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertySchema("Manager", Title = "The default Reference Data property `Converter` used by the generated `Mapper`(s) where not specifically defined.", Options = new string[] { "ReferenceDataCodeConverter", "ReferenceDataInt32IdConverter", "ReferenceDataNullableInt32IdConverter", "ReferenceDataGuidIdConverter", "ReferenceDataNullableGuidIdConverter" }, - Description = "Defaults to `Business`. A value of `Business` indicates that the Validators will be defined within the `Business` namespace/assembly; otherwise, defined within the `Common` namespace/assembly.")] + Description = "Defaults to `ReferenceDataCodeConverter`.")] public string? RefDataDefaultMapperConverter { get; set; } /// @@ -274,10 +274,18 @@ public void ReplaceRuntimeParameters(Dictionary parameters) foreach (var p in parameters) { - RuntimeParameters.Add(p.Key, p.Value); + if (RuntimeParameters.ContainsKey(p.Key)) + RuntimeParameters[p.Key] = p.Value; + else + RuntimeParameters.Add(p.Key, p.Value); } } + /// + /// Resets the runtime parameters. + /// + public void ResetRuntimeParameters() => RuntimeParameters.Clear(); + /// /// Gets the specified runtime parameter value. /// @@ -319,6 +327,31 @@ internal bool GetRuntimeBoolParameter(string name) [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "This is appropriate for what is obstensibly a DTO.")] public List? Entities { get; set; } + /// + /// Gets the that are selected for IXxxManager. + /// + public List? IManagerEntities => Entities.Where(x => CompareNullOrValue(x.ExcludeIManager, false) && x.Operations!.Count > 0).ToList(); + + /// + /// Gets the that are selected for IXxxData. + /// + public List? IDataSvcEntities => Entities.Where(x => CompareNullOrValue(x.ExcludeIDataSvc, false) && x.Operations!.Count > 0).ToList(); + + /// + /// Gets the that are selected for IXxxData. + /// + public List? IDataEntities => Entities.Where(x => CompareNullOrValue(x.ExcludeIData, false) && x.Operations!.Count > 0).ToList(); + + /// + /// Gets the that are selected for Reference Data. + /// + public List? RefDataEntities => Entities.Where(x => !string.IsNullOrEmpty(x.RefDataType) && CompareNullOrValue(x.Abstract, false)).ToList(); + + /// + /// Gets the that are selected for Grpc. + /// + public List? GrpcEntities => Entities.Where(x => CompareValue(x.Grpc, true) && CompareNullOrValue(x.Abstract, false)).ToList(); + /// /// Gets the company name from the . /// @@ -329,6 +362,11 @@ internal bool GetRuntimeBoolParameter(string name) /// public string AppName => GetRuntimeParameter("AppName", true)!; + /// + /// Gets the API name from the . + /// + public string ApiName => DefaultWhereNull(GetRuntimeParameter("ApiName"), () => "Api")!; + /// /// Gets the entity scope from the from the (defaults to 'Common'). /// @@ -344,6 +382,16 @@ internal bool GetRuntimeBoolParameter(string name) /// public bool IsDataModel => GetRuntimeBoolParameter("IsDataModel"); + /// + /// Indicates whether the intended code generation is explicitly for Reference Data. + /// + public bool IsRefData => GetRuntimeBoolParameter("IsRefData"); + + /// + /// Gets the reference data specific properties. + /// + public RefDataConfig? RefData { get; private set; } + /// /// /// @@ -359,14 +407,19 @@ protected override void Prepare() CosmosName = DefaultWhereNull(CosmosName, () => "ICosmosDb"); ODataName = DefaultWhereNull(ODataName, () => "IOData"); JsonSerializer = DefaultWhereNull(JsonSerializer, () => "Newtonsoft"); + RefDataDefaultMapperConverter = DefaultWhereNull(RefDataDefaultMapperConverter, () => "ReferenceDataCodeConverter"); + WebApiAuthorize = DefaultWhereNull(WebApiAuthorize, () => false); if (Entities != null && Entities.Count > 0) { foreach (var entity in Entities) { - entity.Prepare(this); + entity.Prepare(Root!, this); } } + + RefData = new RefDataConfig(); + RefData.Prepare(Root!, this); } } } \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Config/Entity/ConstConfig.cs b/tools/Beef.CodeGen.Core/Config/Entity/ConstConfig.cs index 46c42c860..e3ab41f5b 100644 --- a/tools/Beef.CodeGen.Core/Config/Entity/ConstConfig.cs +++ b/tools/Beef.CodeGen.Core/Config/Entity/ConstConfig.cs @@ -10,7 +10,7 @@ namespace Beef.CodeGen.Config.Entity [JsonObject(MemberSerialization = MemberSerialization.OptIn)] [ClassSchema("Const", Title = "The **Const** is used to define an constant value for an `Entity`.", Description = "", Markdown = "")] [CategorySchema("Key", Title = "Provides the **key** configuration.")] - public class ConstConfig : ConfigBase + public class ConstConfig : ConfigBase { /// /// Gets or sets the unique constant name. @@ -24,7 +24,7 @@ public class ConstConfig : ConfigBase /// [JsonProperty("value", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertySchema("Key", Title = "The C# code for the constant value.", IsMandatory = true, IsImportant = true, - Description = "Where the `Type` is `string` then the specified default value will need to be delimited. Any valid value assignment C# code can be used.")] + Description = "The code generation will ensure it is delimited correctly to output correctly formed C# code.")] public string? Value { get; set; } /// @@ -36,17 +36,21 @@ public class ConstConfig : ConfigBase public string? Text { get; set; } /// - /// Gets or sets the formatted summary text. + /// Gets the formatted summary text. /// - public string? SummaryText { get; set; } + public string? SummaryText => $"Represents a {Text} constant value."; + + /// + /// Gets the value formatted for code output. + /// + public string? FormattedValue => CompareValue(Value, "int") ? Value : (CompareValue(Value, "Guid") ? $"new Guid(\"{Value}\")" : $"\"{Value}\""); /// /// /// protected override void Prepare() { - DefaultWhereNull(Text, () => CodeGenerator.ToSentenceCase(Name)); - SummaryText = $"Represents a {Text} constant value."; + DefaultWhereNull(Text, () => StringConversion.ToSentenceCase(Name)); } } } \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Config/Entity/EntityConfig.cs b/tools/Beef.CodeGen.Core/Config/Entity/EntityConfig.cs index 247a40c27..c6dd32b9a 100644 --- a/tools/Beef.CodeGen.Core/Config/Entity/EntityConfig.cs +++ b/tools/Beef.CodeGen.Core/Config/Entity/EntityConfig.cs @@ -2,6 +2,7 @@ using Beef.Caching; using Beef.Entities; +using HandlebarsDotNet; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -31,7 +32,8 @@ namespace Beef.CodeGen.Config.Entity [CategorySchema("Model", Title = "Provides the data **Model** configuration.")] [CategorySchema("Grpc", Title = "Provides the **gRPC** configuration.")] [CategorySchema("Exclude", Title = "Provides the **Exclude** configuration.")] - public class EntityConfig : ConfigBase + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "This is appropriate for what is obstensibly a DTO.")] + public class EntityConfig : ConfigBase { #region Key @@ -126,6 +128,14 @@ public class EntityConfig : ConfigBase Description = "Specifies the default sort order for the underlying Reference Data collection. Defaults to `SortOrder`.")] public string? RefDataSortOrder { get; set; } + /// + /// Gets or sets the Reference Data ToString composite . + /// + [JsonProperty("refDataStringFormat", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("RefData", Title = "The Reference Data `ToString` composite format.", + Description = "The string format supports the standard composite formatting; where the following indexes are used: `{0}` for `Id`, `{1}` for `Code` and `{2}` for `Text`. Defaults to `{2}`.")] + public string? RefDataStringFormat { get; set; } + #endregion #region Entity @@ -372,27 +382,27 @@ public class EntityConfig : ConfigBase public string? EntityFrameworkName { get; set; } /// - /// Gets or sets the corresponding Entity Framework entity model name required where is EntityFramework. + /// Gets or sets the corresponding Entity Framework model name required where is EntityFramework. /// - [JsonProperty("entityFrameworkEntity", DefaultValueHandling = DefaultValueHandling.Ignore)] - [PropertySchema("EntityFramework", Title = "The corresponding Entity Framework entity model name (required where `AutoImplement` is `EntityFramework`).", IsImportant = true)] - public string? EntityFrameworkEntity { get; set; } + [JsonProperty("entityFrameworkModel", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("EntityFramework", Title = "The corresponding Entity Framework model name (required where `AutoImplement` is `EntityFramework`).", IsImportant = true)] + public string? EntityFrameworkModel { get; set; } /// /// Gets or sets the name of the Mapper that the generated Entity Framework Mapper inherits from. /// - [JsonProperty("dataEntityFrameworkMapperInheritsFrom", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("entityFrameworkMapperInheritsFrom", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertySchema("EntityFramework", Title = "The name of the `Mapper that the generated Entity Framework `Mapper` inherits from.", Description = "Defaults to `Model.{Name}`; i.e. an entity with the same name in the `Model` namespace.")] - public string? DataEntityFrameworkMapperInheritsFrom { get; set; } + public string? EntityFrameworkMapperInheritsFrom { get; set; } /// /// Indicates that a custom Entity Framework `Mapper` will be used; i.e. not generated. /// - [JsonProperty("dataEntityFrameworkCustomMapper", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("entityFrameworkCustomMapper", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertySchema("EntityFramework", Title = "Indicates that a custom Entity Framework `Mapper` will be used; i.e. not generated.", Description = "Otherwise, by default, a `Mapper` will be generated.")] - public bool? DataEntityFrameworkCustomMapper { get; set; } + public bool? EntityFrameworkCustomMapper { get; set; } #endregion @@ -407,11 +417,11 @@ public class EntityConfig : ConfigBase public string? CosmosName { get; set; } /// - /// Gets or sets the corresponding Cosmos entity model name required where is Cosmos. + /// Gets or sets the corresponding Cosmos model name required where is Cosmos. /// - [JsonProperty("cosmosEntity", DefaultValueHandling = DefaultValueHandling.Ignore)] - [PropertySchema("Cosmos", Title = "The corresponding Cosmos entity model name (required where `AutoImplement` is `Cosmos`).", IsImportant = true)] - public string? CosmosEntity { get; set; } + [JsonProperty("cosmosModel", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Cosmos", Title = "The corresponding Cosmos model name (required where `AutoImplement` is `Cosmos`).", IsImportant = true)] + public string? CosmosModel { get; set; } /// /// Gets or sets the Cosmos ContainerId required where is Cosmos. @@ -458,16 +468,16 @@ public class EntityConfig : ConfigBase /// Gets or sets the .NET OData interface name used where `AutoImplement` is `OData`. /// [JsonProperty("odataName", DefaultValueHandling = DefaultValueHandling.Ignore)] - [PropertySchema("OData", Title = "The .NET OData interface name used where `AutoImplement` is `OData`.", + [PropertySchema("OData", Title = "The .NET OData interface name used where `AutoImplement` is `OData`.", Description = "Defaults to the `CodeGeneration.ODataName` configuration property (its default value is `IOData`).")] public string? ODataName { get; set; } /// - /// Gets or sets the corresponding OData entity model name required where is OData. + /// Gets or sets the corresponding OData model name required where is OData. /// - [JsonProperty("odataEntity", DefaultValueHandling = DefaultValueHandling.Ignore)] - [PropertySchema("OData", Title = "The corresponding OData entity model name (required where `AutoImplement` is `OData`).", IsImportant = true)] - public string? ODataEntity { get; set; } + [JsonProperty("odataModel", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("OData", Title = "The corresponding OData model name (required where `AutoImplement` is `OData`).", IsImportant = true)] + public string? ODataModel { get; set; } /// /// Gets or sets the name of the underlying OData collection name where is OData. @@ -613,11 +623,12 @@ public class EntityConfig : ConfigBase public bool? ExcludeIData { get; set; } /// - /// Indicates whether to exclude the creation of the Data class (XxxData.cs). + /// Gets or sets the option to exclude the creation of the Data class (XxxData.cs). /// [JsonProperty("excludeData", DefaultValueHandling = DefaultValueHandling.Ignore)] - [PropertySchema("Exclude", Title = "Indicates whether to exclude the creation of the `Data` class (`XxxData.cs`).")] - public bool? ExcludeData { get; set; } + [PropertySchema("Exclude", Title = "The option to exclude the creation of the `Data` class (`XxxData.cs`).", Options = new string[] { "No", "Yes", "Mapper" }, + Description = "Defaults to `No` indicating _not_ to exlude. A value of `Yes` indicates to exclude all output; alternatively, `Mapper` indicates to at least output the corresponding `Mapper` class.")] + public string? ExcludeData { get; set; } /// /// Indicates whether to exclude the creation of the DataSvc interface (IXxxDataSvc.cs). @@ -699,72 +710,257 @@ public class EntityConfig : ConfigBase /// [JsonProperty("properties", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertyCollectionSchema(Title = "The corresponding `Property` collection.")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "This is appropriate for what is obstensibly a DTO.")] public List? Properties { get; set; } + /// + /// Gets the list of private properties to be implemented (that are not inherited). + /// + public List? PrivateProperties => Properties!.Where(x => (x.Inherited == null || !x.Inherited.Value) && (x.RefDataMapping == null || !x.RefDataMapping.Value)).ToList(); + + /// + /// Gets the list of core properties to be implemented (that are not inherited). + /// + public List? CoreProperties => Properties!.Where(x => (x.Inherited == null || !x.Inherited.Value)).ToList(); + + /// + /// Gets the list of properties that form the unique key. + /// + public List? UniqueKeyProperties => Properties!.Where(x => (x.UniqueKey.HasValue && x.UniqueKey.Value) && (x.Inherited == null || !x.Inherited.Value)).ToList(); + + /// + /// Gets the list of properties that are sub-entities. + /// + public List? EntityProperties => Properties!.Where(x => (x.Inherited == null || !x.Inherited.Value) && x.IsEntity.HasValue && x.IsEntity.Value).ToList(); + + /// + /// Gets the list of properties that are to be used for database mapping. + /// + public List? DatabaseMapperProperties => Properties!.Where(x => CompareNullOrValue(x.DatabaseIgnore, false) && x.Name != "ETag" && x.Name != "ChangeLog").ToList(); + + /// + /// Gets the list of properties that are to be used for entity framework mapping. + /// + public List? EntityFrameworkMapperProperties => Properties!.Where(x => CompareNullOrValue(x.EntityFrameworkIgnore, false) && x.Name != "ETag" && x.Name != "ChangeLog").ToList(); /// - /// Gets the list of properties that are not inherited. + /// Gets the list of properties that are to be used for entity framework mapping. /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1819:Properties should not return arrays", Justification = "This is appropriate for what is obstensibly a DTO.")] - public PropertyConfig[] PropertiesNotInherited => Properties!.Where(x => (x.Inherited == null || !x.Inherited.Value) && (x.RefDataMapping == null || !x.RefDataMapping.Value)).ToArray(); + public List? CosmosMapperProperties => Properties!.Where(x => CompareNullOrValue(x.CosmosIgnore, false) && x.Name != "ETag" && x.Name != "ChangeLog").ToList(); + + /// + /// Gets the list of properties that are to be used for entity framework mapping. + /// + public List? ODataMapperProperties => Properties!.Where(x => CompareNullOrValue(x.ODataIgnore, false) && x.Name != "ETag" && x.Name != "ChangeLog").ToList(); + + /// + /// Gets the list of properties that are to be used for gRPC. + /// + public List? GrpcProperties => Properties!.Where(x => x.GrpcFieldNo.HasValue && x.GrpcFieldNo.Value > 0).ToList(); /// /// Gets or sets the corresponding collection. /// [JsonProperty("operations", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertyCollectionSchema(Title = "The corresponding `Operation` collection.")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "This is appropriate for what is obstensibly a DTO.")] public List? Operations { get; set; } + /// + /// Gets the IEntityManager collection. + /// + public List? IManagerOperations => Operations!.Where(x => CompareNullOrValue(x.ExcludeIManager, false)).ToList(); + + /// + /// Gets the EntityManager collection. + /// + public List? ManagerOperations => Operations!.Where(x => CompareNullOrValue(x.ExcludeManager, false)).ToList(); + + /// + /// Gets the EntityManager collection where the Manager is not custom. + /// + public List? ManagerAutoOperations => Operations!.Where(x => CompareNullOrValue(x.ExcludeManager, false) && CompareNullOrValue(x.ManagerCustom, false)).ToList(); + + /// + /// Gets the IEntityDataSvc collection. + /// + public List? IDataSvcOperations => Operations!.Where(x => CompareNullOrValue(x.ExcludeIDataSvc, false)).ToList(); + + /// + /// Gets the EntityDataSvc collection. + /// + public List? DataSvcOperations => Operations!.Where(x => CompareNullOrValue(x.ExcludeDataSvc, false)).ToList(); + + /// + /// Gets the EntityDataSvc collection where the DataSvc is not custom. + /// + public List? DataSvcAutoOperations => Operations!.Where(x => CompareNullOrValue(x.ExcludeDataSvc, false) && CompareNullOrValue(x.DataSvcCustom, false)).ToList(); + + /// + /// Gets the DataSvc constructor parameters. + /// + public List DataSvcConstructorParameters { get; } = new List(); + + /// + /// Gets the IEntityData collection. + /// + public List? IDataOperations => Operations!.Where(x => CompareNullOrValue(x.ExcludeIData, false)).ToList(); + + /// + /// Gets the IEntityData collection. + /// + public List? DataOperations => Operations!.Where(x => CompareNullOrValue(x.ExcludeData, false)).ToList(); + + /// + /// Gets the Grpc collection. + /// + public List? GrpcOperations => Operations!.Where(x => CompareValue(x.Grpc, true) && !x.IsPatch).ToList(); + + /// + /// Gets the Data constructor parameters. + /// + public List DataConstructorParameters { get; } = new List(); + + /// + /// Gets the EntityController collection. + /// + public List? WebApiOperations => Operations!.Where(x => CompareNullOrValue(x.ExcludeWebApi, false)).ToList(); + + /// + /// Gets the EntityAgent collection. + /// + public List? WebApiAgentOperations => Operations!.Where(x => CompareNullOrValue(x.ExcludeWebApiAgent, false)).ToList(); + + /// + /// Gets the WebApi Contructor parameters. + /// + public List WebApiConstructorParameters { get; } = new List(); + /// /// Gets or sets the corresponding collection. /// [JsonProperty("consts", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertyCollectionSchema(Title = "The corresponding `Consts` collection.")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "This is appropriate for what is obstensibly a DTO.")] public List? Consts { get; set; } /// - /// Gets or sets the formatted summary text. + /// Gets the formatted summary text. + /// + public string? SummaryText => CodeGenerator.ToComments($"Represents the {Text} entity."); + + /// + /// Gets the entity name (accounts for ). + /// + public string? EntityName => !GenericWithT.HasValue || !GenericWithT.Value ? Name : $"{Name}"; + + /// + /// Gets the entity collection name (accounts for ). + /// + public string? EntityCollectionName => !GenericWithT.HasValue || !GenericWithT.Value ? $"{Name}Collection" : $"{Name}Collection"; + + /// + /// Gets the entity collection result name (accounts for ). + /// + public string? EntityCollectionResultName => !GenericWithT.HasValue || !GenericWithT.Value ? $"{Name}CollectionResult" : $"{Name}CollectionResult"; + + /// + /// Gets the formatted as see comments. + /// + public string? EntityNameSeeComments => CompareValue(ExcludeEntity, true) ? $"{Name}" : CodeGenerator.ToSeeComments(Name); + + /// + /// Gets or sets the computed entity inherits. + /// + public string? EntityInherits { get; set; } + + /// + /// Gets or sets the computed model inherits. + /// + public string? ModelInherits { get; set; } + + /// + /// Gets or sets the computed entity collection inherits. + /// + public string? EntityCollectionInherits { get; set; } + + /// + /// Gets or sets the computed entity inherits. + /// + public string? EntityImplements { get; set; } + + /// + /// Gets or sets the computed model inherits. + /// + public string? ModelImplements { get; set; } + + /// + /// Inidicates whether any of the operations use validators. + /// + public bool UsesValidators => Validator != null || Operations.Any(x => x.Validator != null || x.Parameters.Any(y => y.Validator != null)); + + /// + /// Indicates whether at least one operation needs a Manager. + /// + public bool RequiresManager => !(CompareValue(ExcludeManager, true) && CompareValue(ExcludeIManager, true)); + + /// + /// Indicates whether at least one operation needs a DataSvc. + /// + public bool RequiresDataSvc => !(CompareValue(ExcludeDataSvc, true) && CompareValue(ExcludeIDataSvc, true)) || Operations.Any(x => CompareNullOrValue(x.ManagerCustom, false)); + + /// + /// Indicates whether at least one operation needs a Data. + /// + public bool RequiresData => (CompareValue(ExcludeData, "None") && CompareValue(ExcludeIData, true)) || Operations.Any(x => CompareNullOrValue(x.DataSvcCustom, false)); + + /// + /// Indicates whether any of the operations will raise an event. + /// + public bool SupportsEvents => Operations.Any(x => x.Events.Count > 0); + + /// + /// Indicates whether auto-implementing 'Database'. + /// + public bool UsesDatabase => AutoImplement == "Database" || Operations.Any(x => x.AutoImplement == "Database"); + + /// + /// Indicates whether auto-implementing 'EntityFramework'. + /// + public bool UsesEntityFramework => AutoImplement == "EntityFramework" || EntityFrameworkModel != null || Operations.Any(x => x.AutoImplement == "EntityFramework"); + + /// + /// Indicates whether auto-implementing 'Cosmos'. /// - public string? SummaryText { get; set; } + public bool UsesCosmos => AutoImplement == "Cosmos" || CosmosModel != null || Operations.Any(x => x.AutoImplement == "Cosmos"); /// - /// Gets or sets the entity name (accounts for ). + /// Indicates whether auto-implementing 'OData'. /// - public string? EntityName { get; set; } + public bool UsesOData => AutoImplement == "OData" || ODataModel != null || Operations.Any(x => x.AutoImplement == "OData"); /// - /// Gets or sets the entity collection name (accounts for ). + /// Indicates whether the data extensions section is required. /// - public string? EntityCollectionName { get; set; } + public bool DataExtensionsRequired => CompareValue(DataExtensions, true) || UsesCosmos || DataOperations.Any(x => x.Type == "GetColl"); /// - /// Gets or sets the entity collection result name (accounts for ). + /// Gets the reference data qualified Entity name. /// - public string? EntityCollectionResultName { get; set; } + public string RefDataQualifiedEntityCollectionName => RefDataQualifiedEntityName + "Collection"; /// - /// Indicates whether the entity (based on all configurations) should implement the capabilties. + /// Gets the reference data qualified Entity name. /// - public bool HasEntityBase => !(CompareValue(OmitEntityBase, true) || Parent!.IsDataModel); + public string RefDataQualifiedEntityName => string.IsNullOrEmpty(RefDataType) ? Name! : $"{(string.IsNullOrEmpty(Root?.RefDataNamespace) ? "RefDataBusNamesapce" : "RefDataNamespace")}.{Name}"; /// /// /// protected override void Prepare() { - EntityName = !GenericWithT.HasValue || !GenericWithT.Value ? Name : $"{Name}"; - EntityCollectionName = !GenericWithT.HasValue || !GenericWithT.Value ? Name : $"{Name}Collection"; - EntityCollectionResultName = !GenericWithT.HasValue || !GenericWithT.Value ? Name : $"{Name}CollectionResult"; - - Text = DefaultWhereNull(Text, () => CodeGenerator.ToSentenceCase(Name)); - SummaryText = CodeGenerator.ToComments($"Represents the {Text} entity."); + Text = CodeGenerator.ToComments(DefaultWhereNull(Text, () => StringConversion.ToSentenceCase(Name))); FileName = DefaultWhereNull(FileName, () => Name); EntityScope = DefaultWhereNull(EntityScope, () => "Common"); - PrivateName = DefaultWhereNull(PrivateName, () => CodeGenerator.ToPrivateCase(Name)); - ArgumentName = DefaultWhereNull(ArgumentName, () => CodeGenerator.ToCamelCase(Name)); + PrivateName = DefaultWhereNull(PrivateName, () => StringConversion.ToPrivateCase(Name)); + ArgumentName = DefaultWhereNull(ArgumentName, () => StringConversion.ToCamelCase(Name)); ConstType = DefaultWhereNull(ConstType, () => "string"); RefDataText = DefaultWhereNull(RefDataText, () => Parent!.RefDataText); RefDataSortOrder = DefaultWhereNull(RefDataSortOrder, () => "SortOrder"); @@ -773,15 +969,12 @@ protected override void Prepare() MapperAddStandardProperties = DefaultWhereNull(MapperAddStandardProperties, () => true); AutoImplement = DefaultWhereNull(AutoImplement, () => "None"); DataConstructor = DefaultWhereNull(DataConstructor, () => "Public"); - DatabaseName = DefaultWhereNull(DatabaseName, () => Parent!.DatabaseName); + DatabaseName = InterfaceiseName(DefaultWhereNull(DatabaseName, () => Parent!.DatabaseName)); DatabaseSchema = DefaultWhereNull(DatabaseSchema, () => "dbo"); - EntityFrameworkName = DefaultWhereNull(EntityFrameworkName, () => Parent!.EntityFrameworkName); - EntityFrameworkEntity = DefaultWhereNull(EntityFrameworkEntity, () => $"Model.{Name}"); - CosmosName = DefaultWhereNull(CosmosName, () => Parent!.CosmosName); - CosmosEntity = DefaultWhereNull(CosmosEntity, () => $"Model.{Name}"); - CosmosPartitionKey = DefaultWhereNull(CosmosPartitionKey, () => "ParitionKey.None"); - ODataName = DefaultWhereNull(ODataName, () => Parent!.ODataName); - ODataEntity = DefaultWhereNull(ODataEntity, () => $"Model.{Name}"); + EntityFrameworkName = InterfaceiseName(DefaultWhereNull(EntityFrameworkName, () => Parent!.EntityFrameworkName)); + CosmosName = InterfaceiseName(DefaultWhereNull(CosmosName, () => Parent!.CosmosName)); + CosmosPartitionKey = DefaultWhereNull(CosmosPartitionKey, () => "PartitionKey.None"); + ODataName = InterfaceiseName(DefaultWhereNull(ODataName, () => Parent!.ODataName)); DataSvcCaching = DefaultWhereNull(DataSvcCaching, () => true); DataSvcConstructor = DefaultWhereNull(DataSvcConstructor, () => "Public"); EventPublish = DefaultWhereNull(EventPublish, () => Parent!.EventPublish); @@ -790,7 +983,7 @@ protected override void Prepare() WebApiConstructor = DefaultWhereNull(WebApiConstructor, () => "Public"); ExcludeEntity = DefaultWhereNull(ExcludeEntity, () => false); ExcludeIData = DefaultWhereNull(ExcludeIData, () => CompareValue(ExcludeAll, true)); - ExcludeData = DefaultWhereNull(ExcludeData, () => CompareValue(ExcludeAll, true)); + ExcludeData = DefaultWhereNull(ExcludeData, () => CompareValue(ExcludeAll, true) ? "Yes" : "No"); ExcludeIDataSvc = DefaultWhereNull(ExcludeIDataSvc, () => CompareValue(ExcludeAll, true)); ExcludeDataSvc = DefaultWhereNull(ExcludeDataSvc, () => CompareValue(ExcludeAll, true)); ExcludeIManager = DefaultWhereNull(ExcludeIManager, () => CompareValue(ExcludeAll, true)); @@ -804,37 +997,44 @@ protected override void Prepare() PrepareProperties(); PrepareOperations(); InferImplements(); + PrepareConstructors(); } + /// + /// Interface-ise the name; i.e. prefix with an 'I'. + /// + private static string? InterfaceiseName(string? name) => string.IsNullOrEmpty(name) || (name.StartsWith("I", StringComparison.Ordinal) && name.Length >= 2 && char.IsUpper(name[1])) ? name : "I" + name; + /// /// Infers the , and values. /// private void InferInherits() { - Inherits = DefaultWhereNull(Inherits, () => + EntityInherits = Inherits; + EntityInherits = DefaultWhereNull(EntityInherits, () => RefDataType switch { - if (!CompareNullOrValue(OmitEntityBase, false)) - return null; - - return RefDataType switch - { - "int" => "ReferenceDataBaseInt", - "Guid" => "ReferenceDataBaseGuid", - _ => "EntityBase" - }; + "int" => "ReferenceDataBaseInt", + "Guid" => "ReferenceDataBaseGuid", + _ => CompareNullOrValue(OmitEntityBase, false) ? "EntityBase" : null }); - CollectionInherits = DefaultWhereNull(CollectionInherits, () => + ModelInherits = RefDataType switch { - if (!CompareNullOrValue(OmitEntityBase, false)) - return $"List<{EntityName}>"; + "int" => "ReferenceDataBaseInt", + "Guid" => "ReferenceDataBaseGuid", + _ => null + }; + EntityCollectionInherits = CollectionInherits; + EntityCollectionInherits = DefaultWhereNull(EntityCollectionInherits, () => + { if (RefDataType == null) return CompareValue(CollectionKeyed, true) ? $"EntityBaseKeyedCollection" : $"EntityBaseCollection<{EntityName}>"; else return $"ReferenceDataCollectionBase<{EntityName}>"; }); + CollectionInherits = DefaultWhereNull(CollectionInherits, () => $"List<{EntityName}>"); CollectionResultInherits = DefaultWhereNull(CollectionResultInherits, () => $"EntityCollectionResult<{EntityCollectionName}, {EntityName}>"); } @@ -848,7 +1048,7 @@ private void PrepareConsts() foreach (var constant in Consts) { - constant.Prepare(this); + constant.Prepare(Root!, this); } } @@ -863,7 +1063,7 @@ private void PrepareProperties() if (RefDataType != null) { var i = 0; - AddInheritedProperty("Id", ref i, () => new PropertyConfig { Text = "{{" + Name + "}} identifier", Type = RefDataType, UniqueKey = true, DataAutoGenerated = true, DataName = $"{Name}Id" }); + AddInheritedProperty("Id", ref i, () => new PropertyConfig { Text = $"{{{{{Name}}}}} identifier", Type = RefDataType, UniqueKey = true, DataAutoGenerated = true, DataName = $"{Name}Id" }); AddInheritedProperty("Code", ref i, () => new PropertyConfig { Type = "string" }); AddInheritedProperty("Text", ref i, () => new PropertyConfig { Type = "string" }); AddInheritedProperty("IsActive", ref i, () => new PropertyConfig { Type = "bool" }); @@ -874,7 +1074,7 @@ private void PrepareProperties() foreach (var property in Properties) { - property.Prepare(this); + property.Prepare(Root!, this); } } @@ -922,7 +1122,7 @@ private void PrepareOperations() // Prepare each operations. foreach (var operation in Operations) { - operation.Prepare(this); + operation.Prepare(Root!, this); } } @@ -931,22 +1131,24 @@ private void PrepareOperations() /// private void InferImplements() { - if (!ImplementsAutoInfer.HasValue || !ImplementsAutoInfer.Value) + if (ImplementsAutoInfer.HasValue && !ImplementsAutoInfer.Value) return; var implements = new List(); + var modelImplements = new List(); + if (Implements != null) { foreach (var str in Implements!.Split(",", StringSplitOptions.RemoveEmptyEntries)) { var txt = str?.Trim(); - if (string.IsNullOrEmpty(txt)) + if (!string.IsNullOrEmpty(txt)) implements.Add(txt!); } } var i = 0; - var id = Properties.FirstOrDefault(x => x.Name == "Id" && (!x.Inherited.HasValue || !x.Inherited.Value) && x.UniqueKey.HasValue && x.UniqueKey.Value); + var id = Properties.FirstOrDefault(x => x.Name == "Id" && CompareNullOrValue(x.Inherited, false)); if (id != null) { var iid = id.Type switch @@ -957,17 +1159,82 @@ private void InferImplements() _ => "IIdentifier", }; - if (!implements.Contains(iid)) - implements.Insert(i++, iid); + implements.Insert(i, iid); + modelImplements.Insert(i++, iid); + } + + if (Properties.Any(x => x.Name == "ETag" && x.Type == "string" && CompareNullOrValue(x.Inherited, false))) + { + implements.Insert(i, "IETag"); + modelImplements.Insert(i++, "IETag"); + } + + if (Properties.Any(x => x.Name == "ChangeLog" && x.Type == "ChangeLog" && CompareNullOrValue(x.Inherited, false))) + { + implements.Insert(i, "IChangeLog"); + modelImplements.Insert(i++, "IChangeLog"); } - if (Properties.Any(x => x.Name == "ETag" && x.Type == "string") && !implements.Contains("IETag")) - implements.Insert(i++, "IETag"); + //Implements = implements.Count == 0 ? null : string.Join(", ", implements.ToArray()); - if (Properties.Any(x => x.Name == "ChangeLog" && x.Type == "ChangeLog") && !implements.Contains("IChangeLog")) - implements.Insert(i++, "IChangeLog"); + if (RefDataType == null) + implements.Add($"IEquatable<{EntityName}>"); - Implements = implements.Count == 0 ? null : string.Join(", ", implements.ToArray()); + EntityImplements = implements.Count == 0 ? null : string.Join(", ", implements.GroupBy(x => x).Select(y => y.First()).ToArray()); + ModelImplements = modelImplements.Count == 0 ? null : string.Join(", ", modelImplements.GroupBy(x => x).Select(y => y.First()).ToArray()); + } + + /// + /// Prepare the constructors. + /// + private void PrepareConstructors() + { + // DataSvc constructors. + var oc = new OperationConfig(); + oc.Prepare(Root!, this); + + if (RequiresData) + DataSvcConstructorParameters.Add(new ParameterConfig { Name = "Data", Type = $"I{Name}Data", Text = $"{{{{I{Name}Data}}}}" }); + + if (SupportsEvents) + DataSvcConstructorParameters.Add(new ParameterConfig { Name = "EvtPub", Type = $"IEventPublisher", Text = "{{IEventPublisher}}" }); + + if (CompareValue(DataSvcCaching, true) && Operations.Any(x => x.SupportsCaching)) + DataSvcConstructorParameters.Add(new ParameterConfig { Name = "Cache", Type = "IRequestCache", Text = "{{IRequestCache}}" }); + else + DataSvcCaching = false; + + foreach (var ctor in DataSvcConstructorParameters) + { + ctor.Prepare(Root!, oc); + } + + // Data constructors. + if (UsesDatabase) + DataConstructorParameters.Add(new ParameterConfig { Name = "Db", Type = DatabaseName, Text = $"{{{{{DatabaseName}}}}}" }); + + if (UsesEntityFramework) + DataConstructorParameters.Add(new ParameterConfig { Name = "Ef", Type = EntityFrameworkName, Text = $"{{{{{EntityFrameworkName}}}}}" }); + + if (UsesCosmos) + DataConstructorParameters.Add(new ParameterConfig { Name = "Cosmos", Type = CosmosName, Text = $"{{{{{CosmosName}}}}}" }); + + if (UsesOData) + DataConstructorParameters.Add(new ParameterConfig { Name = "OData", Type = ODataName, Text = $"{{{{{ODataName}}}}}" }); + + foreach (var ctor in DataConstructorParameters) + { + ctor.Prepare(Root!, oc); + } + + // WebAPI contstructors. + if (RequiresManager) + WebApiConstructorParameters.Insert(0, new ParameterConfig { Name = "Manager", Type = $"I{Name}Manager", Text = $"{{{{I{Name}Manager}}}}" }); + + foreach (var ctor in WebApiConstructorParameters) + { + ctor.Prepare(Root!, oc); + } } } } \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Config/Entity/OperationConfig.cs b/tools/Beef.CodeGen.Core/Config/Entity/OperationConfig.cs index 55ce0d0f4..e25791870 100644 --- a/tools/Beef.CodeGen.Core/Config/Entity/OperationConfig.cs +++ b/tools/Beef.CodeGen.Core/Config/Entity/OperationConfig.cs @@ -1,9 +1,11 @@ // Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef +using Beef.Events; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace Beef.CodeGen.Config.Entity { @@ -20,7 +22,8 @@ namespace Beef.CodeGen.Config.Entity [CategorySchema("Data", Title = "Provides the generic **Data-layer** configuration.")] [CategorySchema("Grpc", Title = "Provides the **gRPC** configuration.")] [CategorySchema("Exclude", Title = "Provides the **Exclude** configuration.")] - public class OperationConfig : ConfigBase + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "This is appropriate for what is obstensibly a DTO.")] + public class OperationConfig : ConfigBase { #region Key @@ -56,19 +59,19 @@ public class OperationConfig : ConfigBase public string? Validator { get; set; } /// - /// Indicates that the properties marked as a unique key (`Property.UniqueKey`) are to be used as the parameters. + /// Indicates whether the properties marked as a unique key (`Property.UniqueKey`) are to be used as the parameters. /// [JsonProperty("uniqueKey", DefaultValueHandling = DefaultValueHandling.Ignore)] - [PropertySchema("Key", Title = "Indicates that the properties marked as a unique key (`Property.UniqueKey`) are to be used as the parameters.", IsImportant = true, + [PropertySchema("Key", Title = "Indicates whether the properties marked as a unique key (`Property.UniqueKey`) are to be used as the parameters.", IsImportant = true, Description = "This simplifies the specification of these properties versus having to declare each specifically.")] public bool? UniqueKey { get; set; } /// - /// Indicates that a PagingArgs argument is to be added to the operation to enable paging related logic. + /// Indicates whether a PagingArgs argument is to be added to the operation to enable paging related logic. /// - [JsonProperty("pagingArgs", DefaultValueHandling = DefaultValueHandling.Ignore)] - [PropertySchema("Key", Title = "Indicates that a `PagingArgs` argument is to be added to the operation to enable (standardized) paging related logic.", IsImportant = true)] - public bool? PagingArgs { get; set; } + [JsonProperty("paging", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Key", Title = "Indicates whether a `PagingArgs` argument is to be added to the operation to enable (standardized) paging related logic.", IsImportant = true)] + public bool? Paging { get; set; } /// /// Gets or sets the .NET value parameter for the operation. @@ -79,13 +82,21 @@ public class OperationConfig : ConfigBase public string? ValueType { get; set; } /// - /// Gets or sets the .NET value parameter for the operation. + /// Gets or sets the .NET return for the operation. /// [JsonProperty("returnType", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertySchema("Key", Title = "The .NET return `Type` for the operation.", Description = "Defaults to the parent `Entity.Name` where the `Operation.Type` options are `Get`, `GetColl`, `Create` or `Update`; otherwise, defaults to `void`.")] public string? ReturnType { get; set; } + /// + /// Indicates whether the is nullable for the operation. + /// + [JsonProperty("returnTypeNullable", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Key", Title = "Indicates whether the `ReturnType` is nullable for the operation.", + Description = "This is only applicable for an `Operation.Type` of `Custom`. Will be inferred where the `ReturnType` is denoted as nullable; i.e. suffixed by a `?`.")] + public bool? ReturnTypeNullable { get; set; } + /// /// Gets or sets the text for use in comments to describe the . /// @@ -203,7 +214,7 @@ public class OperationConfig : ConfigBase /// [JsonProperty("eventSubject", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertySchema("DataSvc", Title = "The event subject template and corresponding event action pair (separated by a colon).", - Description = "The event subject template defaults to `{AppName}.{Entity.Name}` plus each of the unique key placeholders comma separated; e.g. Domain.Entity.{id1},{id2}. " + + Description = "The event subject template defaults to `{AppName}.{Entity.Name}` plus each of the unique key placeholders comma separated; e.g. `Domain.Entity.{id1},{id2}`. " + "The event action defaults to `WebApiOperationType` or `Operation.Type` where not specified. Multiple events can be raised by specifying more than one subject/action pair separated by a semicolon. " + "E.g. `Demo.Person.{id}:Create;Demo.Other.{id}:Update`.")] public string? EventSubject { get; set; } @@ -257,7 +268,7 @@ public class OperationConfig : ConfigBase /// [JsonProperty("patchGetOperation", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertySchema("WebApi", Title = "The corresponding `Get` method name (in the `XxxManager`) where the `Operation.Type` is `Patch`.", - Description = "Defaults to `Get`. Specify either just the method name (e.g. `OperationName`) or, class and method name (e.g. `XxxManager.OperationName`) to be invoked where in a different `YyyManager.OperationName`.")] + Description = "Defaults to `Get`. Specify either just the method name (e.g. `OperationName`) or, interface and method name (e.g. `IXxxManager.OperationName`) to be invoked where in a different `YyyManager.OperationName`.")] public string? PatchGetOperation { get; set; } /// @@ -265,7 +276,7 @@ public class OperationConfig : ConfigBase /// [JsonProperty("patchUpdateOperation", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertySchema("WebApi", Title = "The corresponding `Update` method name (in the `XxxManager`) where the `Operation.Type` is `Patch`.", - Description = "Defaults to `Update`. Specify either just the method name (e.g. `OperationName`) or, class and method name (e.g. `XxxManager.OperationName`) to be invoked where in a different `YyyManager.OperationName`.")] + Description = "Defaults to `Update`. Specify either just the method name (e.g. `OperationName`) or, interface and method name (e.g. `IXxxManager.OperationName`) to be invoked where in a different `YyyManager.OperationName`.")] public string? PatchUpdateOperation { get; set; } #endregion @@ -380,65 +391,256 @@ public class OperationConfig : ConfigBase /// [JsonProperty("parameters", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertyCollectionSchema(Title = "The corresponding `Parameter` collection.")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2227:Collection properties should be read only", Justification = "This is appropriate for what is obstensibly a DTO.")] public List? Parameters { get; set; } /// - /// Gets or sets the formatted summary text. + /// Gets the collection filtered for data access. + /// + public List DataParameters => Parameters!.Where(x => !x.LayerPassing!.StartsWith("ToManager", StringComparison.OrdinalIgnoreCase)).ToList(); + + /// + /// Gets the collection filtered for data access without value. + /// + public List ValueLessDataParameters => DataParameters!.Where(x => CompareNullOrValue(x.IsValueArg, false)).ToList(); + + /// + /// Gets the without the paging parameter. + /// + public List PagingLessDataParameters => DataParameters!.Where(x => CompareNullOrValue(x.IsPagingArgs, false)).ToList(); + + /// + /// Gets the without the value and paging parameters. + /// + public List CoreDataParameters => ValueLessDataParameters!.Where(x => CompareNullOrValue(x.IsPagingArgs, false)).ToList(); + + /// + /// Gets the collection filtered for validation. + /// + public List ValidateParameters => Parameters!.Where(x => CompareValue(x.IsMandatory, true) || x.Validator != null || x.ValidatorCode != null).OrderBy(x => x.IsValueArg).ToList(); + + /// + /// Indicates whether there is only a single parameter to be validated. + /// + public bool SingleValidateParameters => CompareNullOrValue(Parent!.ManagerExtensions, false) && ValidateParameters.Count <= 1; + + /// + /// Gets the collection without the value parameter. + /// + public List? ValueLessParameters => Parameters!.Where(x => !x.IsValueArg).ToList(); + + /// + /// Gets the collection without the paging parameter. + /// + public List? PagingLessParameters => Parameters!.Where(x => !x.IsPagingArgs).ToList(); + + /// + /// Gets the collection without the value and paging parameter. + /// + public List? CoreParameters => ValueLessParameters!.Where(x => !x.IsPagingArgs).ToList(); + + /// + /// Gets the collection without parameters that do not need cleaning. + /// + public List? CleanerParameters => Parameters!.Where(x => !x.LayerPassing!.StartsWith("ToManager", StringComparison.OrdinalIgnoreCase) && !x.IsPagingArgs).ToList(); + + /// + /// Gets the list of events derived from the . + /// + public List Events { get; } = new List(); + + /// + /// Gets the formatted summary text. + /// + public string? SummaryText => Text + "."; + + /// + /// Gets the return type including nullability (where specified). + /// + public string OperationReturnType => CompareValue(ReturnTypeNullable, true) ? ReturnType + "?" : ReturnType!; + + /// + /// Gets the . + /// + public string OperationTaskReturnType => HasReturnValue ? $"Task<{OperationReturnType}>" : "Task"; + + /// + /// Gets the gRPC return type. + /// + public string GrpcReturnType => PropertyConfig.InferGrpcType(BaseReturnType!) + (Type == "GetColl" ? "CollectionResult" : ""); + + /// + /// Gets the for an agent. + /// + public string AgentOperationTaskReturnType => HasReturnValue ? $"Task>" : "Task"; + + /// + /// Gets the for a gRPC agent. + /// + public string GrpcAgentOperationTaskReturnType => HasReturnValue ? $"Task>" : "Task"; + + /// + /// Gets or sets the base return type. + /// + public string? BaseReturnType { get; set; } + + /// + /// Gets or sets the WebAPI return text. + /// + public string? WebApiReturnText { get; set; } + + /// + /// Indicates whether the operation is returning a vale. + /// + public bool HasReturnValue => ReturnType != "void"; + + /// + /// Indicates whether there is a value operation. + /// + public bool HasValue => Parameters.Any(x => x.IsValueArg); + + /// + /// Indicates whether the operation supports caching. + /// + public bool SupportsCaching => new string[] { "Get", "Create", "Update", "Delete" }.Contains(Type); + + /// + /// Gets or sets the data arguments. + /// + public ParameterConfig? DataArgs { get; set; } + + /// + /// Indicates whether the data entity mapper is to be created for the entity. + /// + public bool DataEntityMapperCreate { get; set; } + + /// + /// Gets the Cosmos PartitionKey as C# code. + /// + public string CosmosPartitionKeyCode => CosmosPartitionKey!.StartsWith("PartitionKey.", StringComparison.InvariantCulture) ? CosmosPartitionKey : $"new PartitionKey({CosmosPartitionKey})"; + + /// + /// Indicates whether the operation is a 'Patch'. /// - public string? SummaryText { get; set; } + public bool IsPatch => Type == "Patch"; + + /// + /// Gets or sets the PATCH Get variable. + /// + public string? PatchGetVariable { get; set; } + + /// + /// Gets or sets the PATCH Update variable. + /// + public string? PatchUpdateVariable { get; set; } + + /// + /// Gets or sets the gRPC converter for the return value. + /// + public string? GrpcReturnConverter { get; set; } + + /// + /// Gets or sets the gRPC mapper for the return value. + /// + public string? GrpcReturnMapper { get; set; } /// /// /// protected override void Prepare() { + BaseReturnType = DefaultWhereNull(ReturnType, () => Type switch + { + "Get" => Parent!.EntityName, + "GetColl" => Parent!.EntityName, + "Create" => Parent!.EntityName, + "Update" => Parent!.EntityName, + "Patch" => Parent!.EntityName, + "Delete" => "void", + _ => "void" + }); + + if (BaseReturnType!.EndsWith("?", StringComparison.InvariantCulture)) + { + ReturnType = BaseReturnType = BaseReturnType[0..^1]; + ReturnTypeNullable = true; + } + + ReturnTypeNullable = DefaultWhereNull(ReturnTypeNullable, () => false); + if (ReturnType == "void") + ReturnTypeNullable = false; + else if (Type == "Get") + ReturnTypeNullable = true; + else if (Type != "Custom") + ReturnTypeNullable = false; + + if (ReturnType != null && Type == "GetColl") + ReturnType += "CollectionResult"; + ReturnType = DefaultWhereNull(ReturnType, () => Type switch { "Get" => Parent!.EntityName, "GetColl" => Parent!.EntityCollectionResultName, "Create" => Parent!.EntityName, "Update" => Parent!.EntityName, + "Patch" => Parent!.EntityName, "Delete" => "void", _ => "void" }); - ValueType = DefaultWhereNull(ReturnType, () => Type switch + ValueType = DefaultWhereNull(ValueType, () => Type switch { "Create" => Parent!.EntityName, "Update" => Parent!.EntityName, + "Patch" => Parent!.EntityName, _ => null }); - Text = DefaultWhereNull(Text, () => CodeGenerator.ToSentenceCase(Name)); - SummaryText = CodeGenerator.ToComments(DefaultWhereNull(Text, () => Type switch - { - "Get" => $"Gets the specified {{{{{ReturnType}}}}}.", - "GetColl" => $"Gets the {{{{{ReturnType}}}}} that includes the items that match the selection criteria.", - "Create" => $"Creates a new {{{{{ValueType}}}}}.", - "Update" => $"Updates an existing {{{{{ValueType}}}}}.", - "Delete" => $"Deletes the specified {{{{{Parent!.EntityName}}}}}", - _ => Text - })); + Text = CodeGenerator.ToComments(DefaultWhereNull(Text, () => Type switch + { + "Get" => $"Gets the specified {{{{{ReturnType}}}}}", + "GetColl" => $"Gets the {{{{{ReturnType}}}}} that contains the items that match the selection criteria", + "Create" => $"Creates a new {{{{{ValueType}}}}}", + "Update" => $"Updates an existing {{{{{ValueType}}}}}", + "Patch" => $"Patches an existing {{{{{ValueType}}}}}", + "Delete" => $"Deletes the specified {{{{{Parent!.EntityName}}}}}", + _ => StringConversion.ToSentenceCase(Name) + })); ReturnText = CodeGenerator.ToComments(DefaultWhereNull(ReturnText, () => Type switch { - "Get" => $"The selected {{{{{ReturnType}}}}} where found; otherwise, null.", + "Get" => $"The selected {{{{{ReturnType}}}}} where found", "GetColl" => $"The {{{{{ReturnType}}}}}", - "Create" => $"A refreshed {{{{{ReturnType}}}}}.", - "Update" => $"A refreshed {{{{{ReturnType}}}}}.", + "Create" => $"The created {{{{{ReturnType}}}}}", + "Update" => $"The updated {{{{{ReturnType}}}}}", + "Patch" => $"The patched {{{{{ReturnType}}}}}", "Delete" => null, - _ => "???" - })); + _ => HasReturnValue ? $"A resultant {{{{{ReturnType}}}}}" : null + })) + "."; + + WebApiReturnText = Type == "GetColl" ? CodeGenerator.ToComments($"The {{{{{BaseReturnType}Collection}}}}") : ReturnText; - PrivateName = DefaultWhereNull(PrivateName, () => CodeGenerator.ToPrivateCase(Name)); + PrivateName = DefaultWhereNull(PrivateName, () => StringConversion.ToPrivateCase(Name)); + Validator = DefaultWhereNull(Validator, () => Parent!.Validator); AutoImplement = DefaultWhereNull(AutoImplement, () => Parent!.AutoImplement); + if (Type == "Custom") + AutoImplement = "None"; + + DataEntityMapperCreate = string.IsNullOrEmpty(DataEntityMapper); + DataEntityMapper = DefaultWhereNull(DataEntityMapper, () => AutoImplement switch + { + "Database" => "DbMapper", + "EntityFramework" => "EfMapper", + "Cosmos" => "CosmosMapper", + "OData" => "ODataMapper", + _ => null + }); + DatabaseStoredProc = DefaultWhereNull(DatabaseStoredProc, () => $"sp{Parent!.Name}{Name}"); CosmosContainerId = DefaultWhereNull(CosmosContainerId, () => Parent!.CosmosContainerId); CosmosPartitionKey = DefaultWhereNull(CosmosPartitionKey, () => Parent!.CosmosPartitionKey); ODataCollectionName = DefaultWhereNull(ODataCollectionName, () => Parent!.ODataCollectionName); - EventPublish = DefaultWhereNull(EventPublish, () => Parent!.EventPublish); - WebApiStatus = DefaultWhereNull(WebApiStatus, () => Type! == "Create" ? "Created" : "OK"); + + WebApiStatus = DefaultWhereNull(WebApiStatus, () => Type! == "Create" ? "Created" : (HasReturnValue ? "OK" : "NoContent")); WebApiMethod = Type == "Patch" ? "HttpPatch" : DefaultWhereNull(WebApiMethod, () => Type switch { "Create" => "HttpPost", @@ -452,10 +654,11 @@ protected override void Prepare() { "Get" => "NotFound", "GetColl" => "NoContent", - "Create" => "NoContent", - "Update" => "NoContent", - "Delete" => "NoContent", - _ => "ThrowException" + "Create" => "ThrowException", + "Update" => "ThrowException", + "Patch" => "ThrowException", + "Delete" => "ThrowException", + _ => HasReturnValue ? "NoContent" : "ThrowException" }); WebApiOperationType = DefaultWhereNull(WebApiOperationType, () => Type switch @@ -464,10 +667,22 @@ protected override void Prepare() "GetColl" => "Read", "Create" => "Create", "Update" => "Update", + "Patch" => "Update", "Delete" => "Delete", _ => "Unspecified" }); + EventPublish = DefaultWhereNull(EventPublish, () => Parent!.EventPublish); + EventSubject = DefaultWhereNull(EventSubject, () => Type switch + { + "Create" => $"{Root!.AppName}.{Parent!.Name}.{string.Join(",", Parent!.Properties.Where(p => p.UniqueKey.HasValue && p.UniqueKey.Value).Select(x => $"{{__result.{x.PropertyName}}}"))}:{ConvertEventAction(WebApiOperationType!)}", + "Update" => $"{Root!.AppName}.{Parent!.Name}.{string.Join(",", Parent!.Properties.Where(p => p.UniqueKey.HasValue && p.UniqueKey.Value).Select(x => $"{{__result.{x.PropertyName}}}"))}:{ConvertEventAction(WebApiOperationType!)}", + "Delete" => $"{Root!.AppName}.{Parent!.Name}.{string.Join(",", Parent!.Properties.Where(p => p.UniqueKey.HasValue && p.UniqueKey.Value).Select(x => $"{{{x.ArgumentName}}}"))}:{ConvertEventAction(WebApiOperationType!)}", + _ => null + }); + + PrepareEvents(); + ExcludeIData = DefaultWhereNull(ExcludeIData, () => CompareValue(ExcludeAll, true)); ExcludeData = DefaultWhereNull(ExcludeData, () => CompareValue(ExcludeAll, true)); ExcludeIDataSvc = DefaultWhereNull(ExcludeIDataSvc, () => CompareValue(ExcludeAll, true)); @@ -478,18 +693,66 @@ protected override void Prepare() ExcludeWebApiAgent = DefaultWhereNull(ExcludeWebApiAgent, () => CompareValue(ExcludeAll, true)); ExcludeGrpcAgent = DefaultWhereNull(ExcludeGrpcAgent, () => CompareValue(ExcludeAll, true)); + if (Type == "Patch") + ExcludeIData = ExcludeData = ExcludeIDataSvc = ExcludeDataSvc = ExcludeIManager = ExcludeManager = true; + PrepareParameters(); WebApiRoute = DefaultWhereNull(WebApiRoute, () => Type switch { "GetColl" => "", "Custom" => "", - _ => string.Join(",", Parameters.Select(x => x.ArgumentName)) + _ => string.Join(",", Parameters.Where(x => !x.IsValueArg && !x.IsPagingArgs).Select(x => $"{{{x.ArgumentName}}}")) }); + + if (Type == "Patch") + { + PatchGetOperation = DefaultWhereNull(PatchGetOperation, () => "Get"); + var parts = string.IsNullOrEmpty(PatchGetOperation) ? Array.Empty() : PatchGetOperation.Split(".", StringSplitOptions.RemoveEmptyEntries); + PatchGetVariable = parts.Length <= 1 ? "_manager" : StringConversion.ToPrivateCase(parts[0].Substring(1)); + if (parts.Length > 1) + { + PatchGetOperation = parts[1]; + if (!Parent!.WebApiConstructorParameters.Any(x => x.Type == parts[0])) + Parent!.WebApiConstructorParameters.Add(new ParameterConfig { Name = parts[0].Substring(1), Type = parts[0], Text = $"{{{{{parts[0]}}}}}" }); + } + + PatchUpdateOperation = DefaultWhereNull(PatchUpdateOperation, () => "Update"); + parts = string.IsNullOrEmpty(PatchUpdateOperation) ? Array.Empty() : PatchUpdateOperation.Split(".", StringSplitOptions.RemoveEmptyEntries); + PatchUpdateVariable = parts.Length <= 1 ? "_manager" : StringConversion.ToPrivateCase(parts[0].Substring(1)); + if (parts.Length > 1) + { + PatchUpdateOperation = parts[1]; + if (!Parent!.WebApiConstructorParameters.Any(x => x.Type == parts[0])) + Parent!.WebApiConstructorParameters.Add(new ParameterConfig { Name = parts[0].Substring(1), Type = parts[0], Text = $"{{{{{parts[0]}}}}}" }); + } + } + + PrepareData(); + + GrpcReturnMapper = Beef.CodeGen.CodeGenConfig.SystemTypes.Contains(BaseReturnType) ? null : GrpcReturnType; + GrpcReturnConverter = BaseReturnType switch + { + "DateTime" => $"{(CompareValue(ReturnTypeNullable, true) ? "Nullable" : "")}DateTimeToTimestamp", + "Guid" => $"{(CompareValue(ReturnTypeNullable, true) ? "Nullable" : "")}GuidToStringConverter", + "decimal" => $"{(CompareValue(ReturnTypeNullable, true) ? "Nullable" : "")}DecimalToDecimalConverter", + _ => null + }; } /// - /// Prepares the properties. + /// Converts the event action. + /// + private string ConvertEventAction(string action) => Root!.EventActionFormat switch + { + "UpperCase" => action.ToUpperInvariant(), + "PastTense" => StringConversion.ToPastTense(action)!, + "PastTenseUpperCase" => StringConversion.ToPastTense(action)!.ToUpperInvariant(), + _ => action + }; + + /// + /// Prepares the parameters. /// private void PrepareParameters() { @@ -498,22 +761,81 @@ private void PrepareParameters() var i = 0; var isCreateUpdate = new string[] { "Create", "Update", "Patch" }.Contains(Type); - if (isCreateUpdate) - Parameters.Insert(i++, new ParameterConfig { Name = "Value", Type = Parent!.Name, Text = $"{{{{{Parent.Name}}}}}", IsMandatory = true}); + Parameters.Insert(i++, new ParameterConfig { Name = "Value", Type = ValueType, Text = $"{{{{{ValueType}}}}}", Nullable = false, IsMandatory = false, Validator = Validator, IsValueArg = true, WebApiFrom = "FromBody" }); if (UniqueKey.HasValue && UniqueKey.Value) { foreach (var pc in Parent!.Properties.Where(p => p.UniqueKey.HasValue && p.UniqueKey.Value)) { - Parameters.Insert(i++, new ParameterConfig { Name = pc.Name, IsMandatory = !isCreateUpdate, LayerPassing = isCreateUpdate ? "ToManagerSet" : "All", Property = pc.Name }); + Parameters.Insert(i++, new ParameterConfig { Name = pc.Name, Text = pc.Text, IsMandatory = new string[] { "Get", "Delete" }.Contains(Type), LayerPassing = isCreateUpdate ? "ToManagerSet" : "All", Property = pc.Name }); } } + if (Type == "GetColl" && CompareValue(Paging, true)) + Parameters.Add(new ParameterConfig { Name = "Paging", Type = "PagingArgs", Text = "{{PagingArgs}}", IsPagingArgs = true }); + foreach (var parameter in Parameters) { - parameter.Prepare(this); + parameter.Prepare(Root!, this); } } + + /// + /// Prepares the . + /// + private void PrepareEvents() + { + if (string.IsNullOrEmpty(EventSubject) || CompareNullOrValue(Parent!.EventPublish, false)) + return; + + foreach (var @event in EventSubject!.Split(";", StringSplitOptions.RemoveEmptyEntries)) + { + var ed = new EventData(); + var parts = @event.Split(":"); + if (parts.Length > 0) + ed.Subject = parts[0]; + + if (parts.Length > 1) + ed.Action = parts[1]; + + if (Root!.EventSubjectRoot != null) + ed.Subject = Root!.EventSubjectRoot + "." + ed.Subject; + + Events.Add(ed); + } + } + + /// + /// Prepares for the data access. + /// + private void PrepareData() + { + DataArgs = new ParameterConfig { PrivateName = "__dataArgs" }; + switch (AutoImplement) + { + case "Database": + DataArgs.Name = "_db"; + DataArgs.Type = "IDatabaseArgs"; + break; + + case "EntityFramework": + DataArgs.Name = "_ef"; + DataArgs.Type = "IEfDbArgs"; + break; + + case "Cosmos": + DataArgs.Name = "_cosmos"; + DataArgs.Type = "ICosmosDbArgs"; + break; + + case "OData": + DataArgs.Name = "_odata"; + DataArgs.Type = "IODataArgs"; + break; + } + + DataArgs.Prepare(Root!, this); + } } } \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Config/Entity/ParameterConfig.cs b/tools/Beef.CodeGen.Core/Config/Entity/ParameterConfig.cs index f544fa052..cc2255c86 100644 --- a/tools/Beef.CodeGen.Core/Config/Entity/ParameterConfig.cs +++ b/tools/Beef.CodeGen.Core/Config/Entity/ParameterConfig.cs @@ -1,5 +1,6 @@ // Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef +using Beef.Entities; using Newtonsoft.Json; using System; using System.Linq; @@ -16,7 +17,8 @@ namespace Beef.CodeGen.Config.Entity [CategorySchema("Manager", Title = "Provides the generic **Manager-layer** configuration.")] [CategorySchema("Data", Title = "Provides the generic **Data-layer** configuration.")] [CategorySchema("WebApi", Title = "Provides the data **Web API** configuration.")] - public class ParameterConfig : ConfigBase + [CategorySchema("Grpc", Title = "Provides the **gRPC** configuration.")] + public class ParameterConfig : ConfigBase { #region Key @@ -75,7 +77,7 @@ public class ParameterConfig : ConfigBase /// Indicates whether the .NET should be declared as nullable. /// [JsonProperty("nullable", DefaultValueHandling = DefaultValueHandling.Ignore)] - [PropertySchema("Property", Title = "Indicates whether the .NET `Type should be declared as nullable; e.g. `string?`.", IsImportant = true)] + [PropertySchema("Property", Title = "Indicates whether the .NET `Type should be declared as nullable; e.g. `string?`. Will be inferred where the `Type` is denoted as nullable; i.e. suffixed by a `?`.", IsImportant = true)] public bool? Nullable { get; set; } /// @@ -120,9 +122,9 @@ public class ParameterConfig : ConfigBase /// /// Gets or sets the fluent-style method-chaining C# validator code to append to `IsMandatory` and `Validator` (where specified). /// - [JsonProperty("validatorFluent", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("validatorCode", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertySchema("Manager", Title = "The fluent-style method-chaining C# validator code to append to `IsMandatory` and `Validator` (where specified).")] - public string? ValidatorFluent { get; set; } + public string? ValidatorCode { get; set; } /// /// Indicates whether a should be thrown when the parameter value has its default value (null, zero, etc). @@ -175,10 +177,72 @@ public class ParameterConfig : ConfigBase #endregion + #region Grpc + + /// + /// Gets or sets the underlying gRPC data type. + /// + [JsonProperty("grpcType", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Grpc", Title = "The underlying gRPC data type; will be inferred where not specified.")] + public string? GrpcType { get; set; } + + #endregion + /// - /// Gets or sets the formatted summary text. + /// Indicates whether the parameter is the auto-added value. /// - public string? SummaryText { get; set; } + public bool IsValueArg { get; set; } + + /// + /// Indicates whether the parameter is the auto-enabled . + /// + public bool IsPagingArgs { get; set; } + + /// + /// Gets the formatted summary text. + /// + public string? SummaryText => IsValueArg && Parent!.Type == "Patch" ? CodeGenerator.ToComments($"The {{{{JToken}}}} that contains the patch content for the {Text}.") + : CodeGenerator.ToComments($"{(Type == "bool" ? "Indicates whether" : "The")} {Text}."); + + /// + /// Gets the computed declared parameter type. + /// + public string? ParameterType => CompareValue(Nullable, true) ? $"{Type}?" : Type; + + /// + /// Gets the WebApi parameter type. + /// + public string WebApiParameterType => IsValueArg && Parent!.Type == "Patch" ? "JToken" : string.IsNullOrEmpty(RefDataType) ? ParameterType! : (CompareValue(Nullable, true) ? $"{RefDataType}?" : RefDataType!); + + /// + /// Gets the WebApi Agent parameter type. + /// + public string WebApiAgentParameterType => IsValueArg && Parent!.Type == "Patch" ? "JToken" : ParameterType!; + + /// + /// Gets the for use in an Agent. + /// + public string? WebApiAgentFrom => WebApiFrom switch { "FromBody" => "FromBody", "FromEntityProperties" => "FromUriUseProperties", _ => null }; + + /// + /// Gets the parameter argument using the specified converter. + /// + public string ParameterConverted => string.IsNullOrEmpty(DataConverter) ? ArgumentName! : $"{DataConverter}{(CompareValue(DataConverterIsGeneric, true) ? $"<{ParameterType}>" : "")}.Default.ConvertToDest({ArgumentName})"; + + /// + /// Gets or sets the related entity. + /// + public EntityConfig? RelatedEntity { get; set; } + + /// + /// Gets or sets the gRPC converter. + /// + public string? GrpcConverter { get; set; } + + /// + /// Gets or sets the gRPC mapper. + /// + public string? GrpcMapper { get; set; } /// /// @@ -188,19 +252,51 @@ protected override void Prepare() var pc = Property == null ? null : Parent!.Parent!.Properties.FirstOrDefault(x => x.Name == Name); Type = DefaultWhereNull(Type, () => pc == null ? "string" : pc.Type); - Text = DefaultWhereNull(Text, () => pc == null ? CodeGenerator.ToSentenceCase(Name) : pc.Text); - SummaryText = CodeGenerator.ToComments($"{(Type == "bool" ? "Indicates whether" : "The")} {Text}."); - PrivateName = DefaultWhereNull(PrivateName, () => pc == null ? CodeGenerator.ToPrivateCase(Name) : pc.Name); - ArgumentName = DefaultWhereNull(ArgumentName, () => pc == null ? CodeGenerator.ToCamelCase(Name) : pc.ArgumentName); + if (Type!.EndsWith("?", StringComparison.InvariantCulture)) + { + Type = Type[0..^1]; + Nullable = true; + } + + RelatedEntity = Root!.Entities.FirstOrDefault(x => x.Name == Type); + Text = CodeGenerator.ToComments(DefaultWhereNull(Text, () => + { + if (Type!.StartsWith("RefDataNamespace.", StringComparison.InvariantCulture)) + return $"{StringConversion.ToSentenceCase(Name)} (see {CodeGenerator.ToSeeComments(Type)})"; + + if (RelatedEntity != null) + { + if (RelatedEntity.EntityScope == null || RelatedEntity.EntityScope == "Common") + return $"{StringConversion.ToSentenceCase(Name)} (see {CodeGenerator.ToSeeComments("Common.Entities." + Type)})"; + else + return $"{StringConversion.ToSentenceCase(Name)} (see {CodeGenerator.ToSeeComments("Business.Entities." + Type)})"; + } + + return StringConversion.ToSentenceCase(Name); + })); + + PrivateName = DefaultWhereNull(PrivateName, () => pc == null ? StringConversion.ToPrivateCase(Name) : pc.Name); + ArgumentName = DefaultWhereNull(ArgumentName, () => pc == null ? StringConversion.ToCamelCase(Name) : pc.ArgumentName); Nullable = DefaultWhereNull(Nullable, () => pc == null ? !Beef.CodeGen.CodeGenConfig.IgnoreNullableTypes.Contains(Type!) : pc.Nullable); LayerPassing = DefaultWhereNull(LayerPassing, () => "All"); RefDataList = DefaultWhereNull(RefDataList, () => pc?.RefDataList); DataConverter = DefaultWhereNull(DataConverter, () => pc?.DataConverter); DataConverterIsGeneric = DefaultWhereNull(DataConverterIsGeneric, () => pc?.DataConverterIsGeneric); + WebApiFrom = DefaultWhereNull(WebApiFrom, () => RelatedEntity == null ? "FromQuery" : "FromEntityProperties"); RefDataType = DefaultWhereNull(RefDataType, () => pc?.RefDataType); if (Type!.StartsWith("RefDataNamespace.", StringComparison.InvariantCulture)) RefDataType = DefaultWhereNull(RefDataType, () => "string"); + + GrpcType = DefaultWhereNull(GrpcType, () => PropertyConfig.InferGrpcType(string.IsNullOrEmpty(RefDataType) ? Type! : RefDataType!, RefDataType, RefDataList)); + GrpcMapper = Beef.CodeGen.CodeGenConfig.SystemTypes.Contains(Type) || RefDataType != null ? null : Type; + GrpcConverter = Type switch + { + "DateTime" => $"{(CompareValue(Nullable, true) ? "Nullable" : "")}DateTimeToTimestamp", + "Guid" => $"{(CompareValue(Nullable, true) ? "Nullable" : "")}GuidToStringConverter", + "decimal" => $"{(CompareValue(Nullable, true) ? "Nullable" : "")}DecimalToDecimalConverter", + _ => null + }; } } } \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Config/Entity/PropertyConfig.cs b/tools/Beef.CodeGen.Core/Config/Entity/PropertyConfig.cs index affe46d28..80b1ce7c9 100644 --- a/tools/Beef.CodeGen.Core/Config/Entity/PropertyConfig.cs +++ b/tools/Beef.CodeGen.Core/Config/Entity/PropertyConfig.cs @@ -22,7 +22,7 @@ namespace Beef.CodeGen.Config.Entity [CategorySchema("Annotation", Title = "Provides additional property **Annotation** configuration.")] [CategorySchema("WebApi", Title = "Provides the data **Web API** configuration.")] [CategorySchema("Grpc", Title = "Provides the **gRPC** configuration.")] - public class PropertyConfig : ConfigBase + public class PropertyConfig : ConfigBase { #region Key @@ -82,7 +82,7 @@ public class PropertyConfig : ConfigBase /// Indicates whether the .NET should be declared as nullable. /// [JsonProperty("nullable", DefaultValueHandling = DefaultValueHandling.Ignore)] - [PropertySchema("Property", Title = "Indicates whether the .NET `Type should be declared as nullable; e.g. `string?`.", IsImportant = true)] + [PropertySchema("Property", Title = "Indicates whether the .NET `Type should be declared as nullable; e.g. `string?`. Will be inferred where the `Type` is denoted as nullable; i.e. suffixed by a `?`.", IsImportant = true)] public bool? Nullable { get; set; } /// @@ -316,17 +316,17 @@ public class PropertyConfig : ConfigBase /// /// Gets or sets the Entity Framework property `Mapper` class name where `Entity.AutoImplement` is selected. /// - [JsonProperty("dataEntityFrameworkMapper", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("entityFrameworkMapper", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertySchema("EntityFramework", Title = "The Entity Framework property `Mapper` class name where `Entity.AutoImplement` is selected.", Description = "A `Mapper` is used to map a data source value to/from a .NET complex `Type` (i.e. class with one or more properties).")] - public string? DataEntityFrameworkMapper { get; set; } + public string? EntityFrameworkMapper { get; set; } /// /// Indicates whether the property should be ignored (excluded) from the Entity Framework `Mapper` generated output. /// - [JsonProperty("dataEntityFrameworkIgnore", DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonProperty("entityFrameworkIgnore", DefaultValueHandling = DefaultValueHandling.Ignore)] [PropertySchema("EntityFramework", Title = "Indicates whether the property should be ignored (excluded) from the Entity Framework `Mapper` generated output.")] - public bool? DataEntityFrameworkIgnore { get; set; } + public bool? EntityFrameworkIgnore { get; set; } #endregion @@ -407,7 +407,7 @@ public class PropertyConfig : ConfigBase /// Gets or sets the `IPropertyMapperConverter` to perform `Type` to `string` conversion for writing to and parsing from the query string. /// [JsonProperty("webApiQueryStringConverter", DefaultValueHandling = DefaultValueHandling.Ignore)] - [PropertySchema("WebApi", Title = "the `IPropertyMapperConverter` to perform `Type` to `string` conversion for writing to and parsing from the query string.")] + [PropertySchema("WebApi", Title = "The `IPropertyMapperConverter` to perform `Type` to `string` conversion for writing to and parsing from the query string.")] public string? WebApiQueryStringConverter { get; set; } #endregion @@ -421,27 +421,53 @@ public class PropertyConfig : ConfigBase [PropertySchema("Grpc", Title = "The unique (immutable) field number required to enable gRPC support.", IsImportant = true)] public int? GrpcFieldNo { get; set; } + /// + /// Gets or sets the underlying gRPC data type. + /// + [JsonProperty("grpcType", DefaultValueHandling = DefaultValueHandling.Ignore)] + [PropertySchema("Grpc", Title = "The underlying gRPC data type; will be inferred where not specified.")] + public string? GrpcType { get; set; } + #endregion /// - /// Gets or sets the formatted summary text. + /// Gets the formatted summary text. + /// + public string? SummaryText => CodeGenerator.ToComments($"{(Type == "bool" ? "Indicates whether" : "Gets or sets the")} {Text}."); + + /// + /// Gets the formatted summary text for the Reference Data Serialization Identifier (SID) property. + /// + public string? SummaryRefDataSid => CompareValue(RefDataList, true) + ? CodeGenerator.ToComments($"Gets or sets the {{{{{Name}}}}} list using the underlying Serialization Identifier (SID).") + : CodeGenerator.ToComments($"Gets or sets the {{{{{Name}}}}} using the underlying Serialization Identifier (SID)."); + + /// + /// Gets the formatted summary text for the Reference Data Text property. + /// + public string? SummaryRefDataText => $"Gets the corresponding {{{{{Name}}}}} text (read-only where selected)."; + + /// + /// Gets the formatted summary text when used in a parameter context. /// - public string? SummaryText { get; set; } + public string? ParameterSummaryText => CodeGenerator.ToComments($"{(Type == "bool" ? "Indicates whether" : "The")} {Text}."); /// - /// Gets or sets the formatted summary text for the Reference Data Serialization Identifier (SID) property. + /// Gets the formatted as see comments. /// - public string? SummaryRefDataSid { get; set; } + public string? PropertyNameSeeComments => CodeGenerator.ToSeeComments(Name); /// - /// Gets or sets the formatted summary text for the Reference Data Text property. + /// Gets the computed declared property type. /// - public string? SummaryRefDataText { get; set; } + public string PropertyType => string.IsNullOrEmpty(RefDataType) + ? PrivateType + : (CompareValue(RefDataList, true) ? $"ReferenceDataSidList<{Type}, {RefDataType}>?" : CompareValue(Nullable, true) ? Type + "?" : Type!); /// - /// Gets the computed declared type. + /// Gets the computed declared private type. /// - public string ComputedType + public string PrivateType { get { @@ -453,10 +479,55 @@ public string ComputedType } } + /// + /// Gets or sets the declared type including nullability. + /// + public string? DeclaredType { get; set; } + + /// + /// Gets the computed property name. + /// + public string PropertyName => string.IsNullOrEmpty(RefDataType) ? Name! : Name! + (CompareValue(RefDataList, true) ? "Sids" : "Sid"); + + /// + /// Gets the computed argument name. + /// + public string PropertyArgumentName => string.IsNullOrEmpty(RefDataType) ? ArgumentName! : ArgumentName! + (CompareValue(RefDataList, true) ? "Sids" : "Sid"); + /// /// Gets the computed private name. /// - public string ComputedPrivateName => string.IsNullOrEmpty(RefDataType) ? PrivateName! : PrivateName! + (CompareValue(RefDataList, true) ? "Sids" : "Sid"); + public string PropertyPrivateName => string.IsNullOrEmpty(RefDataType) ? PrivateName! : PrivateName! + (CompareValue(RefDataList, true) ? "Sids" : "Sid"); + + /// + /// Gets the computed data mapper property name. + /// + public string DataMapperPropertyName => string.IsNullOrEmpty(RefDataType) ? Name! : CompareNullOrValue(DataConverter, "ReferenceDataCodeConverter") ? PropertyName : Name!; + + /// + /// Gets the data converter C# code. + /// + public string? DataConverterCode => string.IsNullOrEmpty(DataConverter) ? null : $".SetConverter({DataConverter}{(CompareValue(DataConverterIsGeneric, true) ? $"<{Type}>" : "")}.Default!)"; + + /// + /// Gets the data converter C# code for reference data data access. + /// + public string? RefDataConverterCode => string.IsNullOrEmpty(DataConverter) ? null : $"{DataConverter}{(CompareValue(DataConverterIsGeneric, true) ? $"<{Type}>" : "")}.Default.ConvertToSrce("; + + /// + /// Gets the WebAPI parameter type. + /// + public string WebApiParameterType => (string.IsNullOrEmpty(RefDataType) ? (string.IsNullOrEmpty(WebApiQueryStringConverter) ? Type! : "string") : (CompareValue(RefDataList, true) ? $"List<{RefDataType}>" : RefDataType!)) + (CompareValue(Nullable, true) ? "?" : ""); + + /// + /// Gets or sets the gRPC converter. + /// + public string? GrpcConverter { get; set; } + + /// + /// Gets or sets the gRPC mapper. + /// + public string? GrpcMapper { get; set; } /// /// @@ -464,12 +535,42 @@ public string ComputedType protected override void Prepare() { Type = DefaultWhereNull(Type, () => "string"); - Text = DefaultWhereNull(Text, () => Type!.StartsWith("RefDataNamespace.", StringComparison.InvariantCulture) || Parent!.Parent!.Entities.Any(x => x.Name == Type) ? CodeGenerator.ToSeeComments(Name) : CodeGenerator.ToSentenceCase(Name)); - SummaryText = CodeGenerator.ToComments($"{(Type == "bool" ? "Indicates whether" : "Get or sets the")} {Text}."); - SummaryRefDataSid = CodeGenerator.ToComments($"Gets or sets the {Text} using the underlying Serialization Identifier (SID)."); - SummaryRefDataText = CodeGenerator.ToComments($"Gets the corresponding {Text} text (read-only where selected)."); - PrivateName = DefaultWhereNull(PrivateName, () => CodeGenerator.ToPrivateCase(Name)); - ArgumentName = DefaultWhereNull(ArgumentName, () => CodeGenerator.ToCamelCase(Name)); + if (Type!.StartsWith("RefDataNamespace.", StringComparison.InvariantCulture)) + RefDataType = DefaultWhereNull(RefDataType, () => "string"); + + if (RefDataType != null && !Type!.StartsWith("RefDataNamespace.", StringComparison.InvariantCulture)) + Type = $"RefDataNamespace.{Type}"; + + if (Type!.EndsWith("?", StringComparison.InvariantCulture)) + { + Type = Type[0..^1]; + Nullable = true; + } + + DeclaredType = $"{Type}{(CompareValue(Nullable, true) ? "?" : "")}"; + + Text = CodeGenerator.ToComments(DefaultWhereNull(Text, () => + { + if (Type!.StartsWith("RefDataNamespace.", StringComparison.InvariantCulture)) + return $"{StringConversion.ToSentenceCase(Name)} (see {CodeGenerator.ToSeeComments(Type)})"; + + if (Type == "ChangeLog") + return $"{StringConversion.ToSentenceCase(Name)} (see {CodeGenerator.ToSeeComments("Beef.Entities." + Type)})"; + + var ent = Root!.Entities.FirstOrDefault(x => x.Name == Type); + if (ent != null) + { + if (ent.EntityScope == null || ent.EntityScope == "Common") + return $"{StringConversion.ToSentenceCase(Name)} (see {CodeGenerator.ToSeeComments("Common.Entities." + Type)})"; + else + return $"{StringConversion.ToSentenceCase(Name)} (see {CodeGenerator.ToSeeComments("Business.Entities." + Type)})"; + } + + return StringConversion.ToSentenceCase(Name); + })); + + PrivateName = DefaultWhereNull(PrivateName, () => StringConversion.ToPrivateCase(Name)); + ArgumentName = DefaultWhereNull(ArgumentName, () => StringConversion.ToCamelCase(Name)); DateTimeTransform = DefaultWhereNull(DateTimeTransform, () => "UseDefault"); StringTrim = DefaultWhereNull(StringTrim, () => "UseDefault"); StringTransform = DefaultWhereNull(StringTransform, () => "UseDefault"); @@ -477,14 +578,32 @@ protected override void Prepare() DisplayName = DefaultWhereNull(DisplayName, () => GenerateDisplayName()); Nullable = DefaultWhereNull(Nullable, () => !Beef.CodeGen.CodeGenConfig.IgnoreNullableTypes.Contains(Type!)); JsonName = DefaultWhereNull(JsonName, () => ArgumentName); + SerializationEmitDefault = DefaultWhereNull(SerializationEmitDefault, () => CompareValue(UniqueKey, true)); DataModelJsonName = DefaultWhereNull(DataModelJsonName, () => JsonName); - DataName = DefaultWhereNull(DataName, () => Name); DataOperationTypes = DefaultWhereNull(DataOperationTypes, () => "Any"); - IsEntity = DefaultWhereNull(IsEntity, () => Parent!.Parent!.Entities!.Any(x => x.Name == Type)); + IsEntity = DefaultWhereNull(IsEntity, () => Parent!.Parent!.Entities!.Any(x => x.Name == Type) && RefDataType == null); Immutable = DefaultWhereNull(Immutable, () => false); + BubblePropertyChanged = DefaultWhereNull(BubblePropertyChanged, () => CompareValue(IsEntity, true)); - if (Type!.StartsWith("RefDataNamespace.", StringComparison.InvariantCulture)) - RefDataType = DefaultWhereNull(RefDataType, () => "string"); + DataConverter = DefaultWhereNull(DataConverter, () => string.IsNullOrEmpty(RefDataType) ? null : Root!.RefDataDefaultMapperConverter); + if (!string.IsNullOrEmpty(DataConverter) && (DataConverter.EndsWith("{T}", StringComparison.InvariantCulture) || DataConverter.EndsWith("", StringComparison.InvariantCulture))) + { + DataConverterIsGeneric = true; + DataConverter = DataConverter![0..^3]; + } + + if (CompareValue(RefDataType, "string") && CompareValue(DataConverter, "ReferenceDataCodeConverter")) + DataConverter = null; + + GrpcType = DefaultWhereNull(GrpcType, () => InferGrpcType(string.IsNullOrEmpty(RefDataType) ? Type! : RefDataType!, RefDataType, RefDataList, DateTimeTransform)); + GrpcMapper = Beef.CodeGen.CodeGenConfig.SystemTypes.Contains(Type) || RefDataType != null ? null : Type; + GrpcConverter = Type switch + { + "DateTime" => $"{(CompareValue(Nullable, true) ? "Nullable" : "")}{(DateTimeTransform == "DateOnly" ? "DateTimeToDateOnly" : "DateTimeToTimestamp")}", + "Guid" => $"{(CompareValue(Nullable, true) ? "Nullable" : "")}GuidToStringConverter", + "decimal" => $"{(CompareValue(Nullable, true) ? "Nullable" : "")}DecimalToDecimalConverter", + _ => null + }; } /// @@ -492,7 +611,7 @@ protected override void Prepare() /// private string GenerateDisplayName() { - var dn = CodeGenerator.ToSentenceCase(Name)!; + var dn = StringConversion.ToSentenceCase(Name)!; var parts = dn.Split(' '); if (parts.Length == 1) return (parts[0] == "Id") ? "Identifier" : dn; @@ -504,5 +623,34 @@ private string GenerateDisplayName() Array.Copy(parts, parts2, parts.Length - 1); return string.Join(" ", parts2); } + + /// + /// Infers the gRPC data type. + /// + internal static string InferGrpcType(string type, string? refDataType = null, bool? refDataList = null, string? dateTimeTransform = null) + { + var gt = type switch + { + "string" => "google.protobuf.StringValue", + "bool" => "google.protobuf.BoolValue", + "double" => "google.protobuf.DoubleValue", + "float" => "google.protobuf.FloatValue", + "int" => "google.protobuf.Int32Value", + "long" => "google.protobuf.Int64Value", + "unit" => "google.protobuf.UInt32Value", + "ulong" => "google.protobuf.UInt64Value", + "short" => "google.protobuf.Int32Value", // Not natively supported + "ushort" => "google.protobuf.UInt32Value", // Not natively supported + "Guid" => "google.protobuf.StringValue", // Not natively supported + "byte[]" => "bytes", // Not natively supported + "Decimal" => "Decimal", // Not natively supported + "DateTime" => string.Compare(dateTimeTransform, "DateOnly", StringComparison.InvariantCulture) == 0 ? "DateOnly" : "google.protobuf.Timestamp", // DateOnly not natively supported + "TimeSpan" => "google.protobuf.Duration", + "void" => "google.protobuf.Empty", + _ => type + }; + + return !string.IsNullOrEmpty(refDataType) && CompareValue(refDataList, true) ? "repeated " + gt : gt; + } } } \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Config/Entity/RefDataConfig.cs b/tools/Beef.CodeGen.Core/Config/Entity/RefDataConfig.cs new file mode 100644 index 000000000..6dbfe6fc1 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Config/Entity/RefDataConfig.cs @@ -0,0 +1,65 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Config.Entity +{ + /// + /// Provides Reference Data specific properties. + /// + public class RefDataConfig : ConfigBase + { + /// + /// Indicates whether auto-implementing 'Database'. + /// + public bool UsesDatabase => Parent!.RefDataEntities.Any(x => x.AutoImplement == "Database"); + + /// + /// Indicates whether auto-implementing 'EntityFramework'. + /// + public bool UsesEntityFramework => Parent!.RefDataEntities.Any(x => x.AutoImplement == "EntityFramework"); + + /// + /// Indicates whether auto-implementing 'Cosmos'. + /// + public bool UsesCosmos => Parent!.RefDataEntities.Any(x => x.AutoImplement == "Cosmos"); + + /// + /// Indicates whether auto-implementing 'OData'. + /// + public bool UsesOData => Parent!.RefDataEntities.Any(x => x.AutoImplement == "OData"); + + /// + /// Gets the Data constructor parameters. + /// + public List DataConstructorParameters { get; } = new List(); + + /// + /// + /// + protected override void Prepare() + { + var oc = new OperationConfig(); + oc.Prepare(Root!, new EntityConfig { Name = "RefData" }); + + // Data constructors. + if (UsesDatabase) + DataConstructorParameters.Add(new ParameterConfig { Name = "Db", Type = Root!.DatabaseName, Text = $"{{{{{Root!.DatabaseName}}}}}" }); + + if (UsesEntityFramework) + DataConstructorParameters.Add(new ParameterConfig { Name = "Ef", Type = Root!.EntityFrameworkName, Text = $"{{{{{Root!.EntityFrameworkName}}}}}" }); + + if (UsesCosmos) + DataConstructorParameters.Add(new ParameterConfig { Name = "Cosmos", Type = Root!.CosmosName, Text = $"{{{{{Root!.CosmosName}}}}}" }); + + if (UsesOData) + DataConstructorParameters.Add(new ParameterConfig { Name = "OData", Type = Root!.ODataName, Text = $"{{{{{Root!.ODataName}}}}}" }); + + foreach (var ctor in DataConstructorParameters) + { + ctor.Prepare(Root!, oc); + } + } + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Config/XmlJsonRename.cs b/tools/Beef.CodeGen.Core/Config/XmlJsonRename.cs index 9c5665887..cc88ada34 100644 --- a/tools/Beef.CodeGen.Core/Config/XmlJsonRename.cs +++ b/tools/Beef.CodeGen.Core/Config/XmlJsonRename.cs @@ -1,7 +1,9 @@ // Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef +using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http.Headers; namespace Beef.CodeGen.Config { @@ -10,13 +12,15 @@ namespace Beef.CodeGen.Config /// public static class XmlJsonRename { - private static readonly List<(ConfigurationEntity Entity, string XmlName, string JsonName)> config = new List<(ConfigurationEntity, string, string)>(new (ConfigurationEntity, string, string)[] + private static readonly List<(ConfigurationEntity Entity, string XmlName, string JsonName)> _config = new List<(ConfigurationEntity, string, string)>(new (ConfigurationEntity, string, string)[] { - (ConfigurationEntity.CodeGen, "WebApiRoutePrefix", "refDataWebApiRoutePrefix"), (ConfigurationEntity.CodeGen, "AppendToNamespace", "refDataAppendToNamespace"), (ConfigurationEntity.CodeGen, "MapperDefaultRefDataConverter", "refDataDefaultMapperConverter"), (ConfigurationEntity.Entity, "AutoInferImplements", "implementsAutoInfer"), + (ConfigurationEntity.Entity, "EntityFrameworkEntity", "entityFrameworkModel"), + (ConfigurationEntity.Entity, "CosmosEntity", "cosmosModel"), + (ConfigurationEntity.Entity, "ODataEntity", "odataModel"), (ConfigurationEntity.Entity, "DataDatabaseMapperInheritsFrom", "databaseMapperInheritsFrom"), (ConfigurationEntity.Entity, "DataDatabaseCustomMapper", "databaseCustomMapper"), (ConfigurationEntity.Entity, "DataEntityFrameworkMapperInheritsFrom", "entityFrameworkMapperInheritsFrom"), @@ -26,7 +30,7 @@ public static class XmlJsonRename (ConfigurationEntity.Entity, "DataCosmosCustomMapper", "cosmosCustomMapper"), (ConfigurationEntity.Entity, "DataODataMapperInheritsFrom", "odataMapperInheritsFrom"), (ConfigurationEntity.Entity, "DataODataCustomMapper", "odataCustomMapper"), - + (ConfigurationEntity.Property, "IgnoreSerialization", "serializationIgnore"), (ConfigurationEntity.Property, "EmitDefaultValue", "serializationEmitDefault"), (ConfigurationEntity.Property, "IsDataConverterGeneric", "dataConverterIsGeneric"), @@ -40,11 +44,17 @@ public static class XmlJsonRename (ConfigurationEntity.Property, "DataODataIgnore", "odataIgnore"), (ConfigurationEntity.Operation, "OperationType", "type"), + (ConfigurationEntity.Operation, "PagingArgs", "paging"), (ConfigurationEntity.Operation, "DataCosmosContainerId", "cosmosContainerId"), (ConfigurationEntity.Operation, "DataCosmosPartitionKey", "cosmosPartitionKey"), - (ConfigurationEntity.Parameter, "IsDataConverterGeneric", "dataConverterIsGeneric") + (ConfigurationEntity.Parameter, "IsDataConverterGeneric", "dataConverterIsGeneric"), + (ConfigurationEntity.Parameter, "ValidatorFluent", "validatorCode") + }); + private static readonly List<(ConfigurationEntity Entity, string XmlName, Func Converter)> _xmlToJsonConvert = new List<(ConfigurationEntity, string, Func)>(new (ConfigurationEntity, string, Func)[] + { + (ConfigurationEntity.Entity, "ExcludeData", (xml) => string.IsNullOrEmpty(xml) ? null : (xml == "true" ? "Yes" : "Mapper")) }); /// @@ -52,8 +62,8 @@ public static class XmlJsonRename /// public static string GetJsonName(ConfigurationEntity entity, string xmlName) { - var item = config.FirstOrDefault(x => x.Entity == entity && x.XmlName == xmlName); - return item.JsonName ?? (Beef.CodeGen.CodeGenerator.ToCamelCase(xmlName)!); + var item = _config.FirstOrDefault(x => x.Entity == entity && x.XmlName == xmlName); + return item.JsonName ?? (StringConversion.ToCamelCase(xmlName)!); } /// @@ -61,8 +71,17 @@ public static string GetJsonName(ConfigurationEntity entity, string xmlName) /// public static string GetXmlName(ConfigurationEntity entity, string jsonName) { - var item = config.FirstOrDefault(x => x.Entity == entity && x.JsonName == jsonName); - return item.XmlName ?? (Beef.CodeGen.CodeGenerator.ToPascalCase(jsonName)!); + var item = _config.FirstOrDefault(x => x.Entity == entity && x.JsonName == jsonName); + return item.XmlName ?? (StringConversion.ToPascalCase(jsonName)!); + } + + /// + /// Gets the JSON value from the XML value. + /// + public static string? GetJsonValue(ConfigurationEntity entity, string xmlName, string? xmlValue) + { + var item = _xmlToJsonConvert.FirstOrDefault(x => x.Entity == entity && x.XmlName == xmlName); + return item.Converter == null ? xmlValue : item.Converter(xmlValue); } } diff --git a/tools/Beef.CodeGen.Core/Converters/JsonOrYamlToXmlConverter.cs b/tools/Beef.CodeGen.Core/Converters/JsonOrYamlToXmlConverter.cs new file mode 100644 index 000000000..ff1a4c4ae --- /dev/null +++ b/tools/Beef.CodeGen.Core/Converters/JsonOrYamlToXmlConverter.cs @@ -0,0 +1,135 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using YamlDotNet.Serialization; + +namespace Beef.CodeGen.Converters +{ + /// + /// Converts either YAML or JSON into the equivalent XML format. The XML is intended for internal use as the likes of formatting and comments are discarded. + /// + /// This is considered temporary until the need for the XML version is no longer required. + public static class JsonOrYamlToXmlConverter + { + /// + /// Converts the YAML . + /// + /// The YAML . + /// The . + public static XDocument ConvertYaml(string yaml) + { + using var sr = new StringReader(yaml); + return ConvertYaml(sr); + } + + /// + /// Converts the YAML . + /// + /// The YAML . + /// The . + public static XDocument ConvertYaml(Stream s) + { + using var sr = new StreamReader(s); + return ConvertYaml(sr); + } + + /// + /// Converts the YAML . + /// + /// The YAML . + /// The . + public static XDocument ConvertYaml(TextReader tr) + { + var yaml = new DeserializerBuilder().Build().Deserialize(tr); + var json = new SerializerBuilder().JsonCompatible().Build().Serialize(yaml!); + return ConvertJson(json); + } + + /// + /// Converts the JSON . + /// + /// The JSON . + /// The . + public static XDocument ConvertJson(string json) + { + using var sr = new StringReader(json); + return ConvertJson(sr); + } + + /// + /// Converts the JSON . + /// + /// The YAML . + /// The . + public static XDocument ConvertJson(Stream s) + { + using var sr = new StreamReader(s); + return ConvertJson(sr); + } + + /// + /// Converts the JSON . + /// + /// The YAML . + /// The . + public static XDocument ConvertJson(TextReader tr) + { + using var jr = new JsonTextReader(tr); + var jo = JObject.Load(jr); + + var xdoc = new XDocument(new XElement("CodeGeneration")); + var (entity, name) = GetConfigInfo(null); + ConvertJson(entity, xdoc.Root, jo); + return xdoc; + } + + /// + /// Converts the JSON into the XML equivalent. + /// + private static void ConvertJson(ConfigurationEntity entity, XElement xml, JObject jo) + { + foreach (var jp in jo.Children().OfType()) + { + if (jp.Value.Type == JTokenType.Array) + { + var ci = GetConfigInfo(jp.Name); + if (ci.entity == ConfigurationEntity.None) + continue; + + var ja = jp.Value as JArray; + foreach (var ji in ja!) + { + if (ji.Type == JTokenType.Object) + { + var xe = new XElement(ci.name); + ConvertJson(ci.entity, xe, (JObject)ji); + xml.Add(xe); + } + } + } + else + xml.Add(new XAttribute(XmlJsonRename.GetXmlName(entity, jp.Name), jp.Value)); + } + } + + + /// + /// Gets the configuration for a given JSON array property name. + /// + private static (ConfigurationEntity entity, string name) GetConfigInfo(string? name) => name switch + { + null => (ConfigurationEntity.CodeGen, "CodeGeneration"), + "entities" => (ConfigurationEntity.Entity, "Entity"), + "properties" => (ConfigurationEntity.Property, "Property"), + "operations" => (ConfigurationEntity.Operation, "Operation"), + "parameters" => (ConfigurationEntity.Parameter, "Parameter"), + "consts" => (ConfigurationEntity.Const, "Const"), + _ => (ConfigurationEntity.None, ""), + }; + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Converters/XmlToYamlConverter.cs b/tools/Beef.CodeGen.Core/Converters/XmlToYamlConverter.cs new file mode 100644 index 000000000..62c1494fa --- /dev/null +++ b/tools/Beef.CodeGen.Core/Converters/XmlToYamlConverter.cs @@ -0,0 +1,192 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config; +using Beef.CodeGen.Config.Entity; +using Newtonsoft.Json; +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Xml.Linq; + +namespace Beef.CodeGen.Converters +{ + /// + /// Provides the YAML format arguments. + /// + internal class YamlFormatArgs + { + /// + /// Initializes a new instance of the . + /// + /// The . + public YamlFormatArgs(StringWriter writer) => Writer = Check.NotNull(writer, nameof(writer)); + + /// + /// Gets the . + /// + public StringWriter Writer { get; private set; } + + /// + /// Gets or sets the number of indent characters. + /// + public int Indent { get; set; } = -2; + } + + /// + /// XML to YAML converter. Generates an opinionated terse YAML format. + /// + public static class XmlToYamlConverter + { + /// + /// Converts an existing Entity XML document into the equivlent new YAML format. + /// + /// The existing . + /// The new YAML formatted . + public static string ConvertEntityXmlToYaml(XDocument xml) + { + if (xml == null) + throw new ArgumentNullException(nameof(xml)); + + if (xml.Root.Name.LocalName != "CodeGeneration") + throw new ArgumentException("Root element must be named 'CodeGeneration'.", nameof(xml)); + + var sb = new StringBuilder(); + using (var sw = new StringWriter(sb)) + { + var (entity, type, _) = GetConfigInfo(xml.Root.Name.LocalName); + WriteElement(new YamlFormatArgs(sw), xml.Root, entity, type); + } + + return sb.ToString(); + } + + /// + /// Gets the configuration for a given XML element. + /// + private static (ConfigurationEntity entity, Type type, string name) GetConfigInfo(string? name) => name switch + { + "CodeGeneration" => (ConfigurationEntity.CodeGen, typeof(Config.Entity.CodeGenConfig), ""), + "Entity" => (ConfigurationEntity.Entity, typeof(EntityConfig), "entities"), + "Property" => (ConfigurationEntity.Property, typeof(PropertyConfig), "properties"), + "Operation" => (ConfigurationEntity.Operation, typeof(OperationConfig), "operations"), + "Parameter" => (ConfigurationEntity.Parameter, typeof(ParameterConfig), "parameters"), + "Const" => (ConfigurationEntity.Const, typeof(ConstConfig), "consts"), + _ => (ConfigurationEntity.None, typeof(object), ""), + }; + + /// + /// Writes the XML element as YAML. + /// + private static void WriteElement(YamlFormatArgs args, XElement xml, ConfigurationEntity entity, Type type) + { + WriteComments(args, xml); + + if (args.Indent == 0) + args.Writer.Write("- { "); + else if (args.Indent > 0) + args.Writer.Write($"{new string(' ', args.Indent)} {{ "); + + args.Indent += 2; + + WriteAttributes(args, entity, type, xml); + + // Group by element name, then process as a collection. + foreach (var grp in xml.Elements().GroupBy(x => x.Name).Select(g => new { g.Key, Children = xml.Elements(g.Key) })) + { + var info = GetConfigInfo(grp.Key.LocalName); + if (info.entity == ConfigurationEntity.None) + return; + + if (args.Indent > 0) + args.Writer.Write(", "); + + args.Writer.Write($"{info.name}:"); + if (args.Indent > 0) + args.Writer.Write(" ["); + + args.Writer.WriteLine(); + int i = 0; + + foreach (var child in grp.Children) + { + if (i++ > 0) + { + args.Writer.Write(" }"); + if (args.Indent > 0) + args.Writer.Write(","); + + args.Writer.WriteLine(); + } + + if (args.Indent == 0 && !xml.Equals(xml.Document.Root.Elements().First())) + args.Writer.WriteLine(); + + WriteElement(args, child, info.entity, info.type); + } + + args.Writer.WriteLine(" }"); + if (args.Indent > 0) + args.Writer.Write($"{new string(' ', args.Indent)}]"); + } + + args.Indent -= 2; + } + + /// + /// Writes the XML comments as YAML. + /// + private static void WriteComments(YamlFormatArgs args, XElement xml) + { + if (xml.PreviousNode != null && xml.PreviousNode.NodeType == System.Xml.XmlNodeType.Comment) + { + using var sr = new StringReader(((XComment)xml.PreviousNode).Value); + while (true) + { + var line = sr.ReadLine(); + if (line == null) + break; + else + args.Writer.WriteLine($"{new string(' ', args.Indent + 2)}# {line.Trim()}"); + } + } + } + + /// + /// Writes the XML attribues as YAML. + /// + private static void WriteAttributes(YamlFormatArgs args, ConfigurationEntity ce, Type type, XElement xml) + { + var needsComma = false; + + foreach (var att in xml.Attributes()) + { + var jname = XmlJsonRename.GetJsonName(ce, att.Name.LocalName); + var pi = type.GetProperty(StringConversion.ToPascalCase(jname)!); + if (pi == null || pi.GetCustomAttribute() == null) + continue; + + if (needsComma) + args.Writer.Write(", "); + + var val = XmlJsonRename.GetJsonValue(ce, att.Name.LocalName, att.Value); + if (val == null) + continue; + + if (val.IndexOfAny(new char[] { ':', '{', '}', '[', ']', ',', '&', '*', '#', '?', '|', '-', '<', '>', '=', '!', '%', '@', '\\', '\"' }) >= 0) + val = $"'{val.Replace("'", "''", StringComparison.InvariantCultureIgnoreCase)}'"; + + if (string.Compare(val, "NULL", StringComparison.InvariantCultureIgnoreCase) == 0) + val = $"'{val}'"; + + args.Writer.Write($"{jname}: {val}"); + + if (args.Indent > 0) + needsComma = true; + else + args.Writer.WriteLine(); + } + } + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Entities/Table.cs b/tools/Beef.CodeGen.Core/Entities/Table.cs index 409e27700..2b75ac11d 100644 --- a/tools/Beef.CodeGen.Core/Entities/Table.cs +++ b/tools/Beef.CodeGen.Core/Entities/Table.cs @@ -161,7 +161,7 @@ public static string CreateAlias(string name) throw new ArgumentNullException(nameof(name)); #pragma warning disable CA1308 // Normalize strings to uppercase; by-design, a lowercase is required. - return new string(Beef.CodeGen.CodeGenerator.ToSentenceCase(name)!.Split(' ').Select(x => x.Substring(0, 1).ToLower(System.Globalization.CultureInfo.InvariantCulture).ToCharArray()[0]).ToArray()); + return new string(StringConversion.ToSentenceCase(name)!.Split(' ').Select(x => x.Substring(0, 1).ToLower(System.Globalization.CultureInfo.InvariantCulture).ToCharArray()[0]).ToArray()); #pragma warning restore CA1308 } diff --git a/tools/Beef.CodeGen.Core/Generators/CodeGeneratorBase.cs b/tools/Beef.CodeGen.Core/Generators/CodeGeneratorBase.cs new file mode 100644 index 000000000..b91e756c4 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/CodeGeneratorBase.cs @@ -0,0 +1,125 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config; +using HandlebarsDotNet; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Provides the base code-generation capabilities leveraging . + /// + public abstract class CodeGeneratorBase + { + /// + /// Static constructor. + /// + static CodeGeneratorBase() => HandlebarsHelpers.RegisterHelpers(); + + /// + /// Check whether the nullable boolean is true. + /// + protected static bool IsTrue(bool? value) => value.HasValue && value.Value; + + /// + /// Check whether the nullable boolean is null or false. + /// + protected static bool IsFalse(bool? value) => !value.HasValue || !value.Value; + + /// + /// Gets or sets the output file name. + /// + public string? OutputFileName { get; set; } + + /// + /// Gets or sets the output directory name. + /// + public string? OutputDirName { get; set; } + + /// + /// Gets or sets the output generated directory name (defaults to Generated). + /// + public string? OutputGenDirName { get; set; } = "Generated"; + + /// + /// Performs the code generation. + /// + /// The template handlebars.js contents. + /// The root . + /// The action that is invoked when a output has been successfully generated. + public abstract void Generate(string templateContents, ConfigBase config, Action codeGenerated); + } + + /// + /// Provides the base code-generation capabilities leveraging . + /// + /// The root . + /// The code-generation required by the underlying template. + public abstract class CodeGeneratorBase : CodeGeneratorBase where TRootConfig : ConfigBase where TGenConfig : ConfigBase + { + /// + /// Selects (filters) the to access the underlying . + /// + /// The root configuration. + /// The selected . + protected abstract IEnumerable SelectGenConfig(TRootConfig config); + + /// + /// Performs the code generation. + /// + /// The template handlebars.js contents. + /// The root . + /// The action that is invoked when a output has been successfully generated. + public override void Generate(string templateContents, ConfigBase config, Action codeGenerated) => Generate(templateContents, (TRootConfig)config, codeGenerated); + + /// + /// Performs the code generation. + /// + /// The template handlebars.js contents. + /// The root . + /// The action that is invoked when a output has been successfully generated. + public void Generate(string template, TRootConfig config, Action codeGenerated) + { + if (string.IsNullOrEmpty(template)) + throw new ArgumentNullException(nameof(template)); + + var values = SelectGenConfig(config); + if (!values.Any()) + return; + + var templateHandlebars = Handlebars.Compile(template); + var outputFileNameHandlebars = Handlebars.Compile(OutputFileName ?? throw new InvalidOperationException($"The '{nameof(OutputFileName)}' must not be null.")); + var outputDirNameHandlebars = Handlebars.Compile(OutputDirName ?? throw new InvalidOperationException($"The '{nameof(OutputDirName)}' must not be null.")); + var outputGenDirNameGenHandlebars = Handlebars.Compile(OutputGenDirName ?? throw new InvalidOperationException($"The '{nameof(OutputGenDirName)}' must not be null.")); + + foreach (var val in values) + { + var args = new CodeGeneratorEventArgs + { + Content = templateHandlebars(val), + OutputFileName = outputFileNameHandlebars(val), + OutputDirName = outputDirNameHandlebars(val), + OutputGenDirName = outputGenDirNameGenHandlebars(val) + }; + + codeGenerated?.Invoke(args); + } + } + } + + /// + /// Provides the base code-generation capabilities leveraging for where the root and code-gen are the same. + /// + /// The root . + public abstract class CodeGeneratorBase : CodeGeneratorBase where TRootConfig : ConfigBase + { + /// + /// The selection is the itself. + /// + /// The root configuration. + /// The root configuration. + protected override IEnumerable SelectGenConfig(TRootConfig config) => new TRootConfig[] { config }; + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/EntityCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/EntityCodeGenerator.cs new file mode 100644 index 000000000..643e4b247 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/EntityCodeGenerator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config.Entity; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the primary entity code generator; where not excluded, within the selected scope, and where not omitting entity base capabilities. + /// + public class EntityCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Entities.Where(x => IsFalse(x.ExcludeEntity) && x.EntityScope == x.Root!.EntityScope && IsFalse(x.OmitEntityBase)).AsEnumerable(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/EntityDataCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/EntityDataCodeGenerator.cs new file mode 100644 index 000000000..901dee468 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/EntityDataCodeGenerator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config.Entity; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the EntityData code generator; where not excluded and at least one operation exists. + /// + public class EntityDataCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Entities.Where(x => x.ExcludeData == "Mapper" || (x.ExcludeData == "No" && x.Operations!.Count > 0)).AsEnumerable(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/EntityDataSvcCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/EntityDataSvcCodeGenerator.cs new file mode 100644 index 000000000..a19e1f6db --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/EntityDataSvcCodeGenerator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config.Entity; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the EntityDataSvc code generator; where not excluded and at least one operation exists. + /// + public class EntityDataSvcCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Entities.Where(x => IsFalse(x.ExcludeDataSvc) && x.Operations!.Count > 0).AsEnumerable(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/EntityGrpcAgentCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/EntityGrpcAgentCodeGenerator.cs new file mode 100644 index 000000000..a083dc7de --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/EntityGrpcAgentCodeGenerator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config.Entity; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the Grpc EntityAgent code generator; where not excluded and at least one operation exists. + /// + public class EntityGrpcAgentCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Entities.Where(x => IsTrue(x.Grpc) && x.GrpcOperations!.Count > 0).AsEnumerable(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/EntityGrpcProtoCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/EntityGrpcProtoCodeGenerator.cs new file mode 100644 index 000000000..666a91085 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/EntityGrpcProtoCodeGenerator.cs @@ -0,0 +1,20 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using System.Collections.Generic; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the primary gRPC Proto code generator. + /// + public class EntityGrpcProtoCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Grpc.HasValue && Check.NotNull(config, nameof(config)).Grpc!.Value ? new Config.Entity.CodeGenConfig[] { config } : System.Array.Empty(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/EntityGrpcServiceCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/EntityGrpcServiceCodeGenerator.cs new file mode 100644 index 000000000..77be5aebd --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/EntityGrpcServiceCodeGenerator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config.Entity; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the Grpc EntityService code generator; where not excluded and at least one operation exists. + /// + public class EntityGrpcServiceCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Entities.Where(x => IsTrue(x.Grpc) && x.GrpcOperations!.Count > 0).AsEnumerable(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/EntityManagerCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/EntityManagerCodeGenerator.cs new file mode 100644 index 000000000..671928110 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/EntityManagerCodeGenerator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config.Entity; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the EntityManager code generator; where not excluded and at least one operation exists. + /// + public class EntityManagerCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Entities.Where(x => IsFalse(x.ExcludeManager) && x.Operations!.Count > 0).AsEnumerable(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/EntityModelCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/EntityModelCodeGenerator.cs new file mode 100644 index 000000000..299003cff --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/EntityModelCodeGenerator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config.Entity; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the entity model code generator; where the is true. + /// + public class EntityModelCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Entities.Where(x => IsTrue(x.DataModel)).AsEnumerable(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/EntityOmitBaseCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/EntityOmitBaseCodeGenerator.cs new file mode 100644 index 000000000..e904d91a7 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/EntityOmitBaseCodeGenerator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config.Entity; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the omit base entity code generator; where the is true. + /// + public class EntityOmitBaseCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Entities.Where(x => IsTrue(x.OmitEntityBase) && x.EntityScope == x.Root!.EntityScope).AsEnumerable(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/EntityWebApiAgentCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/EntityWebApiAgentCodeGenerator.cs new file mode 100644 index 000000000..0898df2ef --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/EntityWebApiAgentCodeGenerator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config.Entity; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the WebAPI AgentController code generator; where not excluded and at least one operation exists. + /// + public class EntityWebApiAgentCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Entities.Where(x => IsFalse(x.ExcludeWebApiAgent) && x.Operations!.Count > 0).AsEnumerable(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/EntityWebApiControllerCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/EntityWebApiControllerCodeGenerator.cs new file mode 100644 index 000000000..190b77c57 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/EntityWebApiControllerCodeGenerator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config.Entity; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the WebAPI EntityController code generator; where not excluded and at least one operation exists. + /// + public class EntityWebApiControllerCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Entities.Where(x => IsFalse(x.ExcludeWebApi) && x.Operations!.Count > 0).AsEnumerable(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/HandlebarsHelpers.cs b/tools/Beef.CodeGen.Core/Generators/HandlebarsHelpers.cs new file mode 100644 index 000000000..176de0ae4 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/HandlebarsHelpers.cs @@ -0,0 +1,212 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using HandlebarsDotNet; +using System.Collections; +using System.Globalization; +using System.IO; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Provides the Handlebars.Net capability. + /// + public static class HandlebarsHelpers + { + private static bool _areRegiestered = false; + + /// + /// Registers all of the required Handlebars helpers. + /// + public static void RegisterHelpers() + { + if (_areRegiestered) + return; + + _areRegiestered = true; + + // Will check that the first argument equals at least one of the subsequent arguments. + Handlebars.RegisterHelper("ifeq", (writer, context, parameters, args) => + { + if (!CheckArgs("ifeq", writer, args)) + return; + + if (IfEq(args)) + context.Template(writer, parameters); + else + context.Inverse(writer, parameters); + }); + + // Will check that the first argument does not equal any of the subsequent arguments. + Handlebars.RegisterHelper("ifne", (writer, context, parameters, args) => + { + if (!CheckArgs("ifne", writer, args)) + return; + + if (IfEq(args)) + context.Inverse(writer, parameters); + else + context.Template(writer, parameters); + }); + + // Will check that all of the arguments have a non-null value. + Handlebars.RegisterHelper("ifval", (writer, context, parameters, args) => + { + if (!CheckArgs("ifval", writer, args)) + return; + + foreach (var arg in args) + { + if (arg == null) + { + context.Inverse(writer, parameters); + return; + } + } + + context.Template(writer, parameters); + }); + + // Will check that all of the arguments have a null value. + Handlebars.RegisterHelper("ifnull", (writer, context, parameters, args) => + { + if (!CheckArgs("ifnull", writer, args)) + return; + + foreach (var arg in args) + { + if (arg != null) + { + context.Inverse(writer, parameters); + return; + } + } + + context.Template(writer, parameters); + }); + + // Will check that all of the arguments have a true value where bool; otherwise, non-null. + Handlebars.RegisterHelper("ifor", (writer, context, parameters, args) => + { + if (!CheckArgs("ifor", writer, args)) + return; + + foreach (var arg in args) + { + if (arg is bool opt) + { + if (opt) + { + context.Template(writer, parameters); + return; + } + } + else if (arg != null) + { + context.Template(writer, parameters); + return; + } + } + + context.Inverse(writer, parameters); + }); + + + // Converts a value to lowercase. +#pragma warning disable CA1308 // Normalize strings to uppercase; this is an explicit and required call to lowercase. + Handlebars.RegisterHelper("lower", (writer, context, parameters) => writer.WriteSafeString(parameters.FirstOrDefault()?.ToString()?.ToLowerInvariant() ?? "")); +#pragma warning restore CA1308 + + // Converts a value to camelcase. + Handlebars.RegisterHelper("camel", (writer, context, parameters) => writer.WriteSafeString(StringConversion.ToCamelCase(parameters.FirstOrDefault()?.ToString()) ?? "")); + + // Converts a value to pascalcase. + Handlebars.RegisterHelper("pascal", (writer, context, parameters) => writer.WriteSafeString(StringConversion.ToPascalCase(parameters.FirstOrDefault()?.ToString()) ?? "")); + + // Converts a value to the c# '' comments equivalent. + Handlebars.RegisterHelper("seecomments", (writer, context, parameters) => writer.WriteSafeString(Beef.CodeGen.CodeGenerator.ToSeeComments(parameters.FirstOrDefault()?.ToString()))); + + // Adds a value to a value. + Handlebars.RegisterHelper("add", (writer, context, parameters) => + { + int sum = 0; + foreach (var p in parameters) + { + if (p is int pi) + sum += pi; + else if (p is string ps) + sum += int.Parse(ps, NumberStyles.Integer, CultureInfo.InvariantCulture); + else + writer.WriteSafeString("!!! add with invalid integer !!!"); + } + + writer.WriteSafeString(sum); + }); + } + + /// + /// Perform the actual IfEq equality check. + /// + private static bool IfEq(object[] args) + { + bool func() + { + for (int i = 1; i < args.Length; i++) + { + if (Comparer.Default.Compare(args[0], RValConvert(args[0], args[i])) == 0) + return true; + } + + return false; + } + + return args.Length switch + { + 0 => true, + 1 => args[0] == null, + 2 => Comparer.Default.Compare(args[0], RValConvert(args[0], args[1])) == 0, + _ => func() + }; + } + + /// + /// Converts the rval to match the lval type. + /// + private static object? RValConvert(object lval, object rval) + { + if (lval == null || rval == null) + return rval; + + var type = lval.GetType(); + if (type == typeof(string)) + return rval; + else if (type == typeof(bool)) + return bool.Parse(rval.ToString()!); + else + return int.Parse(rval.ToString()!, CultureInfo.InvariantCulture); + } + + /// + /// Check the arguments to validate for correctness. + /// + private static bool CheckArgs(string name, TextWriter writer, object[] args, int max = int.MaxValue) + { + if (args.Length > max) + { + writer.WriteLine($"!!! {name}: {args.Length} arguments passed where max of {max} expected !!!"); + return false; + } + + for (int i = 0; i < args.Length; i++) + { + if (args[i] != null && args[i].GetType().Name == "UndefinedBindingResult") + { + writer.WriteLine($"!!! {name}: Arg[{i}] == UndefinedBindingResult !!!"); + return false; + } + } + + return true; + } + } +} diff --git a/tools/Beef.CodeGen.Core/Generators/IEntityDataCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/IEntityDataCodeGenerator.cs new file mode 100644 index 000000000..0fd7a9393 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/IEntityDataCodeGenerator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config.Entity; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the IEntityData code generator; where not excluded and at least one operation exists. + /// + public class IEntityDataCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Entities.Where(x => IsFalse(x.ExcludeIData) && x.Operations!.Count > 0).AsEnumerable(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/IEntityDataSvcCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/IEntityDataSvcCodeGenerator.cs new file mode 100644 index 000000000..46dd88e5f --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/IEntityDataSvcCodeGenerator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config.Entity; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the IEntityDataSvc code generator; where not excluded and at least one operation exists. + /// + public class IEntityDataSvcCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Entities.Where(x => IsFalse(x.ExcludeIDataSvc) && x.Operations!.Count > 0).AsEnumerable(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/IEntityManagerCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/IEntityManagerCodeGenerator.cs new file mode 100644 index 000000000..c7f9e3894 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/IEntityManagerCodeGenerator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config.Entity; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the IEntityManager code generator; where not excluded and at least one operation exists. + /// + public class IEntityManagerCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Entities.Where(x => IsFalse(x.ExcludeIManager) && x.Operations!.Count > 0).AsEnumerable(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/JsonSchemaGenerator.cs b/tools/Beef.CodeGen.Core/Generators/JsonSchemaGenerator.cs index 4f1c4377a..5f4833ac7 100644 --- a/tools/Beef.CodeGen.Core/Generators/JsonSchemaGenerator.cs +++ b/tools/Beef.CodeGen.Core/Generators/JsonSchemaGenerator.cs @@ -60,7 +60,7 @@ private static void WriteObject(Type type, JsonTextWriter jtw, Action? additiona if (jpa == null) continue; - var name = jpa.PropertyName ?? Beef.CodeGen.CodeGenerator.ToCamelCase(pi.Name)!; + var name = jpa.PropertyName ??StringConversion.ToCamelCase(pi.Name)!; jtw.WritePropertyName(name); jtw.WriteStartObject(); @@ -70,7 +70,7 @@ private static void WriteObject(Type type, JsonTextWriter jtw, Action? additiona jtw.WritePropertyName("type"); jtw.WriteValue(GetJsonType(pi)); jtw.WritePropertyName("title"); - jtw.WriteValue(psa.Title ?? Beef.CodeGen.CodeGenerator.ToSentenceCase(name)!); + jtw.WriteValue(psa.Title ??StringConversion.ToSentenceCase(name)!); if (psa.Description != null) { jtw.WritePropertyName("description"); @@ -100,7 +100,7 @@ private static void WriteObject(Type type, JsonTextWriter jtw, Action? additiona jtw.WritePropertyName("type"); jtw.WriteValue("array"); jtw.WritePropertyName("title"); - jtw.WriteValue(pcsa.Title ?? Beef.CodeGen.CodeGenerator.ToSentenceCase(name)!); + jtw.WriteValue(pcsa.Title ??StringConversion.ToSentenceCase(name)!); if (pcsa.Description != null) { jtw.WritePropertyName("description"); diff --git a/tools/Beef.CodeGen.Core/Generators/ModelCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/ModelCodeGenerator.cs new file mode 100644 index 000000000..640286293 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/ModelCodeGenerator.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.CodeGen.Config.Entity; +using System.Collections.Generic; +using System.Linq; + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the model code generator; assumes all entities will be generated as a model unless explicitly excluded. + /// + public class ModelCodeGenerator : CodeGeneratorBase + { + /// + /// + /// + /// + /// + protected override IEnumerable SelectGenConfig(Config.Entity.CodeGenConfig config) + => Check.NotNull(config, nameof(config)).Entities.Where(x => IsFalse(x.ExcludeEntity)).AsEnumerable(); + } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/RootEntityCodeGenerator.cs b/tools/Beef.CodeGen.Core/Generators/RootEntityCodeGenerator.cs new file mode 100644 index 000000000..d6dff88bb --- /dev/null +++ b/tools/Beef.CodeGen.Core/Generators/RootEntityCodeGenerator.cs @@ -0,0 +1,9 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +namespace Beef.CodeGen.Generators +{ + /// + /// Represents the Root code generator for . + /// + public class RootEntityCodeGenerator : CodeGeneratorBase { } +} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Generators/XmlSchemaGenerator.cs b/tools/Beef.CodeGen.Core/Generators/XmlSchemaGenerator.cs index 4b20974aa..a00ef39d3 100644 --- a/tools/Beef.CodeGen.Core/Generators/XmlSchemaGenerator.cs +++ b/tools/Beef.CodeGen.Core/Generators/XmlSchemaGenerator.cs @@ -85,7 +85,7 @@ private static void WriteObject(Type type, XNamespace ns, XElement xe, bool isRo if (psa == null) continue; - var name = jpa.PropertyName ?? Beef.CodeGen.CodeGenerator.ToCamelCase(pi.Name)!; + var name = jpa.PropertyName ?? StringConversion.ToCamelCase(pi.Name)!; var xp = new XElement(ns + "attribute", new XAttribute("name", XmlJsonRename.GetXmlName(ce, name)), new XAttribute("use", psa.IsMandatory ? "required" : "optional")); @@ -129,6 +129,6 @@ private static string GetXmlType(PropertyInfo pi) /// Gets the documentation text. /// private static string GetDocumentation(string name, PropertySchemaAttribute psa) => - psa.Description == null ? (psa.Title ?? Beef.CodeGen.CodeGenerator.ToSentenceCase(name)!) : $"{psa.Title ?? Beef.CodeGen.CodeGenerator.ToSentenceCase(name)!} {psa.Description}"; + psa.Description == null ? (psa.Title ?? StringConversion.ToSentenceCase(name)!) : $"{psa.Title ?? StringConversion.ToSentenceCase(name)!} {psa.Description}"; } } \ No newline at end of file diff --git a/src/Beef.Core/CodeGen/ICodeGenConfigLoader.cs b/tools/Beef.CodeGen.Core/ICodeGenConfigLoader.cs similarity index 100% rename from src/Beef.Core/CodeGen/ICodeGenConfigLoader.cs rename to tools/Beef.CodeGen.Core/ICodeGenConfigLoader.cs diff --git a/tools/Beef.CodeGen.Core/Loaders/EntityConfigLoader.cs b/tools/Beef.CodeGen.Core/Loaders/EntityConfigLoader.cs index 31d39e8cb..3df5c7ff5 100644 --- a/tools/Beef.CodeGen.Core/Loaders/EntityConfigLoader.cs +++ b/tools/Beef.CodeGen.Core/Loaders/EntityConfigLoader.cs @@ -44,9 +44,9 @@ public Task LoadBeforeChildrenAsync(CodeGenConfig config) if (!config.Attributes.ContainsKey("Name")) throw new CodeGenException("Entity element must have a Name property."); - config.AttributeAdd("Text", CodeGenerator.ToSentenceCase(config.Attributes["Name"])); - config.AttributeAdd("PrivateName", CodeGenerator.ToPrivateCase(config.Attributes["Name"])); - config.AttributeAdd("ArgumentName", CodeGenerator.ToCamelCase(config.Attributes["Name"])); + config.AttributeAdd("Text", StringConversion.ToSentenceCase(config.Attributes["Name"])); + config.AttributeAdd("PrivateName", StringConversion.ToPrivateCase(config.Attributes["Name"])); + config.AttributeAdd("ArgumentName", StringConversion.ToCamelCase(config.Attributes["Name"])); config.AttributeUpdate("Text", config.Attributes["Text"]); if (config.Attributes.ContainsKey("RefDataType")) diff --git a/tools/Beef.CodeGen.Core/Loaders/OperationConfigLoader.cs b/tools/Beef.CodeGen.Core/Loaders/OperationConfigLoader.cs index 9fd8a6fb4..eb5c0dd07 100644 --- a/tools/Beef.CodeGen.Core/Loaders/OperationConfigLoader.cs +++ b/tools/Beef.CodeGen.Core/Loaders/OperationConfigLoader.cs @@ -66,13 +66,13 @@ public Task LoadBeforeChildrenAsync(CodeGenConfig config) config.AttributeUpdate("ReturnText", config.Attributes["ReturnText"]); if (config.Attributes.ContainsKey("OperationType") && config.Attributes["OperationType"] == "Custom") - config.AttributeAdd("Text", CodeGenerator.ToSentenceCase(config.Attributes["Name"])); + config.AttributeAdd("Text", StringConversion.ToSentenceCase(config.Attributes["Name"])); if (config.Attributes.ContainsKey("Text")) config.AttributeUpdate("Text", config.Attributes["Text"]); - config.AttributeAdd("PrivateName", CodeGenerator.ToPrivateCase(config.Attributes["Name"])); - config.AttributeAdd("ArgumentName", CodeGenerator.ToCamelCase(config.Attributes["Name"])); + config.AttributeAdd("PrivateName", StringConversion.ToPrivateCase(config.Attributes["Name"])); + config.AttributeAdd("ArgumentName", StringConversion.ToCamelCase(config.Attributes["Name"])); config.AttributeAdd("QualifiedName", entity.Attributes["Name"] + config.Attributes["Name"]); return Task.CompletedTask; diff --git a/tools/Beef.CodeGen.Core/Loaders/ParameterConfigLoader.cs b/tools/Beef.CodeGen.Core/Loaders/ParameterConfigLoader.cs index 3b86756de..1987449df 100644 --- a/tools/Beef.CodeGen.Core/Loaders/ParameterConfigLoader.cs +++ b/tools/Beef.CodeGen.Core/Loaders/ParameterConfigLoader.cs @@ -31,17 +31,17 @@ public Task LoadBeforeChildrenAsync(CodeGenConfig config) if (config.Attributes.ContainsKey("Property")) UpdateConfigFromProperty(config, config.Attributes["Property"]!); - config.AttributeAdd("PrivateName", CodeGenerator.ToPrivateCase(config.Attributes["Name"])); - config.AttributeAdd("ArgumentName", CodeGenerator.ToCamelCase(config.Attributes["Name"])); + config.AttributeAdd("PrivateName", StringConversion.ToPrivateCase(config.Attributes["Name"])); + config.AttributeAdd("ArgumentName", StringConversion.ToCamelCase(config.Attributes["Name"])); config.AttributeAdd("Type", "string"); config.AttributeAdd("LayerPassing", "All"); if (config.GetAttributeValue("RefDataType") != null) - config.AttributeAdd("Text", string.Format(System.Globalization.CultureInfo.InvariantCulture, "{1} (see {{{{{0}}}}})", config.Attributes["Type"], CodeGenerator.ToSentenceCase(config.Attributes["Name"]))); + config.AttributeAdd("Text", string.Format(System.Globalization.CultureInfo.InvariantCulture, "{1} (see {{{{{0}}}}})", config.Attributes["Type"], StringConversion.ToSentenceCase(config.Attributes["Name"]))); else if (CodeGenConfig.SystemTypes.Contains(config.Attributes["Type"]!)) - config.AttributeAdd("Text", CodeGenerator.ToSentenceCase(config.Attributes["Name"])); + config.AttributeAdd("Text", StringConversion.ToSentenceCase(config.Attributes["Name"])); else - config.AttributeAdd("Text", string.Format(System.Globalization.CultureInfo.InvariantCulture, "{1} (see {{{{{0}}}}})", config.Attributes["Type"], CodeGenerator.ToSentenceCase(config.Attributes["Name"]))); + config.AttributeAdd("Text", string.Format(System.Globalization.CultureInfo.InvariantCulture, "{1} (see {{{{{0}}}}})", config.Attributes["Type"], StringConversion.ToSentenceCase(config.Attributes["Name"]))); config.AttributeUpdate("Text", config.Attributes["Text"]); diff --git a/tools/Beef.CodeGen.Core/Loaders/PropertyConfigLoader.cs b/tools/Beef.CodeGen.Core/Loaders/PropertyConfigLoader.cs index fc0c234aa..66064f810 100644 --- a/tools/Beef.CodeGen.Core/Loaders/PropertyConfigLoader.cs +++ b/tools/Beef.CodeGen.Core/Loaders/PropertyConfigLoader.cs @@ -34,19 +34,19 @@ public Task LoadBeforeChildrenAsync(CodeGenConfig config) config.AttributeAdd("RefDataType", "string"); if (config.GetAttributeValue("RefDataType") != null) - config.AttributeAdd("Text", string.Format(System.Globalization.CultureInfo.InvariantCulture, "{1} (see {{{{{0}}}}})", config.Attributes["Type"], CodeGenerator.ToSentenceCase(config.Attributes["Name"]))); + config.AttributeAdd("Text", string.Format(System.Globalization.CultureInfo.InvariantCulture, "{1} (see {{{{{0}}}}})", config.Attributes["Type"], StringConversion.ToSentenceCase(config.Attributes["Name"]))); else if (CodeGenConfig.SystemTypes.Contains(config.Attributes["Type"]!)) - config.AttributeAdd("Text", CodeGenerator.ToSentenceCase(config.Attributes["Name"])); + config.AttributeAdd("Text", StringConversion.ToSentenceCase(config.Attributes["Name"])); else - config.AttributeAdd("Text", string.Format(System.Globalization.CultureInfo.InvariantCulture, "{1} (see {{{{{0}}}}})", config.Attributes["Type"], CodeGenerator.ToSentenceCase(config.Attributes["Name"]))); + config.AttributeAdd("Text", string.Format(System.Globalization.CultureInfo.InvariantCulture, "{1} (see {{{{{0}}}}})", config.Attributes["Type"], StringConversion.ToSentenceCase(config.Attributes["Name"]))); config.AttributeUpdate("Text", config.Attributes["Text"]); config.AttributeAdd("StringTrim", "UseDefault"); config.AttributeAdd("StringTransform", "UseDefault"); config.AttributeAdd("DateTimeTransform", "UseDefault"); - config.AttributeAdd("PrivateName", CodeGenerator.ToPrivateCase(config.Attributes["Name"])); - config.AttributeAdd("ArgumentName", CodeGenerator.ToCamelCase(config.Attributes["Name"])); + config.AttributeAdd("PrivateName", StringConversion.ToPrivateCase(config.Attributes["Name"])); + config.AttributeAdd("ArgumentName", StringConversion.ToCamelCase(config.Attributes["Name"])); config.AttributeAdd("DisplayName", GenerateDisplayName(config)); config.AttributeAdd("Nullable", CodeGenConfig.IgnoreNullableTypes.Contains(config.Attributes["Type"]!) ? "false" : "true"); @@ -59,7 +59,7 @@ public Task LoadBeforeChildrenAsync(CodeGenConfig config) /// private static string GenerateDisplayName(CodeGenConfig config) { - var dn = CodeGenerator.ToSentenceCase(config.Attributes["Name"])!; + var dn = StringConversion.ToSentenceCase(config.Attributes["Name"])!; var parts = dn.Split(' '); if (parts.Length == 1) return (parts[0] == "Id") ? "Identifier" : dn; diff --git a/tools/Beef.CodeGen.Core/Loaders/TableConfigLoader.cs b/tools/Beef.CodeGen.Core/Loaders/TableConfigLoader.cs index 6d08f46dc..70d06f71d 100644 --- a/tools/Beef.CodeGen.Core/Loaders/TableConfigLoader.cs +++ b/tools/Beef.CodeGen.Core/Loaders/TableConfigLoader.cs @@ -163,7 +163,9 @@ public Task LoadAfterChildrenAsync(CodeGenConfig config) sp.AttributeAdd("Name", "GetAll"); sp.AttributeAdd("Type", "GetAll"); - if (config.Root.Attributes.ContainsKey("RefDatabaseSchema") && table.Schema == config.Root.GetAttributeValue("RefDatabaseSchema")) + var isRefData = config.GetAttributeValue("RefData"); + if ((isRefData.HasValue && isRefData.Value) + || (!isRefData.HasValue && config.Root.Attributes.ContainsKey("RefDatabaseSchema") && table.Schema == config.Root.GetAttributeValue("RefDatabaseSchema"))) { var obList = new List(); CodeGenConfig ob = new CodeGenConfig("OrderBy", config); diff --git a/tools/Beef.CodeGen.Core/ResourceManager.cs b/tools/Beef.CodeGen.Core/ResourceManager.cs index 790b2bf18..cdb0edb09 100644 --- a/tools/Beef.CodeGen.Core/ResourceManager.cs +++ b/tools/Beef.CodeGen.Core/ResourceManager.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; -using System.Xml.Linq; namespace Beef.CodeGen { @@ -27,15 +26,14 @@ public static class ResourceManager } /// - /// Gets the Resource content XML from the until found. + /// Gets the Script content from the until found. /// /// The resource name. /// Assemblies to use to probe for assembly resource; will check this assembly also (no need to specify). - /// The resource content XML where found; otherwise, null. - public static async Task GetResourceContentXmlAsync(string name, params Assembly[] assemblies) + /// The resource content where found; otherwise, null. + public static Task GetScriptContentAsync(string name, params Assembly[] assemblies) { - var c = await GetResourceContentAsync(name, "Resources", assemblies).ConfigureAwait(false); - return (c == null) ? null : XElement.Parse(c); + return GetResourceContentAsync(name, "Scripts", assemblies); } /// @@ -50,44 +48,13 @@ public static class ResourceManager } /// - /// Gets the Template content XML from the until found. - /// - /// The resource name. - /// assemblies to use to probe for assembly resource; will check this assembly also (no need to specify). - /// The resource content XML where found; otherwise, null. - public static async Task GetTemplateContentXmlAsync(string name, params Assembly[] assemblies) - { - var c = await GetResourceContentAsync(name, "Templates", assemblies).ConfigureAwait(false); - return (c == null) ? null : XElement.Parse(c); - } - - /// - /// Gets the Template content from the until found. + /// Gets the specified resource content from the until found. /// /// The resource name. - /// Assemblies to use to probe for assembly resource; will check this assembly also (no need to specify). + /// The resource type. + /// The assemblies to use to probe for the assembly resource; will check this assembly also (no need to specify). /// The resource content where found; otherwise, null. - public static Task GetScriptContentAsync(string name, params Assembly[] assemblies) - { - return GetResourceContentAsync(name, "Scripts", assemblies); - } - - /// - /// Gets the Template content XML from the until found. - /// - /// The resource name. - /// assemblies to use to probe for assembly resource; will check this assembly also (no need to specify). - /// The resource content XML where found; otherwise, null. - public static async Task GetScriptContentXmlAsync(string name, params Assembly[] assemblies) - { - var c = await GetResourceContentAsync(name, "Scripts", assemblies).ConfigureAwait(false); - return (c == null) ? null : XElement.Parse(c); - } - - /// - /// Gets the specified resource content. - /// - private static async Task GetResourceContentAsync(string name, string resourceType, params Assembly[] assemblies) + public static async Task GetResourceContentAsync(string name, string resourceType, params Assembly[] assemblies) { if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name)); diff --git a/tools/Beef.CodeGen.Core/Schema/codegen.table.xsd b/tools/Beef.CodeGen.Core/Schema/codegen.table.xsd index 0a51b29d6..56a1a7b53 100644 --- a/tools/Beef.CodeGen.Core/Schema/codegen.table.xsd +++ b/tools/Beef.CodeGen.Core/Schema/codegen.table.xsd @@ -10,7 +10,7 @@ - + The query stored procedure configuration. diff --git a/tools/Beef.CodeGen.Core/Scripts/DataModelOnly.xml b/tools/Beef.CodeGen.Core/Scripts/DataModelOnly.xml index 403284caf..d4902126d 100644 --- a/tools/Beef.CodeGen.Core/Scripts/DataModelOnly.xml +++ b/tools/Beef.CodeGen.Core/Scripts/DataModelOnly.xml @@ -1,5 +1,5 @@  - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Scripts/Database.xml b/tools/Beef.CodeGen.Core/Scripts/Database.xml index 57e883782..e6a5ff9ad 100644 --- a/tools/Beef.CodeGen.Core/Scripts/Database.xml +++ b/tools/Beef.CodeGen.Core/Scripts/Database.xml @@ -1,6 +1,6 @@  - diff --git a/tools/Beef.CodeGen.Core/Scripts/EntityOnly.xml b/tools/Beef.CodeGen.Core/Scripts/EntityOnly.xml index 02105530e..1188a017f 100644 --- a/tools/Beef.CodeGen.Core/Scripts/EntityOnly.xml +++ b/tools/Beef.CodeGen.Core/Scripts/EntityOnly.xml @@ -1,5 +1,5 @@  diff --git a/tools/Beef.CodeGen.Core/Scripts/EntityWebApiCoreAgent.xml b/tools/Beef.CodeGen.Core/Scripts/EntityWebApiCoreAgent.xml index 6673ee69f..96e7b614d 100644 --- a/tools/Beef.CodeGen.Core/Scripts/EntityWebApiCoreAgent.xml +++ b/tools/Beef.CodeGen.Core/Scripts/EntityWebApiCoreAgent.xml @@ -1,26 +1,28 @@  - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Scripts/RefDataCore.xml b/tools/Beef.CodeGen.Core/Scripts/RefDataCore.xml index b616d0ca3..f3f459458 100644 --- a/tools/Beef.CodeGen.Core/Scripts/RefDataCore.xml +++ b/tools/Beef.CodeGen.Core/Scripts/RefDataCore.xml @@ -1,21 +1,24 @@  - diff --git a/tools/Beef.CodeGen.Core/Scripts/RefDataCoreCrud.xml b/tools/Beef.CodeGen.Core/Scripts/RefDataCoreCrud.xml index b81cd93fc..fe92d990b 100644 --- a/tools/Beef.CodeGen.Core/Scripts/RefDataCoreCrud.xml +++ b/tools/Beef.CodeGen.Core/Scripts/RefDataCoreCrud.xml @@ -1,33 +1,34 @@  - diff --git a/tools/Beef.CodeGen.Core/Templates/EntityDataSvc_cs.hbs b/tools/Beef.CodeGen.Core/Templates/EntityDataSvc_cs.hbs new file mode 100644 index 000000000..0c87ae25a --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/EntityDataSvc_cs.hbs @@ -0,0 +1,137 @@ +{{! Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef }} +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005, IDE0044 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Business; +{{#if DataSvcCaching}} +using Beef.Caching; +{{/if}} +using Beef.Entities; +{{#if SupportsEvents}} +using Beef.Events; +{{/if}} +{{#if RequiresData}} +using {{Root.Company}}.{{Root.AppName}}.Business.Data; +{{/if}} +{{#ifeq Root.EntityUsing 'Common' 'All'}} +using {{Root.Company}}.{{Root.AppName}}.Common.Entities; +{{/ifeq}} +{{#ifeq Root.EntityUsing 'Business' 'All'}} +using {{Root.Company}}.{{Root.AppName}}.Business.Entities; +{{/ifeq}} +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.Business.DataSvc +{ + /// + /// Provides the {{{EntityNameSeeComments}}} data repository services. + /// + public partial class {{Name}}DataSvc{{#if GenericWithT}}{{/if}} : I{{Name}}DataSvc{{#if GenericWithT}}{{/if}} + { +{{#each DataSvcConstructorParameters}} + private readonly {{Type}} {{PrivateName}}; + {{#if @last}} + + {{/if}} +{{/each}} +{{#if DataSvcExtensions}} + #region Extensions + #pragma warning disable CS0649 // Defaults to null by design; can be overridden in constructor. + + {{#each DataSvcAutoOperations}} + private Func<{{#if HasReturnValue}}{{OperationReturnType}}, {{/if}}{{#each ValueLessDataParameters}}{{{ParameterType}}}, {{/each}}Task>? {{PrivateName}}OnAfterAsync; + {{/each}} + + #pragma warning restore CS0649 + #endregion + +{{/if}} + /// + /// Initializes a new instance of the class. + /// +{{#each DataSvcConstructorParameters}} + /// {{{SummaryText}}} +{{/each}} + {{lower DataSvcConstructor}} {{Name}}DataSvc({{#each DataSvcConstructorParameters}}{{#unless @first}}, {{/unless}}{{Type}} {{ArgumentName}}{{/each}}) + { {{#each DataSvcConstructorParameters}}{{PrivateName}} = Check.NotNull({{ArgumentName}}, nameof({{ArgumentName}})); {{/each}}{{Name}}DataSvcCtor(); } + + partial void {{Name}}DataSvcCtor(); // Enables additional functionality to be added to the constructor. +{{#each DataSvcOperations}} + + /// + /// {{{SummaryText}}} + /// + {{#each DataParameters}} + /// {{{SummaryText}}} + {{/each}} + {{#if HasReturnValue}} + /// {{{ReturnText}}} + {{/if}} + public {{{OperationTaskReturnType}}} {{Name}}Async({{#each DataParameters}}{{#unless @first}}, {{/unless}}{{ParameterType}} {{ArgumentName}}{{/each}}) + {{#if DataSvcCustom}} + => DataSvcInvoker.Current.InvokeAsync(this, () => {{Name}}OnImplementationAsync({{#each DataParameters}}{{#unless @first}}, {{/unless}}{{ArgumentName}}{{/each}}){{#if DataSvcTransaction}}, new BusinessInvokerArgs { IncludeTransactionScope = true }{{/if}}); + {{else}} + { + return DataSvcInvoker.Current.InvokeAsync(this, async () => + { + {{#if SupportsCaching}} + {{#ifeq Type 'Get'}} + var __key = new UniqueKey({{#each ValueLessDataParameters}}{{#unless @first}}, {{/unless}}{{ArgumentName}}{{/each}}); + if (_cache.TryGetValue(__key, out {{OperationReturnType}} __val)) + return __val; + + {{/ifeq}} + {{/if}} + {{#if HasReturnValue}}var __result = {{/if}}await _data.{{Name}}Async({{#each DataParameters}}{{#unless @first}}, {{/unless}}{{#if IsValueArg}}Check.NotNull(value, nameof(value)){{else}}{{ArgumentName}}{{/if}}{{/each}}).ConfigureAwait(false); + {{#ifeq Events.Count 1}} + {{#each Events}} + await _evtPub.Publish{{#if ../HasReturnValue}}Value{{/if}}Async({{#if ../HasReturnValue}}__result, {{/if}}$"{{Subject}}", "{{Action}}"{{#each ../ValueLessDataParameters}}, {{ArgumentName}}{{/each}}).ConfigureAwait(false); + {{/each}} + {{else}} + {{#ifeq Events.Count 0}} + {{else}} + await _evtPub.PublishAsync( + {{#each Events}} + _evtPub.Create{{#if ../HasReturnValue}}Value{{/if}}Event({{#if ../HasReturnValue}}__result, {{/if}}$"{{Subject}}", "{{Action}}"{{#each ../ValueLessDataParameters}}, {{ArgumentName}}{{/each}}){{#if @last}}).ConfigureAwait(false);{{else}},{{/if}} + {{#if @last}} + + {{/if}} + {{/each}} + {{/ifeq}} + {{/ifeq}} + {{#if SupportsCaching}} + {{#ifeq Type 'Delete'}} + _cache.Remove<{{Parent.Name}}>(new UniqueKey({{#each ValueLessDataParameters}}{{#unless @first}}, {{/unless}}{{ArgumentName}}{{/each}})); + {{else}} + _cache.SetValue({{#ifeq Type 'Get'}}__key{{else}}__result.UniqueKey{{/ifeq}}, __result); + {{/ifeq}} + {{/if}} + {{#if Parent.DataSvcExtensions}} + if ({{PrivateName}}OnAfterAsync != null) await {{PrivateName}}OnAfterAsync({{#if HasReturnValue}}__result{{/if}}{{#each ValueLessDataParameters}}{{#if @first}}{{#if Parent.HasReturnValue}}, {{/if}}{{else}}, {{/if}}{{{ArgumentName}}}{{/each}}).ConfigureAwait(false); + {{/if}} + {{#if HasReturnValue}} + return __result; + {{/if}} + }{{#if DataSvcTransaction}}, new BusinessInvokerArgs { IncludeTransactionScope = true }{{/if}}); + } + {{/if}} +{{/each}} + } +} + +#pragma warning restore IDE0005, IDE0044 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/EntityDataSvc_cs.xml b/tools/Beef.CodeGen.Core/Templates/EntityDataSvc_cs.xml deleted file mode 100644 index 7db026168..000000000 --- a/tools/Beef.CodeGen.Core/Templates/EntityDataSvc_cs.xml +++ /dev/null @@ -1,671 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/EntityData_cs.hbs b/tools/Beef.CodeGen.Core/Templates/EntityData_cs.hbs new file mode 100644 index 000000000..d7ffe465a --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/EntityData_cs.hbs @@ -0,0 +1,342 @@ +{{! Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef }} +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +{{#if UsesCosmos}} +using Microsoft.Azure.Cosmos; +{{/if}} +using Beef; +using Beef.Business; +{{#if UsesCosmos}} +using Beef.Data.Cosmos; + {{#ifval Root.CosmosUsingNamespace}} +using {{Root.CosmosUsingNamespace}}; + {{/ifval}} +{{/if}} +{{#if UsesDatabase}} +using Beef.Data.Database; + {{#ifval Root.DatabaseUsingNamespace}} +using {{Root.DatabaseUsingNamespace}}; + {{/ifval}} +{{/if}} +{{#if UsesEntityFramework}} +using Beef.Data.EntityFrameworkCore; + {{#ifval Root.EntityFrameworkUsingNamespace}} +using {{Root.EntityFrameworkUsingNamespace}}; + {{/ifval}} +{{/if}} +{{#if UsesOData}} +using Beef.Data.OData; + {{#ifval Root.ODataUsingNamespace}} +using {{Root.ODataUsingNamespace}}; + {{/ifval}} +{{/if}} +using Beef.Entities; +using Beef.Mapper; +using Beef.Mapper.Converters; +{{#ifeq Root.EntityUsing 'Common' 'All'}} +using {{Root.Company}}.{{Root.AppName}}.Common.Entities; +{{/ifeq}} +{{#ifeq Root.EntityUsing 'Business' 'All'}} +using {{Root.Company}}.{{Root.AppName}}.Business.Entities; +{{/ifeq}} +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} +{{#if UsesOData}} +using Soc = Simple.OData.Client; +{{/if}} + +namespace {{Root.Company}}.{{Root.AppName}}.Business.Data +{ + /// + /// Provides the {{{EntityNameSeeComments}}} data access. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1052:Static holder types should be Static or NotInheritable", Justification = "Will not always appear static depending on code-gen options")] + public partial class {{Name}}Data{{#if GenericWithT}}{{/if}}{{#ifne Operations.Count 0}} : I{{Name}}Data{{#if GenericWithT}}{{/if}}{{/ifne}} + { +{{#ifne Operations.Count 0}} + {{#each DataConstructorParameters}} + private readonly {{Type}} {{PrivateName}}; + {{#if @last}} + + {{/if}} + {{/each}} + {{#if DataExtensionsRequired}} + #region Extensions + #pragma warning disable CS0649, IDE0044 // Defaults to null by design; can be overridden in constructor. + + {{#if UsesCosmos}} + private Action? _onDataArgsCreate; + {{/if}} + {{#each DataOperations}} + {{#ifeq Type 'GetColl'}} + {{#ifeq AutoImplement 'Database'}} + private Action? {{PrivateName}}OnQuery; + {{/ifeq}} + {{#ifeq AutoImplement 'EntityFramework'}} + private Func, {{#each CoreDataParameters}}{{{ParameterType}}}, {{/each}}{{DataArgs.Type}}, IQueryable<{{Parent.EntityFrameworkModel}}>>? {{PrivateName}}OnQuery; + {{/ifeq}} + {{#ifeq AutoImplement 'Cosmos'}} + {{#ifval Parent.CosmosValueContainer}} + private Func>, {{#each CoreDataParameters}}{{{ParameterType}}}, {{/each}}{{DataArgs.Type}}, IQueryable>>? {{PrivateName}}OnQuery; + {{else}} + private Func, {{#each CoreDataParameters}}{{{ParameterType}}}, {{/each}}{{DataArgs.Type}}, IQueryable<{{Parent.CosmosModel}}>>? {{PrivateName}}OnQuery; + {{/ifval}} + {{/ifeq}} + {{#ifeq AutoImplement 'OData'}} + private Func, {{#each CoreDataParameters}}{{{ParameterType}}}, {{/each}}{{DataArgs.Type}}, Soc.IBoundClient<{{Parent.ODataModel}}>>? {{PrivateName}}OnQuery; + {{/ifeq}} + {{/ifeq}} + {{#if Parent.DataExtensions}} + {{#ifne AutoImplement 'None'}} + private Func<{{#each PagingLessDataParameters}}{{{ParameterType}}}, {{/each}}{{DataArgs.Type}}, Task>? {{PrivateName}}OnBeforeAsync; + private Func<{{#if HasReturnValue}}{{OperationReturnType}}, {{/if}}{{#each CoreDataParameters}}{{{ParameterType}}}, {{/each}}Task>? {{PrivateName}}OnAfterAsync; + {{/ifne}} + private Action? {{PrivateName}}OnException; + {{/if}} + {{/each}} + + #pragma warning restore CS0649, IDE0044 + #endregion + + {{/if}} + /// + /// Initializes a new instance of the class. + /// + {{#each DataConstructorParameters}} + /// {{{SummaryText}}} + {{/each}} + {{lower DataConstructor}} {{Name}}Data({{#each DataConstructorParameters}}{{#unless @first}}, {{/unless}}{{Type}} {{ArgumentName}}{{/each}}) + { {{#each DataConstructorParameters}}{{PrivateName}} = Check.NotNull({{ArgumentName}}, nameof({{ArgumentName}})); {{/each}}{{Name}}DataCtor(); } + + partial void {{Name}}DataCtor(); // Enables additional functionality to be added to the constructor. + {{#each DataOperations}} + + /// + /// {{{SummaryText}}} + /// + {{#each DataParameters}} + /// {{{SummaryText}}} + {{/each}} + {{#if HasReturnValue}} + /// {{{ReturnText}}} + {{/if}} + public {{{OperationTaskReturnType}}} {{Name}}Async({{#each DataParameters}}{{#unless @first}}, {{/unless}}{{ParameterType}} {{ArgumentName}}{{/each}}) + {{#ifeq AutoImplement 'None'}} + {{#if Parent.DataExtensions}} + => DataInvoker.Current.InvokeAsync(this, () => {{Name}}OnImplementationAsync({{#each DataParameters}}{{#unless @first}}, {{/unless}}{{#if IsValueArg}}Check.NotNull(value, nameof(value)){{else}}{{ArgumentName}}{{/if}}{{/each}}), new BusinessInvokerArgs { ExceptionHandler = {{PrivateName}}OnException{{#if DataTransaction}}, IncludeTransactionScope = true{{/if}} }); + {{else}} + => DataInvoker.Current.InvokeAsync(this, () => {{Name}}OnImplementationAsync({{#each DataParameters}}{{#unless @first}}, {{/unless}}{{#if IsValueArg}}Check.NotNull(value, nameof(value)){{else}}{{ArgumentName}}{{/if}}{{/each}}){{#if DataTransaction}}, new BusinessInvokerArgs { IncludeTransactionScope = true }{{/if}}); + {{/if}} + {{else}} + { + return DataInvoker.Current.InvokeAsync(this, async () => + { + {{#ifeq Type 'GetColl'}} + {{OperationReturnType}} __result = new {{OperationReturnType}}({{#if Paging}}paging{{/if}}); + {{else}} + {{#if Parent.DataExtensions}} + {{#if HasReturnValue}} + {{OperationReturnType}} __result; + {{/if}} + {{/if}} + {{/ifeq}} + {{#ifeq AutoImplement 'Database'}} + var __dataArgs = {{DataEntityMapper}}.Default.CreateArgs("[{{Parent.DatabaseSchema}}].[{{DatabaseStoredProc}}]"{{#if Paging}}, __result.Paging!{{/if}}); + {{/ifeq}} + {{#ifeq AutoImplement 'EntityFramework'}} + var __dataArgs = {{DataEntityMapper}}.Default.CreateArgs({{#if Paging}}__result.Paging!{{/if}}); + {{/ifeq}} + {{#ifeq AutoImplement 'Cosmos'}} + var __dataArgs = {{DataEntityMapper}}.Default.CreateArgs("{{Parent.CosmosContainerId}}"{{#if Paging}}, __result.Paging!{{/if}}, {{CosmosPartitionKeyCode}}, onCreate: _onDataArgsCreate); + {{/ifeq}} + {{#ifeq AutoImplement 'OData'}} + var __dataArgs = {{DataEntityMapper}}.Default.CreateArgs({{#if Paging}}__result.Paging!{{/if}}{{#ifval ODataCollectionName}}{{#if Paging}}, {{/if}}"{{ODataCollectionName}}"{{/ifval}}); + {{/ifeq}} + {{#if Parent.DataExtensions}} + if ({{PrivateName}}OnBeforeAsync != null) await {{PrivateName}}OnBeforeAsync({{#each PagingLessDataParameters}}{{{ArgumentName}}}, {{/each}}__dataArgs).ConfigureAwait(false); + {{/if}} + {{#ifeq AutoImplement 'Database'}} + {{#ifeq Type 'GetColl'}} + __result.Result = await {{DataArgs.Name}}.Query(__dataArgs, p => {{PrivateName}}OnQuery?.Invoke(p, {{#each CoreDataParameters}}{{{ArgumentName}}}, {{/each}}__dataArgs)).SelectQueryAsync<{{Parent.EntityCollectionName}}>().ConfigureAwait(false); + {{else}} + {{#if Parent.DataExtensions}}{{#if HasReturnValue}}__result = {{/if}}{{else}}{{#if HasReturnValue}}return {{/if}}{{/if}}await {{DataArgs.Name}}.{{Type}}Async(__dataArgs{{#each PagingLessDataParameters}}, {{#if IsValueArg}}Check.NotNull(value, nameof(value)){{else}}{{ParameterConverted}}{{/if}}{{/each}}).ConfigureAwait(false); + {{/ifeq}} + {{/ifeq}} + {{#ifeq AutoImplement 'EntityFramework'}} + {{#ifeq Type 'GetColl'}} + __result.Result = {{DataArgs.Name}}.Query(__dataArgs, q => {{PrivateName}}OnQuery?.Invoke(q, {{#each CoreDataParameters}}{{{ArgumentName}}}, {{/each}}__dataArgs) ?? q).SelectQuery<{{BaseReturnType}}Collection>(); + {{else}} + {{#if Parent.DataExtensions}}{{#if HasReturnValue}}__result = {{/if}}{{else}}{{#if HasReturnValue}}return {{/if}}{{/if}}await {{DataArgs.Name}}.{{Type}}Async(__dataArgs{{#each PagingLessDataParameters}}, {{#if IsValueArg}}Check.NotNull(value, nameof(value)){{else}}{{ParameterConverted}}{{/if}}{{/each}}).ConfigureAwait(false); + {{/ifeq}} + {{/ifeq}} + {{#ifeq AutoImplement 'Cosmos'}} + {{#ifeq Type 'GetColl'}} + __result.Result = {{DataArgs.Name}}.{{#if CosmosValueContainer}}Value{{/if}}Container(__dataArgs).Query(q => {{PrivateName}}OnQuery?.Invoke(q, {{#each CoreDataParameters}}{{{ArgumentName}}}, {{/each}}__dataArgs) ?? q).SelectQuery<{{BaseReturnType}}Collection>(); + {{else}} + {{#if Parent.DataExtensions}}{{#if HasReturnValue}}__result = {{/if}}{{else}}{{#if HasReturnValue}}return {{/if}}{{/if}}await {{DataArgs.Name}}.{{#if CosmosValueContainer}}Value{{/if}}Container(__dataArgs).{{Type}}Async({{#each PagingLessDataParameters}}{{#unless @first}}, {{/unless}}{{#if IsValueArg}}Check.NotNull(value, nameof(value)){{else}}{{ParameterConverted}}{{/if}}{{/each}}).ConfigureAwait(false); + {{/ifeq}} + {{/ifeq}} + {{#ifeq AutoImplement 'OData'}} + {{#ifeq Type 'GetColl'}} + __result.Result = {{DataArgs.Name}}.Query(__dataArgs, q => {{PrivateName}}OnQuery?.Invoke(q, {{#each CoreDataParameters}}{{{ArgumentName}}}, {{/each}}__dataArgs) ?? q).SelectQuery<{{BaseReturnType}}Collection>(); + {{else}} + {{#if Parent.DataExtensions}}{{#if HasReturnValue}}__result = {{/if}}{{else}}{{#if HasReturnValue}}return {{/if}}{{/if}}await {{DataArgs.Name}}.{{Type}}Async(__dataArgs{{#each PagingLessDataParameters}}, {{#if IsValueArg}}Check.NotNull(value, nameof(value)){{else}}{{ParameterConverted}}{{/if}}{{/each}}).ConfigureAwait(false); + {{/ifeq}} + {{/ifeq}} + {{#if Parent.DataExtensions}} + if ({{PrivateName}}OnAfterAsync != null) await {{PrivateName}}OnAfterAsync({{#if HasReturnValue}}__result{{/if}}{{#each CoreDataParameters}}{{#if @first}}{{#if Parent.HasReturnValue}}, {{/if}}{{else}}, {{/if}}{{ArgumentName}}{{/each}}).ConfigureAwait(false); + {{#if HasReturnValue}} + return __result; + {{/if}} + }, new BusinessInvokerArgs { ExceptionHandler = {{PrivateName}}OnException{{#if DataTransaction}}, IncludeTransactionScope = true{{/if}} }); + {{else}} + {{#if HasReturnValue}} + {{#ifeq Type 'GetColl'}} + return await Task.FromResult(__result).ConfigureAwait(false); + {{/ifeq}} + {{/if}} + }{{#if DataTransaction}}, new BusinessInvokerArgs { IncludeTransactionScope = true }{{/if}}); + {{/if}} + } + {{/ifeq}} + {{/each}} +{{/ifne}} +{{#if UsesDatabase}} + {{#unless DatabaseCustomMapper}} + + /// + /// Provides the {{{EntityNameSeeComments}}} property and database column mapping. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] + public partial class DbMapper : DatabaseMapper<{{EntityName}}, DbMapper> + { + /// + /// Initializes a new instance of the class. + /// + public DbMapper() + { + {{#ifval DatabaseMapperInheritsFrom}} + InheritPropertiesFrom({{DatabaseMapperInheritsFrom}}.Default); + {{/ifval}} + {{#each DatabaseMapperProperties}} + Property(s => s.{{DataMapperPropertyName}}{{#ifval DataName}}, "{{DataName}}"{{/ifval}}{{ifne DataOperationTypes 'Any'}}, operationTypes: OperationTypes.{{DataOperationTypes}}{{/ifne}}){{#if UniqueKey}}.SetUniqueKey({{#if DataAutoGenerated}}true{{else}}false{{/if}}){{/if}}{{#ifval DataConverterCode}}{{{DataConverterCode}}}{{/ifval}}{{#ifval DatabaseMapper}}.SetMapper({{DatabaseMapper}}.Default!){{/ifval}}; + {{/each}} + {{#if MapperAddStandardProperties}} + AddStandardProperties(); + {{/if}} + DbMapperCtor(); + } + + partial void DbMapperCtor(); // Enables the DbMapper constructor to be extended. + } + {{/unless}} +{{/if}} +{{#if UsesEntityFramework}} + {{#unless EntityFrameworkCustomMapper}} + + /// + /// Provides the {{{EntityNameSeeComments}}} and Entity Framework {{{seecomments EntityFrameworkModel}}} property mapping. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] + public partial class EfMapper : EfDbMapper<{{EntityName}}, {{EntityFrameworkModel}}, EfMapper> + { + /// + /// Initializes a new instance of the class. + /// + public EfMapper() + { + {{#ifval EntityFrameworkMapperInheritsFrom}} + InheritPropertiesFrom({{EntityFrameworkMapperInheritsFrom}}.Default); + {{/ifval}} + {{#each EntityFrameworkMapperProperties}} + Property(s => s.{{DataMapperPropertyName}}, d => d.{{#ifval DataName}}{{DataName}}{{else}}{{Name}}{{/ifval}}){{ifne DataOperationTypes 'Any'}}.SetOperationTypes(OperationTypes.{{DataOperationTypes}}){{/ifne}}{{#if UniqueKey}}.SetUniqueKey({{#if DataAutoGenerated}}true{{else}}false{{/if}}){{/if}}{{#ifval DataConverterCode}}{{{DataConverterCode}}}{{/ifval}}{{#ifval EntityFrameworkMapper}}.SetMapper({{EntityFrameworkMapper}}.Default!){{/ifval}}; + {{/each}} + {{#if MapperAddStandardProperties}} + AddStandardProperties(); + {{/if}} + EfMapperCtor(); + } + + partial void EfMapperCtor(); // Enables the EfMapper constructor to be extended. + } + {{/unless}} +{{/if}} +{{#if UsesCosmos}} + {{#unless CosmosCustomMapper}} + + /// + /// Provides the {{{EntityNameSeeComments}}} and Cosmos {{{seecomments EntityFrameworkModel}}} property mapping. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] + public partial class CosmosMapper : CosmosDbMapper<{{EntityName}}, {{CosmosModel}}, CosmosMapper> + { + /// + /// Initializes a new instance of the class. + /// + public CosmosMapper() + { + {{#ifval CosmosMapperInheritsFrom}} + InheritPropertiesFrom({{CosmosMapperInheritsFrom}}.Default); + {{/ifval}} + {{#each CosmosMapperProperties}} + Property(s => s.{{DataMapperPropertyName}}, d => d.{{#ifval DataName}}{{DataName}}{{else}}{{Name}}{{/ifval}}){{ifne DataOperationTypes 'Any'}}.SetOperationTypes(OperationTypes.{{DataOperationTypes}}){{/ifne}}{{#if UniqueKey}}.SetUniqueKey({{#if DataAutoGenerated}}true{{else}}false{{/if}}){{/if}}{{#ifval DataConverterCode}}{{{DataConverterCode}}}{{/ifval}}{{#ifval CosmosMapper}}.SetMapper({{CosmosMapper}}.Default!){{/ifval}}; + {{/each}} + {{#if MapperAddStandardProperties}} + AddStandardProperties(); + {{/if}} + CosmosMapperCtor(); + } + + partial void CosmosMapperCtor(); // Enables the CosmosMapper constructor to be extended. + } + {{/unless}} +{{/if}} +{{#if UsesOData}} + {{#unless ODataCustomMapper}} + + /// + /// Provides the {{{EntityNameSeeComments}}} and OData {{{seecomments EntityFrameworkModel}}} property mapping. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "By design; as there is a direct relationship")] + public partial class ODataMapper : ODataMapper<{{EntityName}}, {{ODataModel}}, ODataMapper> + { + /// + /// Initializes a new instance of the class. + /// + public ODataMapper() + { + {{#ifval ODataMapperInheritsFrom}} + InheritPropertiesFrom({{ODataMapperInheritsFrom}}.Default); + {{/ifval}} + {{#each ODataMapperProperties}} + Property(s => s.{{DataMapperPropertyName}}, d => d.{{#ifval DataName}}{{DataName}}{{else}}{{Name}}{{/ifval}}){{ifne DataOperationTypes 'Any'}}.SetOperationTypes(OperationTypes.{{DataOperationTypes}}){{/ifne}}{{#if UniqueKey}}.SetUniqueKey({{#if DataAutoGenerated}}true{{else}}false{{/if}}){{/if}}{{#ifval DataConverterCode}}{{{DataConverterCode}}}{{/ifval}}{{#ifval ODataMapper}}.SetMapper({{ODataMapper}}.Default!){{/ifval}}; + {{/each}} + {{#if MapperAddStandardProperties}} + AddStandardProperties(); + {{/if}} + ODataMapperCtor(); + } + + partial void ODataMapperCtor(); // Enables the ODataMapper constructor to be extended. + } + {{/unless}} +{{/if}} + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/EntityData_cs.xml b/tools/Beef.CodeGen.Core/Templates/EntityData_cs.xml deleted file mode 100644 index c2e23911c..000000000 --- a/tools/Beef.CodeGen.Core/Templates/EntityData_cs.xml +++ /dev/null @@ -1,1466 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/EntityGrpcAgent_cs.hbs b/tools/Beef.CodeGen.Core/Templates/EntityGrpcAgent_cs.hbs new file mode 100644 index 000000000..9a49dbe72 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/EntityGrpcAgent_cs.hbs @@ -0,0 +1,82 @@ +{{! Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef }} +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Beef.Entities; +using Beef.Grpc; +using Beef.WebApi; +using {{Root.Company}}.{{Root.AppName}}.Common.Entities; +using proto = {{Root.Company}}.{{Root.AppName}}.Common.Grpc.Proto; +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.Common.Grpc +{ + /// + /// Defines the {{{EntityNameSeeComments}}} gRPC agent. + /// + public partial interface I{{Name}}Agent + { +{{#each GrpcOperations}} + {{#unless @first}} + + {{/unless}} + /// + /// {{{SummaryText}}} + /// + {{#ifeq Type 'Patch'}} + /// The . + {{/ifeq}} + {{#each Parameters}} + /// {{{SummaryText}}} + {{/each}} + /// The optional . + /// A . + {{{GrpcAgentOperationTaskReturnType}}} {{Name}}Async({{#each Parameters}}{{ParameterType}} {{ArgumentName}}{{#if IsPagingArgs}} = null{{/if}}, {{/each}}GrpcRequestOptions? requestOptions = null); +{{/each}} + } + + /// + /// Provides the {{{EntityNameSeeComments}}} gRPC agent. + /// + public partial class {{Name}}Agent : GrpcAgentBase, I{{Name}}Agent + { + /// + /// Initializes a new instance of the class. + /// + /// The . + public {{Name}}Agent(IWebApiAgentArgs args) : base(args) { } +{{#each GrpcOperations}} + + /// + /// {{{SummaryText}}} + /// + {{#ifeq Type 'Patch'}} + /// The . + {{/ifeq}} + {{#each Parameters}} + /// {{{SummaryText}}} + {{/each}} + /// The optional . + /// A . + public {{{GrpcAgentOperationTaskReturnType}}} {{Name}}Async({{#each Parameters}}{{ParameterType}} {{ArgumentName}}{{#if IsPagingArgs}} = null{{/if}}, {{/each}}GrpcRequestOptions? requestOptions = null) + { + {{#ifne Parameters.Count 0}} + var __req = new proto.{{Parent.Name}}{{Name}}Request { {{#each Parameters}}{{#unless @first}}, {{/unless}}{{Name}} = {{#if IsValueArg}}Transformers.{{Type}}.MapToDest(Check.NotNull(value, nameof(value))){{else}}{{#if IsPagingArgs}}Transformers.PagingArgsToPagingArgsConverter.ConvertToDest(paging!){{else}}{{#if GrpcConverter}}Transformers.{{GrpcConverter}}.ConvertToDest({{ArgumentName}}){{else}}{{#if GrpcMapper}}Transformers.{{GrpcMapper}}.MapToDest({{ArgumentName}}){{else}}{{ArgumentName}}{{/if}}{{/if}}{{/if}}{{/if}}{{/each}} }; + {{/ifne}} + return InvokeAsync((c, o) => c.{{Name}}Async({{#ifne Parameters.Count 0}}__req, {{/ifne}}o), {{#ifne Parameters.Count 0}}__req, {{/ifne}}{{#if HasReturnValue}}{{#if GrpcReturnConverter}}Transformers.{{GrpcReturnConverter}}, {{else}}{{#if GrpcReturnMapper}}Transformers.{{GrpcReturnMapper}}, {{/if}}{{/if}}{{/if}}requestOptions); + } +{{/each}} + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/EntityGrpcAgent_cs.xml b/tools/Beef.CodeGen.Core/Templates/EntityGrpcAgent_cs.xml deleted file mode 100644 index 2d7f3441f..000000000 --- a/tools/Beef.CodeGen.Core/Templates/EntityGrpcAgent_cs.xml +++ /dev/null @@ -1,522 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/EntityGrpcService_cs.hbs b/tools/Beef.CodeGen.Core/Templates/EntityGrpcService_cs.hbs new file mode 100644 index 000000000..0bec34fb5 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/EntityGrpcService_cs.hbs @@ -0,0 +1,70 @@ +{{! Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef }} +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Net; +using System.Threading.Tasks; +using Beef; +using Beef.Grpc; +using Grpc.Core; +using Microsoft.AspNetCore.Authorization; +using {{Root.Company}}.{{Root.AppName}}.Business; +using {{Root.Company}}.{{Root.AppName}}.Common.Grpc; +using {{Root.Company}}.{{Root.AppName}}.Common.Grpc.Proto; +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.{{Root.ApiName}}.Grpc +{ + /// + /// Provides the {{{EntityNameSeeComments}}} gRPC Server functionality. + /// + [{{#if WebApiAuthorize}}Authorize{{else}}AllowAnonymous{{/if}}] + public partial class {{Name}}Service : {{Name}}GrpcService.{{Name}}GrpcServiceBase + { + private readonly I{{Name}}Manager _manager; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public {{Name}}Service(I{{Name}}Manager manager) + { _manager = Check.NotNull(manager, nameof(manager)); ServiceCtor(); } + + partial void ServiceCtor(); // Enables additional functionality to be added to the constructor. + +{{#each GrpcOperations}} + {{#unless @first}} + + {{/unless}} + /// + /// {{{SummaryText}}} + /// + /// The . + /// The . + /// The {{#if HasReturnValue}}{{seecomments ReturnType}}{{else}}{{seecomments 'Google.Protobuf.WellKnownTypes.Empty'}}{{/if}}. + [{{#if WebApiAuthorize}}Authorize{{else}}AllowAnonymous{{/if}}] + public override Task<{{#if HasReturnValue}}{{ReturnType}}{{else}}Google.Protobuf.WellKnownTypes.Empty{{/if}}> {{Name}}({{Parent.Name}}{{Name}}Request request, ServerCallContext context) + { + return new GrpcService<{{#if HasReturnValue}}{{ReturnType}}{{else}}Google.Protobuf.WellKnownTypes.Empty{{/if}}>(context, async () => + { + var __req = request ?? new {{Parent.Name}}{{Name}}Request(); + {{#if HasReturnValue}}var __result = {{/if}}await _manager.{{Name}}Async({{#each Parameters}}{{#unless @first}}, {{/unless}}{{#if IsValueArg}}Transformers.{{Type}}.MapToSrce(__req.Value)!{{else}}{{#if IsPagingArgs}}__req.Paging == null ? new Entities.PagingArgs() : Transformers.PagingArgsToPagingArgsConverter.ConvertToSrce(__req.Paging){{else}}{{#if GrpcConverter}}Transformers.{{GrpcConverter}}.ConvertToSrce(__req.{{Name}}){{else}}{{#if GrpcMapper}}Transformers.{{GrpcMapper}}.MapToSrce(__req.{{Name}})!{{else}}__req.{{Name}}{{/if}}{{/if}}{{/if}}{{/if}}{{/each}}); + return {{#if HasReturnValue}}Transformers.{{ReturnType}}.MapToDest(__result!)!;{{else}}new Google.Protobuf.WellKnownTypes.Empty();{{/if}} + }, operationType: OperationType.{{WebApiOperationType}}, statusCode: HttpStatusCode.{{WebApiStatus}}{{#if HasReturnValue}}, alternateStatusCode: {{#ifeq WebApiAlternateStatus 'ThrowException'}}null{{else}}HttpStatusCode.{{WebApiAlternateStatus}}{{/ifeq}}{{/if}}).ExecuteAsync(); + } +{{/each}} + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/EntityGrpcService_cs.xml b/tools/Beef.CodeGen.Core/Templates/EntityGrpcService_cs.xml deleted file mode 100644 index cf0cbb1c4..000000000 --- a/tools/Beef.CodeGen.Core/Templates/EntityGrpcService_cs.xml +++ /dev/null @@ -1,279 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/EntityManager_cs.hbs b/tools/Beef.CodeGen.Core/Templates/EntityManager_cs.hbs new file mode 100644 index 000000000..716571033 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/EntityManager_cs.hbs @@ -0,0 +1,158 @@ +{{! Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef }} +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Business; +using Beef.Entities; +using Beef.Validation; +{{#ifeq Root.EntityUsing 'Common' 'All'}} +using {{Root.Company}}.{{Root.AppName}}.Common.Entities; +{{/ifeq}} +{{#ifeq Root.EntityUsing 'Business' 'All'}} +using {{Root.Company}}.{{Root.AppName}}.Business.Entities; +{{/ifeq}} +using {{Root.Company}}.{{Root.AppName}}.Business.DataSvc; +{{#if UsesValidators}} +using {{Root.Company}}.{{Root.AppName}}.{{Root.ValidatorLayer}}.Validation; +{{/if}} +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.Business +{ + /// + /// Provides the {{{EntityNameSeeComments}}} business functionality. + /// + public partial class {{Name}}Manager{{#if GenericWithT}}{{/if}} : I{{Name}}Manager{{#if GenericWithT}}{{/if}} + { +{{#if RequiresDataSvc}} + private readonly I{{Name}}DataSvc _dataService; + +{{/if}} +{{#if ManagerExtensions}} + #region Extensions + #pragma warning disable CS0649, IDE0044 // Defaults to null by design; can be overridden in constructor. + + {{#each ManagerAutoOperations}} + {{#unless SingleValidateParameters}} + private Func<{{#each Parameters}}{{{ParameterType}}}, {{/each}}Task>? {{PrivateName}}OnPreValidateAsync; + private Action? {{PrivateName}}OnValidate; + {{/unless}} + private Func<{{#each Parameters}}{{{ParameterType}}}, {{/each}}Task>? {{PrivateName}}OnBeforeAsync; + private Func<{{#if HasReturnValue}}{{OperationReturnType}}, {{/if}}{{#each ValueLessParameters}}{{{ParameterType}}}, {{/each}}Task>? {{PrivateName}}OnAfterAsync; + + {{/each}} + #pragma warning restore CS0649, IDE0044 + #endregion + +{{/if}} + /// + /// Initializes a new instance of the class. + /// +{{#if RequiresDataSvc}} + /// The . + {{lower ManagerConstructor}} {{Name}}Manager(I{{Name}}DataSvc dataService) + { _dataService = Check.NotNull(dataService, nameof(dataService)); {{Name}}ManagerCtor(); } +{{else}} + {{lower ManagerConstructor}} {{Name}}Manager() => {{Name}}ManagerCtor(); +{{/if}} + + partial void {{Name}}ManagerCtor(); // Enables additional functionality to be added to the constructor. + +{{#each ManagerOperations}} + {{#unless @first}} + + {{/unless}} + /// + /// {{{SummaryText}}} + /// + {{#each Parameters}} + /// {{{SummaryText}}} + {{/each}} + {{#if HasReturnValue}} + /// {{{ReturnText}}} + {{/if}} + public {{{OperationTaskReturnType}}} {{Name}}Async({{#each Parameters}}{{#unless @first}}, {{/unless}}{{ParameterType}} {{ArgumentName}}{{/each}}) + { + {{#ifeq Type 'Create' 'Update'}} + value.Validate(nameof(value)).Mandatory().Run().ThrowOnError(); + + {{/ifeq}} + return ManagerInvoker.Current.InvokeAsync(this, async () => + { + {{#ifval AuthRole}} + ExecutionContext.Current.IsInRole("{{AuthRole}}", true); + {{/ifval}} + {{#ifval AuthPermission}} + ExecutionContext.Current.IsAuthorized("{{AuthPermission}}", true); + {{/ifval}} + ExecutionContext.Current.OperationType = OperationType.{{WebApiOperationType}}; + {{#each Parameters}} + {{#ifeq LayerPassing 'ToManagerSet'}} + value.{{Name}} = {{ArgumentName}}; + {{/ifeq}} + {{#ifeq LayerPassing 'ToManagerCollSet'}} + value?.ForEach(__item => __item.{{Name}} = {{ArgumentName}}); + {{/ifeq}} + {{/each}} + {{#if ManagerCustom}} + {{#if HasReturnValue}}return Cleaner.Clean({{/if}}await {{Name}}OnImplementationAsync({{#each Parameters}}{{#unless @first}}, {{/unless}}{{ArgumentName}}{{/each}}).ConfigureAwait(false){{#if HasReturnValue}}){{/if}}; + {{else}} + {{#ifne CleanerParameters.Count 0}} + Cleaner.CleanUp({{#each CleanerParameters}}{{#unless @first}}, {{/unless}}{{ArgumentName}}{{/each}}); + {{/ifne}} + {{#if Parent.ManagerExtensions}} + {{#unless SingleValdiateParameters}} + if ({{PrivateName}}OnPreValidateAsync != null) await {{PrivateName}}OnPreValidateAsync({{#each Parameters}}{{#unless @first}}, {{/unless}}{{{ArgumentName}}}{{/each}}).ConfigureAwait(false); + + {{/unless}} + {{/if}} + {{#if SingleValidateParameters}} + {{#each ValidateParameters}} + {{ArgumentName}}.Validate(nameof({{ArgumentName}})){{#if IsMandatory}}.Mandatory(){{/if}}{{#ifval Validator}}.Entity({{Validator}}.Default){{/ifval}}{{#ifval ValidatorCode}}.{{ValidatorCode}}{{/ifval}}.Run().ThrowOnError(); + {{/each}} + {{else}} + MultiValidator.Create() + {{#each ValidateParameters}} + .Add({{ArgumentName}}.Validate(nameof({{ArgumentName}})){{#if IsMandatory}}.Mandatory(){{/if}}{{#ifval Validator}}.Entity({{Validator}}.Default){{/ifval}}{{#ifval ValidatorCode}}.{{ValidatorCode}}{{/ifval}}) + {{/each}} + {{#if Parent.ManagerExtensions}} + {{#unless SingleValidateParameters}} + .Additional((__mv) => {{PrivateName}}OnValidate?.Invoke(__mv{{#each Parameters}}, {{ArgumentName}}{{/each}})) + {{/unless}} + {{/if}} + .Run().ThrowOnError(); + + {{/if}} + {{#if Parent.ManagerExtensions}} + if ({{PrivateName}}OnBeforeAsync != null) await {{PrivateName}}OnBeforeAsync({{#each Parameters}}{{#unless @first}}, {{/unless}}{{{ArgumentName}}}{{/each}}).ConfigureAwait(false); + {{#if HasReturnValue}}var __result = {{/if}}await _dataService.{{Name}}Async({{#each DataParameters}}{{#unless @first}}, {{/unless}}{{{ArgumentName}}}{{/each}}).ConfigureAwait(false); + if ({{PrivateName}}OnAfterAsync != null) await {{PrivateName}}OnAfterAsync({{#if HasReturnValue}}__result{{/if}}{{#each ValueLessParameters}}{{#if @first}}{{#if Parent.HasReturnValue}}, {{/if}}{{else}}, {{/if}}{{{ArgumentName}}}{{/each}}).ConfigureAwait(false); + {{#if HasReturnValue}} + return Cleaner.Clean(__result); + {{/if}} + {{else}} + {{#if HasReturnValue}}return Cleaner.Clean({{/if}}await _dataService.{{Name}}Async({{#each DataParameters}}{{#unless @first}}, {{/unless}}{{{ArgumentName}}}{{/each}}).ConfigureAwait(false){{#if HasReturnValue}}){{/if}}; + {{/if}} + {{/if}} + }{{#if ManagerTransaction}}, new BusinessInvokerArgs { IncludeTransactionScope = true }{{/if}}); + } +{{/each}} + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/EntityManager_cs.xml b/tools/Beef.CodeGen.Core/Templates/EntityManager_cs.xml deleted file mode 100644 index fdcdbe5e0..000000000 --- a/tools/Beef.CodeGen.Core/Templates/EntityManager_cs.xml +++ /dev/null @@ -1,770 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/EntityWebApiAgent_cs.hbs b/tools/Beef.CodeGen.Core/Templates/EntityWebApiAgent_cs.hbs new file mode 100644 index 000000000..38274fcc2 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/EntityWebApiAgent_cs.hbs @@ -0,0 +1,101 @@ +{{! Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef }} +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Beef.Entities; +using Beef.WebApi; +using Newtonsoft.Json.Linq; +using {{Root.Company}}.{{Root.AppName}}.Common.Entities; +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.Common.Agents +{ + /// + /// Defines the {{{EntityNameSeeComments}}} Web API agent. + /// + public partial interface I{{Name}}Agent + { +{{#each WebApiAgentOperations}} + {{#unless @first}} + + {{/unless}} + /// + /// {{{SummaryText}}} + /// + {{#ifeq Type 'Patch'}} + /// The . + {{/ifeq}} + {{#each Parameters}} + /// {{{SummaryText}}} + {{/each}} + /// The optional . + /// A . + {{{AgentOperationTaskReturnType}}} {{Name}}Async({{#ifeq Type 'Patch'}}WebApiPatchOption patchOption, {{/ifeq}}{{#each Parameters}}{{WebApiAgentParameterType}} {{ArgumentName}}{{#if IsPagingArgs}} = null{{/if}}, {{/each}}WebApiRequestOptions? requestOptions = null); +{{/each}} + } + + /// + /// Provides the {{{EntityNameSeeComments}}} Web API agent. + /// + public partial class {{Name}}Agent : WebApiAgentBase, I{{Name}}Agent + { + /// + /// Initializes a new instance of the class. + /// + /// The . + public {{Name}}Agent(IWebApiAgentArgs args) : base(args) { } +{{#each WebApiAgentOperations}} + + /// + /// {{{SummaryText}}} + /// + {{#ifeq Type 'Patch'}} + /// The . + {{/ifeq}} + {{#each Parameters}} + /// {{{SummaryText}}} + {{/each}} + /// The optional . + /// A . + public {{{AgentOperationTaskReturnType}}} {{Name}}Async({{#ifeq Type 'Patch'}}WebApiPatchOption patchOption, {{/ifeq}}{{#each Parameters}}{{WebApiAgentParameterType}} {{ArgumentName}}{{#if IsPagingArgs}} = null{{/if}}, {{/each}}WebApiRequestOptions? requestOptions = null) => + {{#ifeq WebApiMethod 'HttpGet'}} + {{#ifeq Type 'GetColl'}} + GetCollectionResultAsync<{{BaseReturnType}}CollectionResult, {{BaseReturnType}}Collection, {{BaseReturnType}}>("{{Parent.WebApiRoutePrefix}}{{#ifval WebApiRoute}}{{#ifne WebApiRoute ''}}/{{WebApiRoute}}{{/ifne}}{{/ifval}}", requestOptions: requestOptions, + {{else}} + GetAsync{{#if HasReturnValue}}<{{OperationReturnType}}>{{/if}}("{{Parent.WebApiRoutePrefix}}{{#ifval WebApiRoute}}{{#ifne WebApiRoute ''}}/{{WebApiRoute}}{{/ifne}}{{/ifval}}", requestOptions: requestOptions, + {{/ifeq}} + {{/ifeq}} + {{#ifeq WebApiMethod 'HttpPost'}} + PostAsync{{#if HasReturnValue}}<{{OperationReturnType}}>{{/if}}("{{Parent.WebApiRoutePrefix}}{{#ifval WebApiRoute}}{{#ifne WebApiRoute ''}}/{{WebApiRoute}}{{/ifne}}{{/ifval}}", {{#if HasValue}}Beef.Check.NotNull(value, nameof(value)), {{/if}}requestOptions: requestOptions, + {{/ifeq}} + {{#ifeq WebApiMethod 'HttpPut'}} + PutAsync{{#if HasReturnValue}}<{{OperationReturnType}}>{{/if}}("{{Parent.WebApiRoutePrefix}}{{#ifval WebApiRoute}}{{#ifne WebApiRoute ''}}/{{WebApiRoute}}{{/ifne}}{{/ifval}}", {{#if HasValue}}Beef.Check.NotNull(value, nameof(value)), {{/if}}requestOptions: requestOptions, + {{/ifeq}} + {{#ifeq WebApiMethod 'HttpPatch'}} + PatchAsync{{#if HasReturnValue}}<{{OperationReturnType}}>{{/if}}("{{Parent.WebApiRoutePrefix}}{{#ifval WebApiRoute}}{{#ifne WebApiRoute ''}}/{{WebApiRoute}}{{/ifne}}{{/ifval}}", patchOption, {{#if HasValue}}Beef.Check.NotNull(value, nameof(value)), {{/if}}requestOptions: requestOptions, + {{/ifeq}} + {{#ifeq WebApiMethod 'HttpDelete'}} + DeleteAsync{{#if HasReturnValue}}<{{OperationReturnType}}>{{/if}}("{{Parent.WebApiRoutePrefix}}{{#ifval WebApiRoute}}{{#ifne WebApiRoute ''}}/{{WebApiRoute}}{{/ifne}}{{/ifval}}", {{/if}}requestOptions: requestOptions, + {{/ifeq}} + {{#ifeq ValueLessParameters.Count 0}} + args: Array.Empty()); + {{else}} + args: new WebApiArg[] { {{#each CoreParameters}}{{#unless @first}}, {{/unless}}new WebApiArg<{{WebApiAgentParameterType}}>("{{ArgumentName}}", {{ArgumentName}}{{#ifval WebApiAgentFrom}}, WebApiArgType.{{WebApiAgentFrom}}{{/ifval}}){{/each}}{{#if Paging}}{{ifne CoreParameters.Count 0}}, {{/ifne}}new WebApiPagingArgsArg("paging", paging){{/if}} }); + {{/ifeq}} +{{/each}} + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/EntityWebApiAgent_cs.xml b/tools/Beef.CodeGen.Core/Templates/EntityWebApiAgent_cs.xml deleted file mode 100644 index 7b8595c99..000000000 --- a/tools/Beef.CodeGen.Core/Templates/EntityWebApiAgent_cs.xml +++ /dev/null @@ -1,568 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/EntityWebApiController_cs.hbs b/tools/Beef.CodeGen.Core/Templates/EntityWebApiController_cs.hbs new file mode 100644 index 000000000..fb1f5037a --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/EntityWebApiController_cs.hbs @@ -0,0 +1,120 @@ +{{! Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef }} +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Net; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json.Linq; +using Beef; +using Beef.AspNetCore.WebApi; +using Beef.Entities; +using {{Root.Company}}.{{Root.AppName}}.Business; +{{#ifeq Root.EntityUsing 'Business' 'All'}} +using {{Root.Company}}.{{Root.AppName}}.Business.Entities; +{{/ifeq}} +{{#ifeq Root.EntityUsing 'Common' 'All'}} +using {{Root.Company}}.{{Root.AppName}}.Common.Entities; +{{/ifeq}} +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.{{Root.ApiName}}.Controllers +{ + /// + /// Provides the {{{EntityNameSeeComments}}} Web API functionality. + /// + [{{#if WebApiAuthorize}}Authorize{{else}}AllowAnonymous{{/if}}] +{{#ifval WebApiRoutePrefix}} + [Route("{{WebApiRoutePrefix}}")] +{{/ifval}} + public partial class {{Name}}Controller : ControllerBase + { +{{#each WebApiConstructorParameters}} + private readonly {{Type}} {{PrivateName}}; + {{#if @last}} + + {{/if}} +{{/each}} + /// + /// Initializes a new instance of the class. + /// +{{#each WebApiConstructorParameters}} + /// {{{SummaryText}}} +{{/each}} + {{lower WebApiConstructor}} {{Name}}Controller({{#each WebApiConstructorParameters}}{{#unless @first}}, {{/unless}}{{Type}} {{ArgumentName}}{{/each}}) + { {{#each WebApiConstructorParameters}}{{PrivateName}} = Check.NotNull({{ArgumentName}}, nameof({{ArgumentName}})); {{/each}}{{Name}}ControllerCtor(); } + + partial void {{Name}}ControllerCtor(); // Enables additional functionality to be added to the constructor. + +{{#each WebApiOperations}} + {{#unless @first}} + + {{/unless}} + /// + /// {{{SummaryText}}} + /// + {{#each PagingLessParameters}} + {{#ifeq WebApiFrom 'FromEntityProperties'}} + {{#each RelatedEntity.Properties}} + /// {{{ParameterSummaryText}}} + {{/each}} + {{else}} + /// {{{SummaryText}}} + {{/ifeq}} + {{/each}} + {{#if HasReturnValue}} + /// {{{WebApiReturnText}}} + {{/if}} + [{{#if WebApiAuthorize}}Authorize{{else}}AllowAnonymous{{/if}}] + [{{WebApiMethod}}("{{WebApiRoute}}")] + [ProducesResponseType({{#if HasReturnValue}}typeof({{#ifeq Type 'GetColl'}}{{BaseReturnType}}Collection{{else}}{{BaseReturnType}}{{/ifeq}}), {{/if}}(int)HttpStatusCode.{{WebApiStatus}})] + {{#if HasReturnValue}} + {{#ifne WebApiAlternateStatus 'ThrowException'}} + [ProducesResponseType((int)HttpStatusCode.{{WebApiAlternateStatus}})] + {{/ifne}} + {{/if}} + public IActionResult {{Name}}({{#each PagingLessParameters}}{{#unless @first}}, {{/unless}}{{#ifeq WebApiFrom 'FromEntityProperties'}}{{#each RelatedEntity.Properties}}{{#unless @first}}, {{/unless}}{{#ifne ArgumentName JsonName}}[FromQuery(Name = "{{JsonName}}")] {{/ifne}}{{{WebApiParameterType}}} {{ArgumentName}} = {{#ifval Default}}{{Default}}{{else}}default{{/ifval}}{{/each}}{{else}}{{#ifne WebApiFrom 'FromQuery'}}[{{WebApiFrom}}] {{/ifne}}{{WebApiParameterType}} {{ArgumentName}}{{#ifval Default}} = {{Default}}{{/ifval}}{{/ifeq}}{{/each}}) + { + {{#each Parameters}} + {{#ifeq WebApiFrom 'FromEntityProperties'}} + var {{ArgumentName}} = new {{Type}} { {{#each RelatedEntity.Properties}}{{#unless @first}}, {{/unless}}{{PropertyName}} = {{#ifval WebApiQueryStringConverter}}new {{WebApiQueryStringConverter}}().ConvertToSrce({{ArgumentName}}){{else}}{{ArgumentName}}{{/ifval}}{{/each}} }; + {{/ifeq}} + {{/each}} + {{#ifeq WebApiMethod 'HttpGet'}} + {{#ifeq Type 'GetColl'}} + return new WebApiGet<{{BaseReturnType}}CollectionResult, {{BaseReturnType}}Collection, {{BaseReturnType}}>(this, () => _manager.{{Name}}Async({{#each Parameters}}{{#unless @first}}, {{/unless}}{{#if IsPagingArgs}}WebApiQueryString.CreatePagingArgs(this){{else}}{{ArgumentName}}{{/if}}{{/each}}), + {{else}} + return new WebApiGet<{{OperationReturnType}}>(this, () => _manager.{{Name}}Async({{#each Parameters}}{{#unless @first}}, {{/unless}}{{#if IsValueArg}}WebApiActionBase.Value({{ArgumentName}}){{else}}{{#if IsPagingArgs}}WebApiQueryString.CreatePagingArgs(this){{else}}{{ArgumentName}}{{/if}}{{/if}}{{/each}}), + {{/ifeq}} + {{/ifeq}} + {{#ifeq WebApiMethod 'HttpPost'}} + return new WebApiPost{{#if HasReturnValue}}<{{OperationReturnType}}>{{/if}}(this, () => _manager.{{Name}}Async({{#each Parameters}}{{#unless @first}}, {{/unless}}{{#if IsValueArg}}WebApiActionBase.Value({{ArgumentName}}){{else}}{{#if IsPagingArgs}}WebApiQueryString.CreatePagingArgs(this){{else}}{{ArgumentName}}{{/if}}{{/if}}{{/each}}), + {{/ifeq}} + {{#ifeq WebApiMethod 'HttpPut'}} + return new WebApiPut{{#if HasReturnValue}}<{{OperationReturnType}}>{{/if}}(this, () => _manager.{{Name}}Async({{#each Parameters}}{{#unless @first}}, {{/unless}}{{#if IsValueArg}}WebApiActionBase.Value({{ArgumentName}}){{else}}{{#if IsPagingArgs}}WebApiQueryString.CreatePagingArgs(this){{else}}{{ArgumentName}}{{/if}}{{/if}}{{/each}}), + {{/ifeq}} + {{#ifeq WebApiMethod 'HttpPatch'}} + return new WebApiPatch{{#if HasReturnValue}}<{{OperationReturnType}}>{{/if}}(this, value, () => {{PatchGetVariable}}.{{PatchGetOperation}}Async({{#each ValueLessParameters}}{{#unless @first}}, {{/unless}}{{ArgumentName}}{{/each}}), (__value) => {{PatchUpdateVariable}}.{{PatchUpdateOperation}}Async(__value{{#each ValueLessParameters}}, {{ArgumentName}}{{/each}}), + {{/ifeq}} + {{#ifeq WebApiMethod 'HttpDelete'}} + return new WebApiDelete{{#if HasReturnValue}}<{{OperationReturnType}}>{{/if}}(this, () => _manager.{{Name}}Async({{#each Parameters}}{{#unless @first}}, {{/unless}}{{#if IsValueArg}}WebApiActionBase.Value({{ArgumentName}}){{else}}{{#if IsPagingArgs}}WebApiQueryString.CreatePagingArgs(this){{else}}{{ArgumentName}}{{/if}}{{/if}}{{/each}}), + {{/ifeq}} + operationType: OperationType.{{WebApiOperationType}}, statusCode: HttpStatusCode.{{WebApiStatus}}{{#if HasReturnValue}}, alternateStatusCode: {{#ifeq WebApiAlternateStatus 'ThrowException'}}null{{else}}HttpStatusCode.{{WebApiAlternateStatus}}{{/ifeq}}{{/if}}); + } +{{/each}} + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/EntityWebApiCoreController_cs.xml b/tools/Beef.CodeGen.Core/Templates/EntityWebApiCoreController_cs.xml deleted file mode 100644 index 800cf0e78..000000000 --- a/tools/Beef.CodeGen.Core/Templates/EntityWebApiCoreController_cs.xml +++ /dev/null @@ -1,805 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/Entity_cs.hbs b/tools/Beef.CodeGen.Core/Templates/Entity_cs.hbs new file mode 100644 index 000000000..b6269bf5d --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/Entity_cs.hbs @@ -0,0 +1,586 @@ +{{! Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef }} +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +{{! ===== Using ===== }} +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +using Beef.RefData; +{{#ifeq JsonSerializer 'Newtonsoft'}} +using Newtonsoft.Json; +{{/ifeq}} +{{#if Root.UsingNamespace1}} +using {{Root.UsingNamespace1}}; +{{/if}} +{{#if Root.UsingNamespace2}} +using {{Root.UsingNamespace2}}; +{{/if}} +{{#if Root.UsingNamespace3}} +using {{Root.UsingNamespace3}}; +{{/if}} +{{#ifeq Root.EntityUsing 'Common' 'All'}} + {{#ifeq EntityScope 'Business'}} +using {{Root.Company}}.{{Root.AppName}}.Common.Entities; + {{/ifeq}} +{{/ifeq}} +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataBusNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +{{! ===== Class ===== }} +namespace {{Root.Company}}.{{Root.AppName}}.{{Root.EntityScope}}.Entities{{#ifval Namespace}}.{{Namespace}}{{/ifval}} +{ + /// + /// Represents the {{{Text}}} entity. + /// +{{#ifeq JsonSerializer 'Newtonsoft'}} + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] +{{/ifeq}} +{{#ifval RefDataType}} + [ReferenceDataInterface(typeof(IReferenceData))] +{{/ifval}} + public {{#if Abstract}}abstract {{/if}}partial class {{{EntityName}}} : {{{EntityInherits}}}{{#ifval EntityImplements}}, {{{EntityImplements}}}{{/ifval}} + { +{{! ===== Constants ===== }} +{{#each Consts}} + {{#if @first}} + #region Constants + + {{/if}} + /// + /// {{{SummaryText}}} + /// + public const {{Parent.ConstType}} {{Name}} = {{{FormattedValue}}}; + {{#if @last}} + + #endregion + + {{/if}} +{{/each}} +{{! ===== Privates ===== }} +{{#each PrivateProperties}} + {{#if @first}} + #region Privates + + {{/if}} + private {{{PrivateType}}} {{PropertyPrivateName}}{{#ifval Default}} = {{Default}}{{/ifval}}; + {{#ifval RefDataType}} + {{#unless RefDataList}} + {{#if RefDataText}} + private string? {{PrivateName}}Text; + {{/if}} + {{/unless}} + {{/ifval}} + {{#if @last}} + + #endregion + + {{/if}} +{{/each}} +{{! ===== Properties ===== }} +{{#each CoreProperties}} + {{#if @first}} + #region Properties + {{/if}} + {{#ifval RefDataType}} + + /// + /// {{{SummaryRefDataSid}}} + /// + {{#ifeq Root.JsonSerializer 'Newtonsoft'}} + {{#unless SerializationIgnore}} + [JsonProperty("{{JsonName}}", DefaultValueHandling = {{#if SerializationEmitDefault}}DefaultValueHandling.Include{{else}}DefaultValueHandling.Ignore{{/if}})] + {{/unless}} + {{/ifeq}} + {{#ifval DisplayName}} + [Display(Name="{{DisplayName}}")] + {{/ifval}} + public {{{PrivateType}}} {{PropertyName}} + { + get => {{PropertyPrivateName}}; + set => SetValue(ref {{PropertyPrivateName}}, value, {{lower Immutable}}, {{#if RefDataList}}false{{else}}{{ifeq RefDataType 'string'}}StringTrim.UseDefault, StringTransform.UseDefault{{else}}false{{/ifeq}}{{/if}}, nameof({{Name}})); + } + {{#unless SerializationIgnore}} + {{#unless RefDataList}} + {{#if RefDataText}} + + /// + /// {{{SummaryRefDataText}}} + /// + {{#ifeq Root.JsonSerializer 'Newtonsoft'}} + [JsonProperty("{{JsonName}}Text", DefaultValueHandling = DefaultValueHandling.Ignore)] + {{/ifeq}} + public string? {{Name}}Text { get => {{PrivateName}}Text ?? GetRefDataText(() => {{Name}}); set => {{PrivateName}}Text = value; } + {{/if}} + {{/unless}} + {{/unless}} + + /// + /// {{{SummaryText}}} + /// + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + {{#ifval DisplayName}} + [Display(Name="{{DisplayName}}")] + {{/ifval}} + {{#ifval Annotation1}} + {{Annotation1}} + {{/ifval}} + {{#ifval Annotation2}} + {{Annotation2}} + {{/ifval}} + {{#ifval Annotation3}} + {{Annotation3}} + {{/ifval}} + {{#if RefDataList}} + public ReferenceDataSidList<{{{Type}}}, {{RefDataType}}>? {{Name}} + { + get => new ReferenceDataSidList<{{{Type}}}, {{RefDataType}}>(ref {{PropertyPrivateName}}); + set => SetValue(ref {{PropertyPrivateName}}, value?.ToSidList(), {{lower Immutable}}, false, nameof({{Name}}){{#ifval SecondaryPropertyChanged}}, {{SecondaryPropertyChanged}}{{/ifval}}); + } + {{else}} + public {{{PropertyType}}} {{Name}} + { + get => {{PropertyPrivateName}}; + set => SetValue(ref {{PropertyPrivateName}}, value, {{lower Immutable}}, false, nameof({{Name}}){{#ifval SecondaryPropertyChanged}}, {{SecondaryPropertyChanged}}{{/ifval}}); + } + {{/if}} + {{else}} + + /// + /// {{{SummaryText}}} + /// + {{#ifeq Root.JsonSerializer 'Newtonsoft'}} + {{#unless SerializationIgnore}} + [JsonProperty("{{JsonName}}", DefaultValueHandling = {{#if SerializationEmitDefault}}DefaultValueHandling.Include{{else}}DefaultValueHandling.Ignore{{/if}})] + {{/unless}} + {{/ifeq}} + {{#ifval DisplayName}} + [Display(Name="{{DisplayName}}")] + {{/ifval}} + {{#ifval WebApiQueryStringConverter}} + [Beef.WebApi.WebApiArgFormatter(typeof({{WebApiQueryStringConverter}}))] + {{/ifval}} + {{#ifval Annotation1}} + {{Annotation1}} + {{/ifval}} + {{#ifval Annotation2}} + {{Annotation2}} + {{/ifval}} + {{#ifval Annotation3}} + {{Annotation3}} + {{/ifval}} + public {{{PropertyType}}} {{Name}} + { + {{#if RefDataMapping}} + get => GetMapping(nameof({{Name}})); + set { var _{{PropertyPrivateName}} = GetMapping<{{{PropertyType}}}>(nameof({{Name}})) ?? default; SetValue(ref _{{PropertyPrivateName}}, value, {{lower Immutable}}, {{#if RefDataList}}false{{else}}{{ifeq Type 'string'}}StringTrim.UseDefault, StringTransform.UseDefault{{else}}false{{/ifeq}}{{/if}}, nameof({{Name}}{{#ifval SecondaryPropertyChanged}}, SecondaryPropertyChanged{{/ifval}})); SetMapping(nameof({{Name}}), _{{PropertyPrivateName}}!); } + {{else}} + get => {{#if AutoCreate}}GetAutoValue(ref {{/if}}{{PropertyPrivateName}}{{#if AutoCreate}}){{/if}}; + set => SetValue(ref {{PropertyPrivateName}}, value, {{lower Immutable}}, {{#ifeq Type 'string'}}StringTrim.{{StringTrim}}, StringTransform.{{StringTransform}}{{else}}{{#ifeq Type 'DateTime'}}DateTimeTransform.{{DateTimeTransform}}{{else}}{{lower BubblePropertyChanged}}{{/ifeq}}{{/ifeq}}, nameof({{Name}}){{#ifval SecondaryPropertyChanged}}, {{SecondaryPropertyChanged}}{{/ifval}}); + {{/if}} + } + {{/ifval}} + {{#if @last}} + + #endregion + + {{/if}} +{{/each}} +{{! ===== IChangeTracking ===== }} +{{#ifne EntityProperties.Count 0}} + #region IChangeTracking + + /// + /// Resets the entity state to unchanged by accepting the changes (resets ). + /// + /// Ends and commits the entity changes (see ). + public override void AcceptChanges() + { +{{#each EntityProperties}} + {{Name}}?.AcceptChanges(); +{{/each}} + base.AcceptChanges(); + } + + /// + /// Determines that until is invoked property changes are to be logged (see ). + /// + public override void TrackChanges() + { +{{#each EntityProperties}} + {{Name}}?.TrackChanges(); +{{/each}} + base.TrackChanges(); + } + + #endregion + +{{/ifne}} +{{! ===== UniqueKey ===== }} +{{#unless RefDataType}} + {{#ifne UniqueKeyProperties.Count 0}} + #region IUniqueKey + + /// + /// Indicates whether the {{{EntityNameSeeComments}}} has a value. + /// + public override bool HasUniqueKey => true; + + /// + /// Gets the list of property names that represent the unique key. + /// + public override string[] UniqueKeyProperties => new string[] { {{#each UniqueKeyProperties}}{{#unless @first}}, {{/unless}}nameof({{PropertyName}}){{/each}} }; + + /// + /// Creates the . + /// + /// The . + {{#each UniqueKeyProperties}} + /// The {{{PropertyNameSeeComments}}}. + {{/each}} + public static UniqueKey CreateUniqueKey({{#each UniqueKeyProperties}}{{#unless @first}}, {{/unless}}{{{PropertyType}}} {{PropertyArgumentName}}{{/each}}) => new UniqueKey({{#each UniqueKeyProperties}}{{#unless @first}}, {{/unless}}{{PropertyArgumentName}}{{/each}}); + + /// + /// Gets the (consists of the following property(s): {{#each UniqueKeyProperties}}{{#unless @first}}, {{/unless}}{{{PropertyNameSeeComments}}}{{/each}}). + /// + public override UniqueKey UniqueKey => new UniqueKey({{#each UniqueKeyProperties}}{{#unless @first}}, {{/unless}}{{PropertyName}}{{/each}}); + + #endregion + + {{/ifne}} +{{/unless}} +{{! ===== Operator ===== }} +{{#ifval RefDataType}} + {{#unless Abstract}} + #region Operator + + /// + /// An implicit cast from an Id to a {{{EntityNameSeeComments}}}. + /// + /// The Id. + /// The corresponding {{{EntityNameSeeComments}}}. + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator {{{EntityName}}}({{RefDataType}} id) => ConvertFromId<{{{EntityName}}}>(id); + + /// + /// An implicit cast from a Code to a {{{EntityNameSeeComments}}}. + /// + /// The Code. + /// The corresponding {{{EntityNameSeeComments}}}. + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator {{{EntityName}}}(string? code) => ConvertFromCode<{{{EntityName}}}>(code); + + #endregion + + {{/unless}} +{{/ifval}} +{{! ===== IEquatable (although the code generated looks a little funky this is required to function correctly.) ===== }} +{{#unless RefDataType}} + #region IEquatable + + /// + /// Determines whether the specified object is equal to the current object by comparing the values of all the properties. + /// + /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. + public override bool Equals(object? obj) => obj is {{{EntityName}}} val && Equals(val); + + /// + /// Determines whether the specified {{{EntityNameSeeComments}}} is equal to the current {{{EntityNameSeeComments}}} by comparing the values of all the properties. + /// + /// The {{{EntityNameSeeComments}}} to compare with the current {{{EntityNameSeeComments}}}. + /// true if the specified {{{EntityNameSeeComments}}} is equal to the current {{{EntityNameSeeComments}}}; otherwise, false. + public bool Equals({{EntityName}}? value) + { + if (value == null) + return false; + else if (ReferenceEquals(value, this)) + return true; + + return base.Equals((object)value){{#ifeq CoreProperties.Count 0}};{{/ifeq}} + {{#each CoreProperties}} + && Equals({{PropertyName}}, value.{{PropertyName}}){{#if @last}};{{/if}} + {{/each}} + } + + /// + /// Compares two {{{EntityNameSeeComments}}} types for equality. + /// + /// {{{EntityNameSeeComments}}} A. + /// {{{EntityNameSeeComments}}} B. + /// true indicates equal; otherwise, false for not equal. + public static bool operator == ({{{EntityName}}}? a, {{{EntityName}}}? b) => Equals(a, b); + + /// + /// Compares two {{{EntityNameSeeComments}}} types for non-equality. + /// + /// {{{EntityNameSeeComments}}} A. + /// {{{EntityNameSeeComments}}} B. + /// true indicates not equal; otherwise, false for equal. + public static bool operator != ({{{EntityName}}}? a, {{{EntityName}}}? b) => !Equals(a, b); + + /// + /// Returns the hash code for the {{{EntityNameSeeComments}}}. + /// + /// The hash code for the {{{EntityNameSeeComments}}}. + public override int GetHashCode() + { + var hash = new HashCode(); + {{#each CoreProperties}} + hash.Add({{PropertyName}}); + {{#if @last}} + return base.GetHashCode() ^ hash.ToHashCode(); + } + + #endregion + + {{/if}} + {{/each}} +{{/unless}} +{{! ===== ICopyFrom ===== }} + #region ICopyFrom + + /// + /// Performs a copy from another {{{EntityNameSeeComments}}} updating this instance. + /// + /// The {{{EntityNameSeeComments}}} to copy from. + public override void CopyFrom(object from) + { + var fval = ValidateCopyFromType<{{{EntityName}}}>(from); + CopyFrom(fval); + } + + /// + /// Performs a copy from another {{{EntityNameSeeComments}}} updating this instance. + /// + /// The {{{EntityNameSeeComments}}} to copy from. + public void CopyFrom({{{EntityName}}} from) + { + if (from == null) + throw new ArgumentNullException(nameof(from)); + + CopyFrom(({{EntityInherits}})from); + {{#each CoreProperties}} + {{#if IsEntity}} + {{PropertyName}} = CopyOrClone(from.{{PropertyName}}, {{PropertyName}}); + {{else}} + {{PropertyName}} = from.{{PropertyName}}; + {{/if}} + {{/each}} + + OnAfterCopyFrom(from); + } + + #endregion + +{{! ===== ICloneable ===== }} +{{#unless Abstract}} + #region ICloneable + + /// + /// Creates a deep copy of the {{{EntityNameSeeComments}}}. + /// + /// A deep copy of the {{{EntityNameSeeComments}}}. + public override object Clone() + { + var clone = new {{{EntityName}}}(); + clone.CopyFrom(this); + return clone; + } + + #endregion + +{{/unless}} +{{! ===== ICleanUp ===== }} + #region ICleanUp + + /// + /// Performs a clean-up of the {{{EntityNameSeeComments}}} resetting property values as appropriate to ensure a basic level of data consistency. + /// + public override void CleanUp() + { + base.CleanUp(); +{{#each CoreProperties}} + {{#unless ExcludeCleanUp}} + {{#unless Immutable}} + {{PropertyName}} = Cleaner.Clean({{PropertyName}}{{#ifeq Type 'DateTime'}}, DateTimeTransform.{{DateTimeTransform}}{{/ifeq}}{{#ifeq Type 'string'}}, StringTrim.{{StringTrim}}, StringTransform.{{StringTransform}}{{/ifeq}}); + {{/unless}} + {{/unless}} +{{/each}} + + OnAfterCleanUp(); + } + + /// + /// Indicates whether considered initial; i.e. all properties have their initial value. + /// + /// true indicates is initial; otherwise, false. + public override bool IsInitial + { + get + { +{{#ifnull IsInitialOverride}} + {{#ifne EntityInherits 'EntityBase'}} + if (!base.IsInitial) + return false; + + {{/ifne}} + {{#each CoreProperties}} + {{#unless ExcludeCleanUp}} + {{#if @first}}return{{else}} &&{{/if}} Cleaner.IsInitial({{PropertyName}}){{#if @last}};{{/if}} + {{/unless}} + {{/each}} + {{#ifeq CoreProperties.Count 0}} + return true; + {{/ifeq}} +{{else}} + return {{IsInitialOverride}}; +{{/ifnull}} + } + } + + #endregion + +{{! ===== RefData ===== }} +{{#ifval RefDataType}} + {{#ifval RefDataStringFormat}} + #region RefData + + /// + /// Overrides the default . + /// + /// The format value is '{{{RefDataStringFormat}}}'. + public override string StringFormat => "{{RefDataStringFormat}}"; + + #endregion + + {{/ifval}} +{{/ifval}} +{{! ===== PartialMethods ===== }} + #region PartialMethods + + partial void OnAfterCleanUp(); + + partial void OnAfterCopyFrom({{{EntityName}}} from); + + #endregion + } +{{! ===== Collection ===== }} +{{#if Collection}} + + #region Collection + + /// + /// Represents the {{{EntityNameSeeComments}}} collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class {{{EntityCollectionName}}} : {{{EntityCollectionInherits}}} + { + /// + /// Initializes a new instance of the {{{seecomments EntityCollectionName}}} class. + /// + public {{{EntityCollectionName}}}(){{#ifne RefDataSortOrder 'SortOrder'}} : base(ReferenceDataSortOrder.{{RefDataSortOrder}}){{/ifne}} { } + + /// + /// Initializes a new instance of the {{{seecomments EntityCollectionName}}} class with an entities range. + /// + /// The {{{EntityNameSeeComments}}} entities. + public {{{EntityCollectionName}}}(IEnumerable<{{{EntityName}}}> entities){{#ifne RefDataSortOrder 'SortOrder'}} : this(){{/ifne}} => AddRange(entities); + {{#if CollectionKeyed}} + + /// + /// Gets the key ({{{EntityNameSeeComments}}} ) for the item. + /// + protected override UniqueKey GetKeyForItem({{{EntityName}}} entity) => entity.UniqueKey; + {{/if}} + {{#ifnull RefDataType}} + + /// + /// Creates a deep copy of the {{{seecomments EntityCollectionName}}}. + /// + /// A deep copy of the {{{seecomments EntityCollectionName}}}. + public override object Clone() + { + var clone = new {{{EntityCollectionName}}}(); + foreach (var item in this) + { + clone.Add(({{EntityName}})item.Clone()); + } + + return clone; + } + {{/ifnull}} + {{#if CollectionResult}} + + /// + /// An implicit cast from the {{{seecomments EntityCollectionResultName}}} to a corresponding {{{seecomments EntityCollectionName}}}. + /// + /// The {{{seecomments EntityCollectionResultName}}}. + /// The corresponding {{{seecomments EntityCollectionName}}}. + [SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "Improves useability")] + public static implicit operator {{{EntityCollectionName}}}({{{EntityCollectionResultName}}} result) => result?.Result!; + {{/if}} + } + + #endregion +{{/if}} +{{! ===== CollectionResult ===== }} +{{#if CollectionResult}} + + #region CollectionResult + + /// + /// Represents the {{{EntityNameSeeComments}}} collection result. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public class {{{EntityCollectionResultName}}} : {{{CollectionResultInherits}}} + { + /// + /// Initializes a new instance of the {{{seecomments EntityCollectionResultName}}} class. + /// + public {{{EntityCollectionResultName}}}() { } + + /// + /// Initializes a new instance of the {{{seecomments EntityCollectionResultName}}} class with . + /// + /// The . + public {{{EntityCollectionResultName}}}(PagingArgs? paging) : base(paging) { } + + /// + /// Initializes a new instance of the {{{seecomments EntityCollectionResultName}}} class with a of items to add. + /// + /// A collection containing items to add. + /// The . + public {{{EntityCollectionResultName}}}(IEnumerable<{{{EntityName}}}> collection, PagingArgs? paging = null) : base(paging) => Result.AddRange(collection); + + /// + /// Creates a deep copy of the {{{seecomments EntityCollectionResultName}}}. + /// + /// A deep copy of the {{{seecomments EntityCollectionResultName}}}. + public override object Clone() + { + var clone = new {{{EntityCollectionResultName}}}(); + clone.CopyFrom(this); + return clone; + } + } + + #endregion +{{/if}} +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/Entity_cs.xml b/tools/Beef.CodeGen.Core/Templates/Entity_cs.xml deleted file mode 100644 index 07ccd2396..000000000 --- a/tools/Beef.CodeGen.Core/Templates/Entity_cs.xml +++ /dev/null @@ -1,1335 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/GrpcTransformers_cs.xml b/tools/Beef.CodeGen.Core/Templates/GrpcTransformers_cs.hbs similarity index 59% rename from tools/Beef.CodeGen.Core/Templates/GrpcTransformers_cs.xml rename to tools/Beef.CodeGen.Core/Templates/GrpcTransformers_cs.hbs index 6d93809b1..c9ece3c4b 100644 --- a/tools/Beef.CodeGen.Core/Templates/GrpcTransformers_cs.xml +++ b/tools/Beef.CodeGen.Core/Templates/GrpcTransformers_cs.hbs @@ -1,8 +1,4 @@ - - - \ No newline at end of file +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/Grpc_proto.hbs b/tools/Beef.CodeGen.Core/Templates/Grpc_proto.hbs new file mode 100644 index 000000000..a654a70f5 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/Grpc_proto.hbs @@ -0,0 +1,95 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +syntax = "proto3"; +package {{camel Root.Company}}.{{camel Root.AppName}}.common.grpc.proto; +import "google/protobuf/wrappers.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/empty.proto"; + +/* +* Common... +*/ + +message PagingArgs { + int64 skip = 1; + int64 take = 2; + bool getCount = 3; +} + +message PagingResult { + int64 skip = 1; + int64 take = 2; + google.protobuf.Int64Value totalCount = 3; +} + +message ChangeLog { + google.protobuf.Timestamp createdDate = 1; + google.protobuf.StringValue createdBy = 2; + google.protobuf.Timestamp updatedDate = 3; + google.protobuf.StringValue updatedBy = 4; +} + +message DateOnly { + int32 year = 1; + int32 month = 2; + int32 day = 3; +} + +message Decimal { + int64 units = 1; // Whole units part of the amount + sfixed32 nanos = 2; // Nano units of the amount (10^-9); must be same sign as units +} + +/* +* Messages... +*/ + +{{#each GrpcEntities}} +message {{Name}} { + {{#each GrpcProperties}} + {{GrpcType}} {{ArgumentName}} = {{GrpcFieldNo}}; + {{/each}} +} + {{#if Collection}} + +message {{Name}}Collection { + repeated {{Name}} items = 1; +} + {{/if}} + {{#if CollectionResult}} + +message {{Name}}CollectionResult { + repeated {{Name}} result = 1; + PagingResult paging = 2; +} + {{/if}} + +{{/each}} +/* +* Services... +*/ +{{#each GrpcEntities}} + {{#each GrpcOperations}} + {{#if @first}} + +service {{Parent.Name}}GrpcService { + {{/if}} + rpc {{Name}} ({{Parent.Name}}{{Name}}Request) returns ({{GrpcReturnType}}); + {{#if @last}} +} + {{/if}} + {{/each}} + {{#each GrpcOperations}} + +message {{Parent.Name}}{{Name}}Request { + {{#each PagingLessParameters}} + {{GrpcType}} {{ArgumentName}} = {{add @index 1}}; + {{/each}} + {{#if Paging}} + PagingArgs Paging = 88; + {{/if}} +} + {{/each}} +{{/each}} \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/Grpc_proto.xml b/tools/Beef.CodeGen.Core/Templates/Grpc_proto.xml deleted file mode 100644 index 53469d848..000000000 --- a/tools/Beef.CodeGen.Core/Templates/Grpc_proto.xml +++ /dev/null @@ -1,270 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/IEntityDataSvc_cs.hbs b/tools/Beef.CodeGen.Core/Templates/IEntityDataSvc_cs.hbs new file mode 100644 index 000000000..b03acd367 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/IEntityDataSvc_cs.hbs @@ -0,0 +1,54 @@ +{{! Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef }} +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Entities; +{{#ifeq Root.EntityUsing 'Common' 'All'}} +using {{Root.Company}}.{{Root.AppName}}.Common.Entities; +{{/ifeq}} +{{#ifeq Root.EntityUsing 'Business' 'All'}} +using {{Root.Company}}.{{Root.AppName}}.Business.Entities; +{{/ifeq}} +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.Business.DataSvc +{ + /// + /// Defines the {{{EntityNameSeeComments}}} data repository services. + /// + public partial interface I{{Name}}DataSvc{{#if GenericWithT}}{{/if}} + { +{{#each IDataSvcOperations}} + {{#unless @first}} + + {{/unless}} + /// + /// {{{SummaryText}}} + /// + {{#each DataParameters}} + /// {{{SummaryText}}} + {{/each}} + {{#if HasReturnValue}} + /// {{{ReturnText}}} + {{/if}} + {{{OperationTaskReturnType}}} {{Name}}Async({{#each DataParameters}}{{#unless @first}}, {{/unless}}{{ParameterType}} {{ArgumentName}}{{/each}}); +{{/each}} + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/IEntityDataSvc_cs.xml b/tools/Beef.CodeGen.Core/Templates/IEntityDataSvc_cs.xml deleted file mode 100644 index af53bafd7..000000000 --- a/tools/Beef.CodeGen.Core/Templates/IEntityDataSvc_cs.xml +++ /dev/null @@ -1,235 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/IEntityData_cs.hbs b/tools/Beef.CodeGen.Core/Templates/IEntityData_cs.hbs new file mode 100644 index 000000000..2c13db06d --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/IEntityData_cs.hbs @@ -0,0 +1,54 @@ +{{! Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef }} +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Entities; +{{#ifeq Root.EntityUsing 'Common' 'All'}} +using {{Root.Company}}.{{Root.AppName}}.Common.Entities; +{{/ifeq}} +{{#ifeq Root.EntityUsing 'Business' 'All'}} +using {{Root.Company}}.{{Root.AppName}}.Business.Entities; +{{/ifeq}} +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.Business.Data +{ + /// + /// Defines the {{{EntityNameSeeComments}}} data access. + /// + public partial interface I{{Name}}Data{{#if GenericWithT}}{{/if}} + { +{{#each IDataOperations}} + {{#unless @first}} + + {{/unless}} + /// + /// {{{SummaryText}}} + /// + {{#each DataParameters}} + /// {{{SummaryText}}} + {{/each}} + {{#if HasReturnValue}} + /// {{{ReturnText}}} + {{/if}} + {{{OperationTaskReturnType}}} {{Name}}Async({{#each DataParameters}}{{#unless @first}}, {{/unless}}{{ParameterType}} {{ArgumentName}}{{/each}}); +{{/each}} + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/IEntityData_cs.xml b/tools/Beef.CodeGen.Core/Templates/IEntityData_cs.xml deleted file mode 100644 index 2cd3cd7ec..000000000 --- a/tools/Beef.CodeGen.Core/Templates/IEntityData_cs.xml +++ /dev/null @@ -1,235 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/IEntityManager_cs.hbs b/tools/Beef.CodeGen.Core/Templates/IEntityManager_cs.hbs new file mode 100644 index 000000000..7ac12a52f --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/IEntityManager_cs.hbs @@ -0,0 +1,54 @@ +{{! Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef }} +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Beef; +using Beef.Entities; +{{#ifeq Root.EntityUsing 'Common' 'All'}} +using {{Root.Company}}.{{Root.AppName}}.Common.Entities; +{{/ifeq}} +{{#ifeq Root.EntityUsing 'Business' 'All'}} +using {{Root.Company}}.{{Root.AppName}}.Business.Entities; +{{/ifeq}} +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.Business +{ + /// + /// Defines the {{{EntityNameSeeComments}}} business functionality. + /// + public partial interface I{{Name}}Manager{{#if GenericWithT}}{{/if}} + { +{{#each IManagerOperations}} + {{#unless @first}} + + {{/unless}} + /// + /// {{{SummaryText}}} + /// + {{#each Parameters}} + /// {{{SummaryText}}} + {{/each}} + {{#if HasReturnValue}} + /// {{{ReturnText}}} + {{/if}} + {{{OperationTaskReturnType}}} {{Name}}Async({{#each Parameters}}{{#unless @first}}, {{/unless}}{{ParameterType}} {{ArgumentName}}{{/each}}); +{{/each}} + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/IEntityManager_cs.xml b/tools/Beef.CodeGen.Core/Templates/IEntityManager_cs.xml deleted file mode 100644 index 912ab871a..000000000 --- a/tools/Beef.CodeGen.Core/Templates/IEntityManager_cs.xml +++ /dev/null @@ -1,235 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/IReferenceDataDataSvc_cs.hbs b/tools/Beef.CodeGen.Core/Templates/IReferenceDataDataSvc_cs.hbs new file mode 100644 index 000000000..0f74cac7e --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/IReferenceDataDataSvc_cs.hbs @@ -0,0 +1,34 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using Beef.RefData; +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataBusNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.Business.DataSvc +{ + /// + /// Provides the ReferenceData data services. + /// + public partial interface IReferenceDataDataSvc + { + /// + /// Gets the for the associated . + /// + /// The type associated + /// A . + IReferenceDataCollection GetCollection(Type type); + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/IReferenceDataData_cs.hbs b/tools/Beef.CodeGen.Core/Templates/IReferenceDataData_cs.hbs new file mode 100644 index 000000000..b589b53a1 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/IReferenceDataData_cs.hbs @@ -0,0 +1,41 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Data; +using System.Text; +using System.Threading.Tasks; +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataBusNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.Business.Data +{ + /// + /// Provides the ReferenceData data access. + /// + public partial interface IReferenceDataData + { +{{#each RefDataEntities}} + {{#unless @first}} + + {{/unless}} + /// + /// Gets all the {{seecomments RefDataQualifiedEntityName}} items. + /// + /// The {{seecomments RefDataQualifiedEntityCollectionName}}. + Task<{{RefDataQualifiedEntityCollectionName}}> {{Name}}GetAllAsync(); +{{/each}} + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/IReferenceDataData_cs.xml b/tools/Beef.CodeGen.Core/Templates/IReferenceDataData_cs.xml deleted file mode 100644 index 2d13e3931..000000000 --- a/tools/Beef.CodeGen.Core/Templates/IReferenceDataData_cs.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/IReferenceData_cs.hbs b/tools/Beef.CodeGen.Core/Templates/IReferenceData_cs.hbs new file mode 100644 index 000000000..a4ae9aedd --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/IReferenceData_cs.hbs @@ -0,0 +1,38 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using Beef.RefData; +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataBusNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.{{Root.EntityScope}}.Entities +{ + /// + /// Provides for the required ReferenceData capabilities. + /// + public partial interface IReferenceData : IReferenceDataProvider + { + #region Collections + +{{#each RefDataEntities}} + /// + /// Gets the {{seecomments RefDataQualifiedEntityCollectionName}}. + /// + {{RefDataQualifiedEntityCollectionName}} {{Name}} { get; } + +{{/each}} + #endregion + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/IReferenceData_cs.xml b/tools/Beef.CodeGen.Core/Templates/IReferenceData_cs.xml deleted file mode 100644 index 89cd85a43..000000000 --- a/tools/Beef.CodeGen.Core/Templates/IReferenceData_cs.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/Model_cs.hbs b/tools/Beef.CodeGen.Core/Templates/Model_cs.hbs new file mode 100644 index 000000000..8d2d911bd --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/Model_cs.hbs @@ -0,0 +1,89 @@ +{{! Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef }} +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options +#pragma warning disable CA2227, CA1819 // Collection/Array properties should be read only; ignored, as acceptable for a DTO. + +{{! ===== Using ===== }} +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Beef.Entities; +{{#ifval RefDataType}} +using Beef.RefData.Model; +{{/ifval}} +{{#ifeq JsonSerializer 'Newtonsoft'}} +using Newtonsoft.Json; +{{/ifeq}} +{{#unless Root.IsDataModel}} + {{#if Root.UsingNamespace1}} +using {{Root.UsingNamespace1}}; + {{/if}} + {{#if Root.UsingNamespace2}} +using {{Root.UsingNamespace2}}; + {{/if}} + {{#if Root.UsingNamespace3}} +using {{Root.UsingNamespace3}}; + {{/if}} + {{#ifeq Root.EntityUsing 'Common' 'All'}} + {{#ifeq EntityScope 'Business'}} +using {{Root.Company}}.{{Root.AppName}}.Common.Entities; + {{/ifeq}} + {{/ifeq}} +{{/unless}} + +{{! ===== Class ===== }} +namespace {{Root.Company}}.{{Root.AppName}}.{{#if Root.IsDataModel}}Business.Data.Model{{else}}{{Root.EntityScope}}.Entities{{#ifval Namespace}}.{{Namespace}}{{/ifval}}{{/if}} +{ + /// + /// Represents the {{{Text}}} {{#if Root.IsDataModel}}model{{else}}entity{{/if}}. + /// +{{#ifeq JsonSerializer 'Newtonsoft'}} + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] +{{/ifeq}} + public {{#if Abstract}}abstract {{/if}}partial class {{{EntityName}}}{{#ifval ModelInherits}} : {{{ModelInherits}}}{{/ifval}}{{#ifval ModelImplements}}{{#ifval ModelInherits}}, {{else}} : {{/ifval}}{{{ModelImplements}}}{{/ifval}} + { +{{! ===== Properties ===== }} +{{#each CoreProperties}} + {{#unless @first}} + + {{/unless}} + /// + /// {{{SummaryText}}} + /// + {{#ifeq Parent.JsonSerializer 'Newtonsoft'}} + {{#unless SerializationIgnore}} + [JsonProperty("{{JsonName}}", DefaultValueHandling = {{#if SerializationEmitDefault}}DefaultValueHandling.Include{{else}}DefaultValueHandling.Ignore{{/if}})] + {{/unless}} + {{/ifeq}} + {{#unless IsDataModel}} + {{#ifval Annotation1}} + {{Annotation1}} + {{/ifval}} + {{#ifval Annotation2}} + {{Annotation2}} + {{/ifval}} + {{#ifval Annotation3}} + {{Annotation3}} + {{/ifval}} + {{/unless}} + public {{{PrivateType}}} {{Name}} { get; set; } +{{/each}} + } +{{! ===== Collection ===== }} +{{#if Collection}} + + /// + /// Represents the {{{EntityNameSeeComments}}} collection. + /// + [SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Tightly coupled; OK.")] + public partial class {{{EntityCollectionName}}} : {{{CollectionInherits}}} { } +{{/if}} +} + +#pragma warning restore CA2227, CA1819 +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ReferenceDataDataSvc_cs.hbs b/tools/Beef.CodeGen.Core/Templates/ReferenceDataDataSvc_cs.hbs new file mode 100644 index 000000000..1df17ac51 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/ReferenceDataDataSvc_cs.hbs @@ -0,0 +1,72 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Data; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Beef; +using Beef.Business; +using Beef.RefData; +using Beef.RefData.Caching; +using {{Root.Company}}.{{Root.AppName}}.Business.Data; +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataBusNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.Business.DataSvc +{ + /// + /// Provides the ReferenceData data services. + /// + public partial class ReferenceDataDataSvc : IReferenceDataDataSvc + { + private readonly IServiceProvider _provider; + private readonly Dictionary _cacheDict = new Dictionary(); + + /// + /// Initializes a new instance of the class. + /// + /// The . + public ReferenceDataDataSvc(IServiceProvider provider) + { + _provider = Check.NotNull(provider, nameof(provider)); +{{#each RefDataEntities}} + _cacheDict.Add(typeof({{RefDataQualifiedEntityName}}), new {{Root.RefDataCache}}<{{RefDataQualifiedEntityCollectionName}}, {{RefDataQualifiedEntityName}}>(() => DataSvcInvoker.Current.InvokeAsync(typeof(ReferenceDataDataSvc), () => GetDataAsync(data => data.{{Name}}GetAllAsync())))); +{{/each}} + ReferenceDataDataSvcCtor(); + } + + partial void ReferenceDataDataSvcCtor(); // Enables the ReferenceDataDataSvc constructor to be extended. + + /// + /// Gets the data within a new scope; each reference data request needs to occur separately and independently. + /// + private async Task GetDataAsync(Func> func) + { + using var scope = _provider.CreateScope(); + return await func(scope.ServiceProvider.GetService()).ConfigureAwait(false); + } + + /// + /// Gets the for the associated . + /// + /// The type associated + /// A . + public IReferenceDataCollection GetCollection(Type type) => + _cacheDict.TryGetValue(type ?? throw new ArgumentNullException(nameof(type)), out var rdc) ? rdc.GetCollection() : + throw new ArgumentException($"Type {type.Name} does not exist within the ReferenceDataDataSvc cache.", nameof(type)); + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ReferenceDataDataSvc_cs.xml b/tools/Beef.CodeGen.Core/Templates/ReferenceDataDataSvc_cs.xml deleted file mode 100644 index d52331757..000000000 --- a/tools/Beef.CodeGen.Core/Templates/ReferenceDataDataSvc_cs.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ReferenceDataData_cs.hbs b/tools/Beef.CodeGen.Core/Templates/ReferenceDataData_cs.hbs new file mode 100644 index 000000000..9f5363016 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/ReferenceDataData_cs.hbs @@ -0,0 +1,144 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +{{#if RefData.UsesCosmos}} +using Microsoft.Azure.Cosmos; +{{/if}} +using Beef; +using Beef.Business; +{{#if RefData.UsesCosmos}} +using Beef.Data.Cosmos; + {{#ifval Root.CosmosUsingNamespace}} +using {{Root.CosmosUsingNamespace}}; + {{/ifval}} +{{/if}} +{{#if RefData.UsesDatabase}} +using Beef.Data.Database; + {{#ifval Root.DatabaseUsingNamespace}} +using {{Root.DatabaseUsingNamespace}}; + {{/ifval}} +{{/if}} +{{#if RefData.UsesEntityFramework}} +using Beef.Data.EntityFrameworkCore; + {{#ifval Root.EntityFrameworkUsingNamespace}} +using {{Root.EntityFrameworkUsingNamespace}}; + {{/ifval}} +{{/if}} +{{#if RefData.UsesOData}} +using Beef.Data.OData; + {{#ifval Root.ODataUsingNamespace}} +using {{Root.ODataUsingNamespace}}; + {{/ifval}} +{{/if}} +using Beef.Mapper; +using Beef.Mapper.Converters; +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataBusNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.Business.Data +{ + /// + /// Provides the ReferenceData data access. + /// + public partial class ReferenceDataData : IReferenceDataData + { +{{#each RefData.DataConstructorParameters}} + private readonly {{Type}} {{PrivateName}}; + {{#if @last}} + + {{/if}} +{{/each}} + /// + /// Initializes a new instance of the class. + /// +{{#each RefData.DataConstructorParameters}} + /// {{{SummaryText}}} +{{/each}} + public ReferenceDataData({{#each RefData.DataConstructorParameters}}{{#unless @first}}, {{/unless}}{{Type}} {{ArgumentName}}{{/each}}) + { {{#each RefData.DataConstructorParameters}}{{PrivateName}} = Check.NotNull({{ArgumentName}}, nameof({{ArgumentName}})); {{/each}}{{Name}}DataCtor(); } + + partial void {{Name}}DataCtor(); // Enables additional functionality to be added to the constructor. + +{{#each RefDataEntities}} + {{#unless @first}} + + {{/unless}} + /// + /// Gets all the {{seecomments RefDataQualifiedEntityName}} items. + /// + /// The {{seecomments RefDataQualifiedEntityCollectionName}}. + public async Task<{{RefDataQualifiedEntityCollectionName}}> {{Name}}GetAllAsync() + { + var __coll = new {{RefDataQualifiedEntityCollectionName}}(); + {{#ifeq AutoImplement 'Database'}} + await DataInvoker.Current.InvokeAsync(this, async () => + { + await _db.GetRefDataAsync<{{RefDataQualifiedEntityCollectionName}}, {{RefDataQualifiedEntityName}}>(__coll, "[{{DatabaseSchema}}].[sp{{Name}}GetAll]", "{{Name}}Id"{{#ifeq CoreProperties.Count 0}});{{else}}, additionalProperties: (dr, item, fields) =>{{/ifeq}} + {{#ifne CoreProperties.Count 0}} + { + {{#each CoreProperties}} + item.{{DataMapperPropertyName}} = {{#ifval RefDataConverterCode}}{{{RefDataConverterCode}}}{{/ifval}}dr.GetValue<{{{DeclaredType}}}>("{{#ifval DataName}}{{DataName}}{{else}}{{Name}}{{/ifval}}"){{#ifval RefDataConverterCode}}){{/ifval}}; + {{/each}} + }); + {{/ifne}} + }, BusinessInvokerArgs.TransactionSuppress).ConfigureAwait(false); + + {{/ifeq}} + {{#ifeq AutoImplement 'EntityFramework'}} + await DataInvoker.Current.InvokeAsync(this, async () => { _ef.Query({{Name}}Mapper.CreateArgs()).SelectQuery(__coll); await Task.CompletedTask.ConfigureAwait(false); }, BusinessInvokerArgs.TransactionSuppress).ConfigureAwait(false); + {{/ifeq}} + {{#ifeq AutoImplement 'Cosmos'}} + await DataInvoker.Current.InvokeAsync(this, async () => { _cosmos.ValueQuery({{Name}}Mapper.CreateArgs("{{CosmosContainerId}}")).SelectQuery(__coll); await Task.CompletedTask.ConfigureAwait(false); }).ConfigureAwait(false); + {{/ifeq}} + {{#ifeq AutoImplement 'None'}} + await DataInvoker.Current.InvokeAsync(this, async () => await {{Name}}GetAll_OnImplementation(__coll).ConfigureAwait(false)).ConfigureAwait(false); + {{/ifeq}} + return __coll; + } +{{/each}} +{{#each RefDataEntities}} + {{#ifeq AutoImplement 'EntityFramework'}} + {{#unless EntityFrameworkCustomMapper}} + + /// + /// Provides the {{seecomments RefDataQualifiedEntityName}} and Entity Framework {{{seecomments EntityFrameworkModel}}} property mapping. + /// + public static EfDbMapper<{{RefDataQualifiedEntityName}}, {{EntityFrameworkModel}}> {{Name}}Mapper => EfDbMapper.CreateAuto<{{RefDataQualifiedEntityName}}, {{EntityFrameworkModel}}>() + .HasProperty(s => s.Id, d => d.{{Name}}Id) + {{#each CoreProperties}} + .HasProperty(s => s.{{DataMapperPropertyName}}, d => d.{{#ifval DataName}}{{DataName}}{{else}}{{Name}}{{/ifval}}, => p.{{ifne DataOperationTypes 'Any'}}SetOperationTypes(OperationTypes.{{DataOperationTypes}}{{/ifne}}){{/if}}{{#ifval DataConverterCode}}{{{DataConverterCode}}}{{/ifval}}{{#ifval EntityFrameworkMapper}}.SetMapper({{EntityFrameworkMapper}}.Default!){{/ifval}}) + {{/each}} + .AddStandardProperties(); + {{/unless}} + {{/ifeq}} + {{#ifeq AutoImplement 'Cosmos'}} + {{#unless CosmosCustomMapper}} + + /// + /// Provides the {{seecomments RefDataQualifiedEntityName}} and Cosmos {{{seecomments CosmosModel}}} property mapping. + /// + public static CosmosDbMapper<{{RefDataQualifiedEntityName}}, {{CosmosModel}}> {{Name}}Mapper => CosmosDbMapper.CreateAuto<{{RefDataQualifiedEntityName}}, {{CosmosModel}}>() + {{#each CoreProperties}} + .HasProperty(s => s.{{DataMapperPropertyName}}, d => d.{{#ifval DataName}}{{DataName}}{{else}}{{Name}}{{/ifval}}, p => p.SetOperationTypes(OperationTypes.{{DataOperationTypes}}){{/if}}{{#ifval DataConverterCode}}.SetConverter({{{DataConverterCode}}}){{/ifval}}{{#ifval EntityFrameworkMapper}}.SetMapper({{EntityFrameworkMapper}}.Default!){{/ifval}}) + {{/each}} + .AddStandardProperties(); + {{/unless}} + {{/ifeq}} +{{/each}} + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ReferenceDataData_cs.xml b/tools/Beef.CodeGen.Core/Templates/ReferenceDataData_cs.xml deleted file mode 100644 index 02e5cd740..000000000 --- a/tools/Beef.CodeGen.Core/Templates/ReferenceDataData_cs.xml +++ /dev/null @@ -1,461 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ReferenceDataProvider_cs.hbs b/tools/Beef.CodeGen.Core/Templates/ReferenceDataProvider_cs.hbs new file mode 100644 index 000000000..85969e0d5 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/ReferenceDataProvider_cs.hbs @@ -0,0 +1,89 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Beef; +using Beef.RefData; +using {{Root.Company}}.{{Root.AppName}}.Business.DataSvc; +using {{Root.Company}}.{{Root.AppName}}.Common.Entities; +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataBusNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.Business +{ + /// + /// Provides the implementation using the corresponding data services. + /// + public partial class ReferenceDataProvider : {{#ifval Root.RefDataNamespace}}RefDataNamespace.{{else}}RefDataBusNamespace.{{/ifval}}ReferenceData + { + private readonly IReferenceDataDataSvc _dataService; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public ReferenceDataProvider(IReferenceDataDataSvc dataService) { _dataService = Check.NotNull(dataService, nameof(dataService)); ReferenceDataProviderCtor(); } + + partial void ReferenceDataProviderCtor(); // Enables the ReferenceDataProvider constructor to be extended. + + #region Collections + +{{#each RefDataEntities}} + /// + /// Gets the {{seecomments RefDataQualifiedEntityCollectionName}}. + /// + public override {{RefDataQualifiedEntityCollectionName}} {{Name}} => ({{RefDataQualifiedEntityCollectionName}})this[typeof({{RefDataQualifiedEntityName}})]; + +{{/each}} + #endregion + + /// + /// Gets the for the associated . + /// + /// The . + /// A . + public override IReferenceDataCollection this[Type type] => _dataService.GetCollection(type); + + /// + /// Prefetches all, or the list of objects, where not already cached or expired. + /// + /// The list of names; otherwise, null for all. + public override Task PrefetchAsync(params string[] names) + { + var types = new List(); + if (names == null) + { + types.AddRange(GetAllTypes()); + } + else + { + foreach (string name in names.Distinct()) + { + switch (name) + { +{{#each RefDataEntities}} + case var n when string.Compare(n, nameof({{RefDataQualifiedEntityName}}), StringComparison.InvariantCultureIgnoreCase) == 0: types.Add(typeof({{RefDataQualifiedEntityName}})); break; +{{/each}} + } + } + } + + Parallel.ForEach(types, (type, _) => { var __ = this[type]; }); + return Task.CompletedTask; + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ReferenceDataProvider_cs.xml b/tools/Beef.CodeGen.Core/Templates/ReferenceDataProvider_cs.xml deleted file mode 100644 index d19396130..000000000 --- a/tools/Beef.CodeGen.Core/Templates/ReferenceDataProvider_cs.xml +++ /dev/null @@ -1,107 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiAgentProvider_cs.xml b/tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiAgentProvider_cs.hbs similarity index 57% rename from tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiAgentProvider_cs.xml rename to tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiAgentProvider_cs.hbs index 4db9ccb2d..d074edee2 100644 --- a/tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiAgentProvider_cs.xml +++ b/tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiAgentProvider_cs.hbs @@ -1,11 +1,4 @@ - - - \ No newline at end of file +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiAgent_cs.xml b/tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiAgent_cs.hbs similarity index 52% rename from tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiAgent_cs.xml rename to tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiAgent_cs.hbs index 92e412d62..2995d42a6 100644 --- a/tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiAgent_cs.xml +++ b/tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiAgent_cs.hbs @@ -1,11 +1,4 @@ - - - \ No newline at end of file +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiController_cs.hbs b/tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiController_cs.hbs new file mode 100644 index 000000000..20a39891e --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiController_cs.hbs @@ -0,0 +1,91 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Beef; +using Beef.AspNetCore.WebApi; +using Beef.Entities; +using Beef.RefData; +using {{Root.Company}}.{{Root.AppName}}.Common.Entities; +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.{{Root.ApiName}}.Controllers +{ + /// + /// Provides the ReferenceData Web API functionality. + /// + [{{#if WebApiAuthorize}}Authorize{{else}}AllowAnonymous{{/if}}] + public partial class ReferenceDataController : ControllerBase + { +{{#each RefDataEntities}} + /// + /// Gets all of the {{seecomments RefDataQualifiedEntityName}} reference data items that match the specified criteria. + /// + /// The reference data code list. + /// The reference data text (including wildcards). + /// A {{RefDataQualifiedEntityName}} collection. + [{{#if WebApiAuthorize}}Authorize{{else}}AllowAnonymous{{/if}}] + [HttpGet()] + {{#ifval WebApiRoutePrefix}} + [Route("{{WebApiRoutePrefix}}")] + {{/ifval}} + [ProducesResponseType(typeof(IEnumerable<{{RefDataQualifiedEntityName}}>), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public IActionResult {{Name}}GetAll(List? codes = default, string? text = default) => new WebApiGet>(this, + () => Task.FromResult(ReferenceDataFilter.ApplyFilter<{{RefDataQualifiedEntityCollectionName}}, {{RefDataQualifiedEntityName}}>({{#ifval Root.RefDataNamespace}}RefDataNamespace.{{else}}RefDataBusNamespace.{{/ifval}}ReferenceData.Current.{{Name}}, codes, text, includeInactive: this.IncludeInactive())), + operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); + +{{/each}} + /// + /// Gets the reference data entries for the specified entities and codes from the query string; e.g: {{Config.RefDataWebApiRoute}}?entity=codeX,codeY&entity2=codeZ&entity3 + /// + /// A . + [{{#if WebApiAuthorize}}Authorize{{else}}AllowAnonymous{{/if}}] + [HttpGet()] + [Route("{{RefDataWebApiRoute}}")] + [ProducesResponseType(typeof(ReferenceDataMultiCollection), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public IActionResult GetNamed() + { + return new WebApiGet(this, async () => + { + var coll = new ReferenceDataMultiCollection(); + var inactive = this.IncludeInactive(); + var refSelection = this.ReferenceDataSelection(); + + var names = refSelection.Select(x => x.Key).ToArray(); + await {{#ifval Root.RefDataNamespace}}RefDataNamespace.{{else}}RefDataBusNamespace.{{/ifval}}ReferenceData.Current.PrefetchAsync(names).ConfigureAwait(false); + + foreach (var q in refSelection) + { + switch (q.Key) + { +{{#each RefDataEntities}} + case var s when string.Compare(s, nameof({{RefDataQualifiedEntityName}}), StringComparison.InvariantCultureIgnoreCase) == 0: coll.Add(new ReferenceDataMultiItem(nameof({{RefDataQualifiedEntityName}}), ReferenceDataFilter.ApplyFilter<{{RefDataQualifiedEntityCollectionName}}, {{RefDataQualifiedEntityName}}>({{#ifval Root.RefDataNamespace}}RefDataNamespace.{{else}}RefDataBusNamespace.{{/ifval}}ReferenceData.Current.{{Name}}, q.Value, includeInactive: inactive))); break; +{{/each}} + } + } + + return coll; + }, operationType: OperationType.Read, statusCode: HttpStatusCode.OK, alternateStatusCode: HttpStatusCode.NoContent); + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiCoreController_cs.xml b/tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiCoreController_cs.xml deleted file mode 100644 index 855b9dd00..000000000 --- a/tools/Beef.CodeGen.Core/Templates/ReferenceDataWebApiCoreController_cs.xml +++ /dev/null @@ -1,143 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ReferenceData_cs.hbs b/tools/Beef.CodeGen.Core/Templates/ReferenceData_cs.hbs new file mode 100644 index 000000000..1c43e2099 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/ReferenceData_cs.hbs @@ -0,0 +1,76 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using System; +using System.Threading.Tasks; +using Beef.RefData; +{{#ifval Root.RefDataNamespace}} +using RefDataNamespace = {{Root.RefDataNamespace}}; +{{/ifval}} +{{#ifval Root.RefDataBusNamespace}} +using RefDataBusNamespace = {{Root.RefDataBusNamespace}}; +{{/ifval}} + +namespace {{Root.Company}}.{{Root.AppName}}.{{Root.EntityScope}}.Entities +{ + /// + /// Provides a standard mechanism for accessing the ReferenceData. + /// + public abstract partial class ReferenceData : IReferenceData + { + /// + /// Gets the current instance. + /// + public static ReferenceData Current => (ReferenceData)ReferenceDataManager.Current.GetProvider(typeof(IReferenceData)); + + /// + /// Gets all the underlying types. + /// + /// An array of the types. + public static Type[] GetAllTypes() => new Type[] + { +{{#each RefDataEntities}} + typeof({{Name}}){{#unless @last}},{{/unless}} +{{/each}} + }; + + /// + /// Gets the provider interface cref="Type"/> used for . The value is . + /// + public Type ProviderType => typeof(IReferenceData); + + /// + /// Gets the for the associated . + /// + /// The associated . + /// The corresponding . + [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1043:Use Integral Or String Argument For Indexers", Justification = "Logical for type")] + public abstract IReferenceDataCollection this[Type type] { get; } + + /// + /// Prefetches all of the named objects. + /// + /// The list of names. + /// Note for implementers; should only fetch where not already cached or expired. This is provided to improve performance for consuming applications to reduce the overhead of + /// making multiple individual invocations, i.e. reduces chattiness across a potentially high-latency connection. + public abstract Task PrefetchAsync(params string[] names); + + #region Collections + +{{#each RefDataEntities}} + /// + /// Gets the {{seecomments RefDataQualifiedEntityCollectionName}}. + /// + public abstract {{RefDataQualifiedEntityCollectionName}} {{Name}} { get; } + +{{/each}} + #endregion + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsDataSvc_cs.hbs b/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsDataSvc_cs.hbs new file mode 100644 index 000000000..34faabdc9 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsDataSvc_cs.hbs @@ -0,0 +1,35 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using Microsoft.Extensions.DependencyInjection; + +namespace {{Company}}.{{AppName}}.Business.DataSvc +{ + /// + /// Provides the generated DataSvc-layer services. + /// + public static class {{#if IsRefData}}ReferenceData{{/if}}ServiceCollectionsExtension + { + /// + /// Adds the generated DataSvc-layer services. + /// + /// The . + /// The . + public static IServiceCollection AddGenerated{{#if IsRefData}}ReferenceData{{/if}}DataSvcServices(this IServiceCollection services) + { +{{#if IsRefData}} + return services.AddSingleton(){{#ifeq IDataSvcEntities.Count 0}};{{/ifeq}} +{{/if}} +{{#each IDataSvcEntities}} + {{#if @first}}{{#if Root.IsRefData}} {{else}}return services{{/if}}{{else}} {{/if}}.AddScoped{{/if}}, {{Name}}DataSvc{{#if GenericWithT}}{{/if}}>(){{#if @last}};{{/if}} +{{/each}} + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsDataSvc_cs.xml b/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsDataSvc_cs.xml deleted file mode 100644 index 3f2ee2ca6..000000000 --- a/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsDataSvc_cs.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsData_cs.hbs b/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsData_cs.hbs new file mode 100644 index 000000000..c13f67324 --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsData_cs.hbs @@ -0,0 +1,35 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using Microsoft.Extensions.DependencyInjection; + +namespace {{Company}}.{{AppName}}.Business.Data +{ + /// + /// Provides the generated Data-layer services. + /// + public static class {{#if IsRefData}}ReferenceData{{/if}}ServiceCollectionsExtension + { + /// + /// Adds the generated Data-layer services. + /// + /// The . + /// The . + public static IServiceCollection AddGenerated{{#if IsRefData}}ReferenceData{{/if}}DataServices(this IServiceCollection services) + { +{{#if IsRefData}} + return services.AddTransient(){{#ifeq IDataEntities.Count 0}};{{/ifeq}} +{{/if}} +{{#each IDataEntities}} + {{#if @first}}{{#if Root.IsRefData}} {{else}}return services{{/if}}{{else}} {{/if}}.AddScoped{{/if}}, {{Name}}Data{{#if GenericWithT}}{{/if}}>(){{#if @last}};{{/if}} +{{/each}} + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsData_cs.xml b/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsData_cs.xml deleted file mode 100644 index dcd158aca..000000000 --- a/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsData_cs.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsManager_cs.hbs b/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsManager_cs.hbs new file mode 100644 index 000000000..90a2b4a2e --- /dev/null +++ b/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsManager_cs.hbs @@ -0,0 +1,43 @@ +/* + * This file is automatically generated; any changes will be lost. + */ + +#nullable enable +#pragma warning disable IDE0005 // Using directive is unnecessary; are required depending on code-gen options + +using Microsoft.Extensions.DependencyInjection; +{{#if IsRefData}} + {{#ifeq EntityUsing 'Business' 'All'}} +using {{Company}}.{{AppName}}.Business.Entities; + {{/ifeq}} + {{#ifeq EntityUsing 'Common' 'All'}} +using {{Company}}.{{AppName}}.Common.Entities; + {{/ifeq}} +{{/if}} + +namespace {{Company}}.{{AppName}}.Business +{ + /// + /// Provides the generated Manager-layer services. + /// + public static class {{#if IsRefData}}ReferenceData{{/if}}ServiceCollectionsExtension + { + /// + /// Adds the generated Manager-layer services. + /// + /// The . + /// The . + public static IServiceCollection AddGenerated{{#if IsRefData}}ReferenceData{{/if}}ManagerServices(this IServiceCollection services) + { +{{#if IsRefData}} + return services.AddSingleton(){{#ifeq IManagerEntities.Count 0}};{{/ifeq}} +{{/if}} +{{#each IManagerEntities}} + {{#if @first}}{{#if Root.IsRefData}} {{else}}return services{{/if}}{{else}} {{/if}}.AddScoped{{/if}}, {{Name}}Manager{{#if GenericWithT}}{{/if}}>(){{#if @last}};{{/if}} +{{/each}} + } + } +} + +#pragma warning restore IDE0005 +#nullable restore \ No newline at end of file diff --git a/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsManager_cs.xml b/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsManager_cs.xml deleted file mode 100644 index 5d140b18b..000000000 --- a/tools/Beef.CodeGen.Core/Templates/ServiceCollectionExtensionsManager_cs.xml +++ /dev/null @@ -1,79 +0,0 @@ - - - \ No newline at end of file diff --git a/tools/Beef.Database.Core/Beef.Database.Core.csproj b/tools/Beef.Database.Core/Beef.Database.Core.csproj index 07f31f9e6..ce55849c3 100644 --- a/tools/Beef.Database.Core/Beef.Database.Core.csproj +++ b/tools/Beef.Database.Core/Beef.Database.Core.csproj @@ -5,7 +5,7 @@ Exe - 4.1.1 + 4.1.2 Beef Developers Avanade false diff --git a/tools/Beef.Database.Core/CHANGELOG.md b/tools/Beef.Database.Core/CHANGELOG.md index 1c858ae51..85f8395da 100644 --- a/tools/Beef.Database.Core/CHANGELOG.md +++ b/tools/Beef.Database.Core/CHANGELOG.md @@ -2,6 +2,10 @@ Represents the **NuGet** versions. +## v4.1.2 +- *Enhancement:* The `ScriptNew` option has been changed to add support for a new `-createref` to create using the reference data schema; instead of the inferring from the schema name (this support has been removed). This way the developer explicitly decides and allows reference data to be used in any schema. +- *Fixed:* Issue [71](https://github.com/Avanade/Beef/issues/71) has been resolved. A runtime error will now correctly result in a return code of `-1`. + ## v4.1.1 - *Enhancement:* Introduction of Dependency Injection support. diff --git a/tools/Beef.Database.Core/DatabaseConsole.cs b/tools/Beef.Database.Core/DatabaseConsole.cs index 06629fc29..83d3d9e08 100644 --- a/tools/Beef.Database.Core/DatabaseConsole.cs +++ b/tools/Beef.Database.Core/DatabaseConsole.cs @@ -31,6 +31,7 @@ public class DatabaseConsole private readonly CommandOption _templateOpt; private readonly CommandOption _outputOpt; private readonly CommandOption _paramsOpt; + private readonly CommandOption _refSchema; private readonly ILogger _logger; /// @@ -75,6 +76,8 @@ private DatabaseConsole() _paramsOpt = App.Option("-p|--param", "Name=Value pair(s) passed into code generation.", CommandOptionType.MultipleValue) .Accepts(v => v.Use(new ParamsValidator())); + _refSchema = App.Option("-rs|--refschema", "Reference data schema name.", CommandOptionType.SingleValue); + Logger.Default = _logger = new ColoredConsoleLogger(nameof(CodeGenConsole)); App.OnValidate((ctx) => OnValidate()); @@ -154,7 +157,7 @@ private async Task RunRunAwayAsync() /* Inspired by https://www.youtube.com WriteHeader(args); - var de = new DatabaseExecutor(_commandArg.ParsedValue, _connectionStringArg.Value!, _scriptAssemblies.ToArray(), args); + var de = new DatabaseExecutor(new DatabaseExecutorArgs(_commandArg.ParsedValue, _connectionStringArg.Value!, _scriptAssemblies.ToArray()) { CodeGenArgs = args }); var sw = Stopwatch.StartNew(); var result = await de.RunAsync().ConfigureAwait(false); diff --git a/tools/Beef.Database.Core/DatabaseConsoleWrapper.cs b/tools/Beef.Database.Core/DatabaseConsoleWrapper.cs index 36332d838..82f8f7137 100644 --- a/tools/Beef.Database.Core/DatabaseConsoleWrapper.cs +++ b/tools/Beef.Database.Core/DatabaseConsoleWrapper.cs @@ -35,10 +35,11 @@ public class DatabaseConsoleWrapper /// The application/domain name. /// The output path/directory. /// Indicates whether to use the standard BEEF dbo schema objects (defaults to true). + /// The optional reference data schema name. /// The instance. - public static DatabaseConsoleWrapper Create(Assembly[] assemblies, string connectionString, string company, string appName, string outDir = "./..", bool useBeefDbo = true) + public static DatabaseConsoleWrapper Create(Assembly[] assemblies, string connectionString, string company, string appName, string outDir = "./..", bool useBeefDbo = true, string? refDataSchemaName = null) { - return new DatabaseConsoleWrapper(assemblies, connectionString, company, appName, outDir, useBeefDbo); + return new DatabaseConsoleWrapper(assemblies, connectionString, company, appName, outDir, useBeefDbo, refDataSchemaName); } /// @@ -49,10 +50,11 @@ public static DatabaseConsoleWrapper Create(Assembly[] assemblies, string connec /// The application/domain name. /// The output path/directory. /// Indicates whether to use the standard BEEF dbo schema objects (defaults to true). + /// The optional reference data schema name. /// The instance. - public static DatabaseConsoleWrapper Create(string connectionString, string company, string appName, string outDir = "./..", bool useBeefDbo = true) + public static DatabaseConsoleWrapper Create(string connectionString, string company, string appName, string outDir = "./..", bool useBeefDbo = true, string? refDataSchemaName = null) { - return new DatabaseConsoleWrapper(new Assembly[] { Assembly.GetEntryAssembly()! }, connectionString, company, appName, outDir, useBeefDbo); + return new DatabaseConsoleWrapper(new Assembly[] { Assembly.GetEntryAssembly()! }, connectionString, company, appName, outDir, useBeefDbo, refDataSchemaName); } /// @@ -64,12 +66,14 @@ public static DatabaseConsoleWrapper Create(string connectionString, string comp /// The application/domain name. /// The output path/directory. /// Indicates whether to use the standard BEEF dbo schema objects (defaults to true). - private DatabaseConsoleWrapper(Assembly[] assemblies, string connectionString, string company, string appName, string outDir, bool useBeefDbo = true) + /// The optional reference data schema name. + private DatabaseConsoleWrapper(Assembly[] assemblies, string connectionString, string company, string appName, string outDir, bool useBeefDbo = true, string? refDataSchemaName = null) { ConnectionString = Check.NotEmpty(connectionString, nameof(connectionString)); Company = Check.NotEmpty(company, nameof(company)); AppName = Check.NotEmpty(appName, nameof(appName)); OutDir = Check.NotEmpty(outDir, nameof(outDir)); + RefDataSchemaName = refDataSchemaName; if (assemblies == null) assemblies = Array.Empty(); @@ -120,6 +124,11 @@ private void OverrideConnectionString() /// public string OutDir { get; private set; } + /// + /// Gets the reference data schema name. + /// + public string? RefDataSchemaName { get; private set; } + /// /// Executes the underlying using the database tooling arguments. /// @@ -137,6 +146,7 @@ public async Task RunAsync(string[] args) var cs = app.Option("-cs|--connectionstring", "Override the database connection string.", CommandOptionType.SingleValue); var eo = app.Option("-eo|--entry-assembly-only", "Override assemblies to use the entry assembly only.", CommandOptionType.NoValue); var ct = app.Option("-create|--scriptnew-create-table", "ScriptNew: use create '[schema.]table' template.", CommandOptionType.SingleValue); + var cr = app.Option("-createref|--scriptnew-create-ref-table", "ScriptNew: use create reference data '[schema.]table' template.", CommandOptionType.SingleValue); var at = app.Option("-alter|--scriptnew-alter-table", "ScriptNew: use alter '[schema.]table' template.", CommandOptionType.SingleValue); app.OnExecuteAsync(async (_) => @@ -150,10 +160,15 @@ public async Task RunAsync(string[] args) var rargs = ReplaceMoustache(CommandLineTemplate, cmd.Value!, cs.Value() ?? ConnectionString, sb.ToString()); if (ct.HasValue()) rargs += GetTableSchemaParams("Create", ct.Value()!); + else if (cr.HasValue()) + rargs += GetTableSchemaParams("CreateRef", cr.Value()!); else if (at.HasValue()) rargs += GetTableSchemaParams("Alter", at.Value()!); - await DatabaseConsole.Create().RunAsync(rargs).ConfigureAwait(false); + if (!string.IsNullOrEmpty(RefDataSchemaName)) + rargs += $" -rs {RefDataSchemaName}"; + + return await DatabaseConsole.Create().RunAsync(rargs).ConfigureAwait(false); }); try @@ -162,7 +177,7 @@ public async Task RunAsync(string[] args) } catch (CommandParsingException cpex) { - Console.WriteLine(cpex.Message); + Console.Error.WriteLine(cpex.Message); return -1; } } @@ -173,7 +188,7 @@ public async Task RunAsync(string[] args) private string GetTableSchemaParams(string action, string arg) { if (string.IsNullOrEmpty(arg)) - return $" -p ScriptNew={action} -p Schema={AppName} -p Table=Xxxxx"; + return $" -p ScriptNew={action} -p Schema={AppName} -p Table=Xxx"; var parts = arg.Split('.', StringSplitOptions.RemoveEmptyEntries); if (parts.Length == 1) @@ -181,7 +196,7 @@ private string GetTableSchemaParams(string action, string arg) else if (parts.Length == 2) return $" -p ScriptNew={action} -p Schema={parts[0]} -p Table={parts[1]}"; else - return $" -p ScriptNew={action} -p Schema={AppName} -p Table=Xxxxx"; + return $" -p ScriptNew={action} -p Schema={AppName} -p Table=Xxx"; } /// diff --git a/tools/Beef.Database.Core/DatabaseExecutor.cs b/tools/Beef.Database.Core/DatabaseExecutor.cs index 0b5f8f8df..f918f272f 100644 --- a/tools/Beef.Database.Core/DatabaseExecutor.cs +++ b/tools/Beef.Database.Core/DatabaseExecutor.cs @@ -20,6 +20,55 @@ namespace Beef.Database.Core { + /// + /// The arguments. + /// + public class DatabaseExecutorArgs + { + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The connection string. + /// The array whose embedded resources will be probed. + public DatabaseExecutorArgs(DatabaseExecutorCommand command, string connectionString, params Assembly[] assemblies) + { + Command = command; + ConnectionString = Check.NotNull(connectionString, nameof(connectionString)); + Assemblies = new List(assemblies); + } + + /// + /// Gets the . + /// + public DatabaseExecutorCommand Command { get; private set; } + + /// + /// Gets the connection string. + /// + public string ConnectionString { get; private set; } + + /// + /// Gets the list whose embedded resources will be probed. + /// + public List Assemblies { get; private set; } + + /// + /// Indicates whether ot use the standard Beef dbo schema objects. + /// + public bool UseBeefDbo { get; set; } = true; + + /// + /// Gets or sets the optional reference data schema name (typically specified where different to the primary schema). + /// + public string? RefDataSchemaName { get; set; } + + /// + /// Gets or sets the arguments. + /// + public CodeGenExecutorArgs? CodeGenArgs { get; set; } + } + /// /// Represents the database executor. /// @@ -42,15 +91,12 @@ public class DatabaseExecutor /// public static string DataNamespace { get; set; } = "Data"; - private readonly DatabaseExecutorCommand _command; - private readonly string _connectionString; - private readonly Assembly[] _assemblies; + private readonly DatabaseExecutorArgs _args; private readonly List _namespaces = new List(); - private readonly CodeGenExecutorArgs _codeGenArgs; private readonly ILogger _logger; /// - /// Represents a DbUp to Beef Logger sink. + /// Represents a DbUp to ILogger sink. /// private class LoggerSink : IUpgradeLog { @@ -89,56 +135,33 @@ public Db(string cs) : base(cs) { } /// /// Runs the directly. /// - /// The . - /// The database connection string. - /// Indicates whether to use the standard Beef dbo schema objects. - /// The array whose embedded resources will be probed. - /// The arguments. + /// The . /// The return code; zero equals success. - public static async Task RunAsync(DatabaseExecutorCommand command, string connectionString, bool useBeefDbo, Assembly[] assemblies, CodeGenExecutorArgs? codeGenArgs = null) + public static async Task RunAsync(DatabaseExecutorArgs args) { - var de = new DatabaseExecutor(command, connectionString, useBeefDbo ? assemblies.Prepend(typeof(DatabaseExecutor).Assembly).ToArray() : assemblies, codeGenArgs); - return await de.RunAsync().ConfigureAwait(false) ? 0 : -1; - } - - /// - /// Runs the directly. - /// - /// The . - /// The database connection string. - /// Indicates whether to use the standard Beef dbo schema objects. - /// The array whose embedded resources will be probed. - /// The return code; zero equals success. - public static async Task RunAsync(DatabaseExecutorCommand command, string connectionString, bool useBeefDbo, params Assembly[] assemblies) - { - var de = new DatabaseExecutor(command, connectionString, useBeefDbo ? assemblies.Prepend(typeof(DatabaseExecutor).Assembly).ToArray() : assemblies, null!); + var de = new DatabaseExecutor(args); return await de.RunAsync().ConfigureAwait(false) ? 0 : -1; } /// /// Initializes a new instance of the class. /// - /// The . - /// The database connection string. - /// The array whose embedded resources will be probed. - /// The arguments. - public DatabaseExecutor(DatabaseExecutorCommand command, string connectionString, Assembly[] assemblies, CodeGenExecutorArgs? codeGenArgs = null) + /// The . + public DatabaseExecutor(DatabaseExecutorArgs args) { - Logger.Default = _logger = new ColoredConsoleLogger(nameof(CodeGenConsole)); + _args = Check.NotNull(args, nameof(args)); + Logger.Default = _logger = new ColoredConsoleLogger(nameof(DatabaseConsole)); - _command = command; - _connectionString = Check.NotEmpty(connectionString, nameof(connectionString)); - _assemblies = assemblies; - _logger = new ColoredConsoleLogger(nameof(DatabaseExecutor)); + Check.IsFalse(_args.Command.HasFlag(DatabaseExecutorCommand.CodeGen) && _args.CodeGenArgs == null, nameof(args), "The code generation arguments must be provided when the 'command' includes 'CodeGen'."); + if (_args.CodeGenArgs != null && !_args.CodeGenArgs.Parameters.ContainsKey("ConnectionString")) + _args.CodeGenArgs.Parameters.Add("ConnectionString", _args.ConnectionString); - Check.IsFalse(_command.HasFlag(DatabaseExecutorCommand.CodeGen) && codeGenArgs == null, nameof(codeGenArgs), "The code generation arguments must be provided when the 'command' includes 'CodeGen'."); - _codeGenArgs = codeGenArgs ?? new CodeGenExecutorArgs(_logger); - if (_codeGenArgs != null && !_codeGenArgs.Parameters.ContainsKey("ConnectionString")) - _codeGenArgs.Parameters.Add("ConnectionString", _connectionString); + _db = new Db(_args.ConnectionString); - _db = new Db(_connectionString); + if (_args.UseBeefDbo) + _args.Assemblies.Insert(0, typeof(DatabaseConsoleWrapper).Assembly); - _assemblies.ForEach(ass => _namespaces.Add(ass.GetName().Name!)); + _args.Assemblies.ForEach(ass => _namespaces.Add(ass.GetName().Name!)); } /// @@ -148,25 +171,25 @@ public async Task RunAsync() { var ls = new LoggerSink(_logger); - if (_command.HasFlag(DatabaseExecutorCommand.Drop)) + if (_args.Command.HasFlag(DatabaseExecutorCommand.Drop)) { _logger.LogInformation(string.Empty); _logger.LogInformation(new string('-', 80)); _logger.LogInformation("DB DROP: Checking database existence and dropping where found..."); - if (!await TimeExecutionAsync(() => { DropDatabase.For.SqlDatabase(_connectionString, ls); return Task.FromResult(true); }).ConfigureAwait(false)) + if (!await TimeExecutionAsync(() => { DropDatabase.For.SqlDatabase(_args.ConnectionString, ls); return Task.FromResult(true); }).ConfigureAwait(false)) return false; } - if (_command.HasFlag(DatabaseExecutorCommand.Create)) + if (_args.Command.HasFlag(DatabaseExecutorCommand.Create)) { _logger.LogInformation(string.Empty); _logger.LogInformation(new string('-', 80)); _logger.LogInformation("DB CREATE: Checking database existence and creating where not found..."); - if (!await TimeExecutionAsync(() => { EnsureDatabase.For.SqlDatabase(_connectionString, ls); return Task.FromResult(true); }).ConfigureAwait(false)) + if (!await TimeExecutionAsync(() => { EnsureDatabase.For.SqlDatabase(_args.ConnectionString, ls); return Task.FromResult(true); }).ConfigureAwait(false)) return false; } - if (_command.HasFlag(DatabaseExecutorCommand.Migrate)) + if (_args.Command.HasFlag(DatabaseExecutorCommand.Migrate)) { _logger.LogInformation(string.Empty); _logger.LogInformation(new string('-', 80)); @@ -177,8 +200,8 @@ public async Task RunAsync() if (!await TimeExecutionAsync(() => { result = DeployChanges.To - .SqlDatabase(_connectionString) - .WithScripts(GetMigrationScripts(_assemblies)) + .SqlDatabase(_args.ConnectionString) + .WithScripts(GetMigrationScripts(_args.Assemblies)) .WithoutTransaction() .LogTo(ls) .Build() @@ -195,35 +218,35 @@ public async Task RunAsync() } } - if (_command.HasFlag(DatabaseExecutorCommand.CodeGen)) + if (_args.Command.HasFlag(DatabaseExecutorCommand.CodeGen)) { _logger.LogInformation(string.Empty); _logger.LogInformation(new string('-', 80)); _logger.LogInformation("DB CODEGEN: Code-gen database objects..."); - CodeGenConsole.LogCodeGenExecutionArgs(_codeGenArgs); + CodeGenConsole.LogCodeGenExecutionArgs(_args.CodeGenArgs!); if (!await TimeExecutionAsync(async () => { - var cge = new CodeGenExecutor(_codeGenArgs); - await cge.RunAsync().ConfigureAwait(false); - return true; + var cge = new CodeGenExecutor(_args.CodeGenArgs!); + return await cge.RunAsync().ConfigureAwait(false); }).ConfigureAwait(false)) { return false; } } - if (_command.HasFlag(DatabaseExecutorCommand.Schema)) + if (_args.Command.HasFlag(DatabaseExecutorCommand.Schema)) { _logger.LogInformation(string.Empty); _logger.LogInformation(new string('-', 80)); _logger.LogInformation("DB SCHEMA: Drops and creates the database objects..."); - if (!await TimeExecutionAsync(() => DropAndCreateAllObjectsAsync(new string[] { "dbo", "Ref" })).ConfigureAwait(false)) + if (!await TimeExecutionAsync(() => + DropAndCreateAllObjectsAsync(string.IsNullOrEmpty(_args.RefDataSchemaName) ? new string[] { "dbo" } : new string[] { "dbo", _args.RefDataSchemaName })).ConfigureAwait(false)) return false; } - if (_command.HasFlag(DatabaseExecutorCommand.Reset)) + if (_args.Command.HasFlag(DatabaseExecutorCommand.Reset)) { _logger.LogInformation(string.Empty); _logger.LogInformation(new string('-', 80)); @@ -233,7 +256,7 @@ public async Task RunAsync() return false; } - if (_command.HasFlag(DatabaseExecutorCommand.Data)) + if (_args.Command.HasFlag(DatabaseExecutorCommand.Data)) { _logger.LogInformation(string.Empty); _logger.LogInformation(new string('-', 80)); @@ -243,7 +266,7 @@ public async Task RunAsync() return false; } - if (_command.HasFlag(DatabaseExecutorCommand.ScriptNew)) + if (_args.Command.HasFlag(DatabaseExecutorCommand.ScriptNew)) { _logger.LogInformation(string.Empty); _logger.LogInformation(new string('-', 80)); @@ -281,7 +304,7 @@ private async Task TimeExecutionAsync(Func> action) /// /// Gets all the migration scripts from the assemblies and ensures order. /// - private List GetMigrationScripts(Assembly[] assemblies) + private List GetMigrationScripts(IEnumerable assemblies) { var scripts = new List(); var count = 0; @@ -325,12 +348,12 @@ private async Task DropAndCreateAllObjectsAsync(string[] schemaOrder) var list = new List(); // See if there are any files out there (recently generated). - if (_codeGenArgs?.OutputPath != null) + if (_args.CodeGenArgs?.OutputPath != null) { - _logger.LogInformation($"Probing for files: '{_codeGenArgs.OutputPath.FullName}*.sql'"); + _logger.LogInformation($"Probing for files: '{_args.CodeGenArgs.OutputPath.FullName}*.sql'"); foreach (var ns in _namespaces) { - var di = new DirectoryInfo(Path.Combine(_codeGenArgs.OutputPath.FullName, ns, SchemaNamespace)); + var di = new DirectoryInfo(Path.Combine(_args.CodeGenArgs.OutputPath.FullName, ns, SchemaNamespace)); if (di.Exists) { foreach (var fi in di.GetFiles("*.sql", SearchOption.AllDirectories)) @@ -343,7 +366,7 @@ private async Task DropAndCreateAllObjectsAsync(string[] schemaOrder) return false; } - var sr = new SqlSchemaScript { Name = name, Reader = sor, FileName = fi.FullName.Substring(_codeGenArgs.OutputPath.FullName.Length + 1) }; + var sr = new SqlSchemaScript { Name = name, Reader = sor, FileName = fi.FullName.Substring(_args.CodeGenArgs.OutputPath.FullName.Length + 1) }; sr.Order = Array.IndexOf(schemaOrder, sr.Reader.Schema); if (sr.Order < 0) sr.Order = schemaOrder.Length; @@ -356,7 +379,7 @@ private async Task DropAndCreateAllObjectsAsync(string[] schemaOrder) // Parse all resources and get ready for the SQL code gen. _logger.LogInformation($"Probing for embedded resources: {string.Join(", ", GetNamespacesWithSuffix($"{SchemaNamespace}.*.sql"))}"); - foreach (var ass in _assemblies) + foreach (var ass in _args.Assemblies) { foreach (var name in ass.GetManifestResourceNames()) { @@ -416,7 +439,7 @@ private async Task DropAndCreateAllObjectsAsync(string[] schemaOrder) /// private string RenameFileToResourceName(FileInfo fi) { - var dir = RenameFileToResourceNameReplace(fi.DirectoryName.Substring(_codeGenArgs.OutputPath!.FullName.Length + 1)); + var dir = RenameFileToResourceNameReplace(fi.DirectoryName.Substring(_args.CodeGenArgs!.OutputPath!.FullName.Length + 1)); var file = RenameFileToResourceNameReplace(fi.Name.Substring(0, fi.Name.Length - fi.Extension.Length)); return dir + "." + file + RenameFileToResourceNameReplace(fi.Extension); } @@ -464,11 +487,11 @@ private async Task InsertOrMergeYamlDataAsync() { // Get all the database table schema information. _logger.LogInformation($"Querying database for all existing table and column configurations..."); - await SqlDataUpdater.RegisterDatabaseAsync(_db, "Ref").ConfigureAwait(false); + await SqlDataUpdater.RegisterDatabaseAsync(_db, _args.RefDataSchemaName).ConfigureAwait(false); // Parse all resources and get ready for the SQL code gen. _logger.LogInformation($"Probing for embedded resources: {(string.Join(", ", GetNamespacesWithSuffix($"{DataNamespace}.*.yaml")))}"); - foreach (var ass in _assemblies) + foreach (var ass in _args.Assemblies) { foreach (var name in ass.GetManifestResourceNames()) { @@ -499,13 +522,13 @@ await sdm.GenerateSqlAsync((a) => /// private async Task CreateScriptNewAsync() { - _codeGenArgs.Parameters.TryGetValue("ScriptNew", out var action); + _args.CodeGenArgs!.Parameters.TryGetValue("ScriptNew", out var action); var di = new DirectoryInfo(Environment.CurrentDirectory); var fi = string.IsNullOrEmpty(action) ? new FileInfo(Path.Combine(di.FullName, MigrationsNamespace, $"{DateTime.Now.ToString("yyyyMMdd-HHmmss", System.Globalization.CultureInfo.InvariantCulture)}-comment-text.sql")) : new FileInfo(Path.Combine(di.FullName, MigrationsNamespace, #pragma warning disable CA1308 // Normalize strings to uppercase; by-design as lowercase is desired. - $"{DateTime.Now.ToString("yyyyMMdd-HHmmss", System.Globalization.CultureInfo.InvariantCulture)}-{action.ToLowerInvariant()}-{(_codeGenArgs.Parameters.TryGetValue("Schema", out var schema) ? schema : "schema")}-{(_codeGenArgs.Parameters.TryGetValue("Table", out var table) ? table : "table")}.sql")); + $"{DateTime.Now.ToString("yyyyMMdd-HHmmss", System.Globalization.CultureInfo.InvariantCulture)}-{action.ToLowerInvariant()}-{(_args.CodeGenArgs.Parameters.TryGetValue("Schema", out var schema) ? schema : "schema")}-{(_args.CodeGenArgs.Parameters.TryGetValue("Table", out var table) ? table : "table")}.sql")); #pragma warning restore CA1308 if (!fi.Directory.Exists) @@ -513,7 +536,7 @@ private async Task CreateScriptNewAsync() using var sr = new StringReader(""); var cg = CodeGenerator.Create(System.Xml.Linq.XElement.Load(sr)); - cg.CopyParameters(_codeGenArgs.Parameters); + cg.CopyParameters(_args.CodeGenArgs.Parameters); cg.CodeGenerated += (s, e) => { File.WriteAllText(fi.FullName, e.Content); @@ -524,7 +547,7 @@ private async Task CreateScriptNewAsync() await cg.GenerateAsync(System.Xml.Linq.XElement.Load(st)).ConfigureAwait(false); } - _logger.LogInformation($"Script file created: {fi.FullName}"); + _logger.LogWarning($"Script file created: {fi.FullName}"); return true; } } diff --git a/tools/Beef.Database.Core/README.md b/tools/Beef.Database.Core/README.md index c92273baa..8456885c0 100644 --- a/tools/Beef.Database.Core/README.md +++ b/tools/Beef.Database.Core/README.md @@ -56,7 +56,7 @@ The data specified follows a basic indenting/levelling rule to enable: ### Reference data -[*Reference Data*](../../docs/Reference-Data.md) is treated as a special case; generally identified by being in the `Ref` schema. The first column name and value pair are treated as the `Code` and `Text` columns. Also the `IsActive` column will automatically be set to `true`, and the `SortOrder` column to the index (1-based) in which it is specified. +[*Reference Data*](../../docs/Reference-Data.md) is treated as a special case. The first column name and value pair are treated as the `Code` and `Text` columns. Also the `IsActive` column will automatically be set to `true`, and the `SortOrder` column to the index (1-based) in which it is specified. Where a column is a *Reference Data* reference the reference data code can be specified, with the identifier being determined at runtime (using a sub-query) as it is unlikely to be known at configuration time. The tooling determines this by the column name being suffixed by `Id` and a corresponding table name in the `Ref` schema; example `GenderId` column and corresponding table `Ref.Gender`. @@ -95,7 +95,7 @@ To simplify the database management here are some further considerations that ma - **Nullable everything** - all columns (except) the primary key should be defined as nullable. The business logic should validate the request to ensure data is provided where mandatory. Makes changes to the database schema easier over time without this constraint. - **Minimise constraints** - do not use database constraints unless absolutely necessary; only leverage where the database is the best and/or most efficient means to perform; i.e. uniqueness. The business logic should validate the request to ensure that any related data is provided, is valid and consistent. -- **No cross-schema referencing** - avoid referencing across `Schemas` where possible as this will impact the Migrations as part of this tooling; and we should not be using constraints as per prior point. Each schema is considered independent of others except `dbo` or `sec` (security where used). +- **No cross-schema referencing** - avoid referencing across `Schemas` where possible as this will impact the Migrations as part of this tooling; and we should not be using constraints as per prior point. Each schema is considered independent of others except special cases, such as `dbo` or `sec` (security where used) for example. - **Standardise column lengths** - use a standard set of column lengths within the database and have the business logic manage the length constraint. As such the column length must be the same or greater that what is required. - **JSON for schema-less** - where there is data that needs to be persisted, but rarely searched on, a schema-less approach should be considered such that a JSON object is persisted versus having to define columns. This can further simplify the database requirements where the data is hierarchical in nature. To enable the [`ObjectToJsonConverter`](../../src/Beef.Core/Mapper/Converters/ObjectToJsonConverter.cs) should be used within the corresponding mapper (e.g. [`DatabasePropertyMapper`](../../src/Beef.Data.Database/DatabasePropertyMapper.cs)). @@ -140,7 +140,8 @@ Command | Description -|- `ScriptNew` | Creates a new (skeleton) script file using the defined naming convention. `ScriptNew -create Schema.Table` | Creates a new table create script file for the named schema and table. -`ScriptNew -alter Schema.Table` | Creates a new table create script file for the named schema and table. +`ScriptNew -createref Schema.Table` | Creates a new reference data table create script file for the named schema and table. +`ScriptNew -alter Schema.Table` | Creates a new table alter script file for the named schema and table.
@@ -158,8 +159,6 @@ public class Program } ``` -
- To automatically added artefacts as embedded resources make the following change to your `.csproj` file: ``` xml @@ -170,12 +169,11 @@ To automatically added artefacts as embedded resources make the following change ``` -
- To run the console application, simply specify the required command; e.g: + ``` dotnet run dropandall dotnet run all dotnet run database -cs "Data Source=.;Initial atalog=Beef.Test;Integrated Security=True" -dotnet run scriptnew -create Ref.Eyecolor +dotnet run scriptnew -createref Ref.Eyecolor ``` \ No newline at end of file diff --git a/tools/Beef.Database.Core/Resources/ScriptNew_sql.xml b/tools/Beef.Database.Core/Resources/ScriptNew_sql.xml index 369008993..5274c51f6 100644 --- a/tools/Beef.Database.Core/Resources/ScriptNew_sql.xml +++ b/tools/Beef.Database.Core/Resources/ScriptNew_sql.xml @@ -6,17 +6,9 @@ BEGIN TRANSACTION ]]> - - - - - - - - - - + + - - - + + - - - - - + + - - - - + + + + + \ No newline at end of file diff --git a/tools/Beef.Database.Core/Sql/SqlDataTable.cs b/tools/Beef.Database.Core/Sql/SqlDataTable.cs index 6d54b6f8e..a6eaa4b83 100644 --- a/tools/Beef.Database.Core/Sql/SqlDataTable.cs +++ b/tools/Beef.Database.Core/Sql/SqlDataTable.cs @@ -35,7 +35,12 @@ public SqlDataTable(string schema, string name) Schema = schema; Name = name; - IsRefData = Schema == SqlDataUpdater.RefDataSchema; + + if (SqlDataUpdater.RefDataSchema != null) + IsRefData = Schema == SqlDataUpdater.RefDataSchema; + else + // Will attempt to infer by checking for specified columns. + IsRefData = DbTable.Columns.Any(x => x.Name == "Code") && DbTable.Columns.Any(x => x.Name == "Text") && DbTable.Columns.Any(x => x.Name == "IsActive") && DbTable.Columns.Any(x => x.Name == "SortOrder"); } /// diff --git a/tools/Beef.Database.Core/Sql/SqlDataUpdater.cs b/tools/Beef.Database.Core/Sql/SqlDataUpdater.cs index 3f9972bcb..ad1382724 100644 --- a/tools/Beef.Database.Core/Sql/SqlDataUpdater.cs +++ b/tools/Beef.Database.Core/Sql/SqlDataUpdater.cs @@ -29,7 +29,7 @@ public class SqlDataUpdater /// /// The . /// The reference data schema. - public static async Task RegisterDatabaseAsync(DatabaseBase db, string refDataSchema) + public static async Task RegisterDatabaseAsync(DatabaseBase db, string? refDataSchema = null) { if (DbTables == null) DbTables = await Table.LoadTablesAndColumnsAsync(db, refDataSchema).ConfigureAwait(false); @@ -42,7 +42,7 @@ public static async Task RegisterDatabaseAsync(DatabaseBase db, string refDataSc ///
/// The tables. /// The reference data schema. - public static void RegisterDatabase(List
tables, string refDataSchema) + public static void RegisterDatabase(List
tables, string? refDataSchema = null) { if (DbTables == null) DbTables = tables; diff --git a/tools/Beef.Test.NUnit/Beef.Test.NUnit.csproj b/tools/Beef.Test.NUnit/Beef.Test.NUnit.csproj index d8f541878..9b437ecc4 100644 --- a/tools/Beef.Test.NUnit/Beef.Test.NUnit.csproj +++ b/tools/Beef.Test.NUnit/Beef.Test.NUnit.csproj @@ -2,7 +2,7 @@ netcoreapp3.1 - 4.1.1 + 4.1.2 Beef Developers Avanade false diff --git a/tools/Beef.Test.NUnit/CHANGELOG.md b/tools/Beef.Test.NUnit/CHANGELOG.md index 303d9fa46..ab05f516f 100644 --- a/tools/Beef.Test.NUnit/CHANGELOG.md +++ b/tools/Beef.Test.NUnit/CHANGELOG.md @@ -2,6 +2,11 @@ Represents the **NuGet** versions. +## v4.1.2 +- *Enhancement:* Added `ValidationTester` to enable simpler unit-tests (with services mocking) of validators external to the API. Provides a simpler and more test run-time performant means to validate versus having to create all the required test data within the underlying data source. +- *Enhancement* Added `ReplaceSingleton`, `ReplaceScoped` and `ReplaceTransient` extension methods to `IServiceCollection` which will replace if existing; otherwise, add. +- *Enhancement:* Moved all subscriber host arguments to `EventSubscriberHostArgs` to centralize and enable simple configuration via DI. + ## v4.1.1 - *Enhancement:* Introduction of Dependency Injection support. diff --git a/tools/Beef.Test.NUnit/ExtensionMethods.cs b/tools/Beef.Test.NUnit/ExtensionMethods.cs index 07367082c..139b10fbb 100644 --- a/tools/Beef.Test.NUnit/ExtensionMethods.cs +++ b/tools/Beef.Test.NUnit/ExtensionMethods.cs @@ -24,10 +24,7 @@ public static class ExtensionMethods /// The value. /// The corresponding . /// Sets the first argument with the and the remainder with zeroes using . - public static Guid ToGuid(this int value) - { - return new Guid(value, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - } + public static Guid ToGuid(this int value) => new Guid(value, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); /// /// Creates a long string by repeating the character for the specified count (defaults to 250). @@ -35,10 +32,7 @@ public static Guid ToGuid(this int value) /// The character value. /// The repeating count. /// The resulting string. - public static string ToLongString(this char value, int count = 250) - { - return new string(value, count); - } + public static string ToLongString(this char value, int count = 250) => new string(value, count); /// /// Extends to simplify the return of a mocked with no result. @@ -48,10 +42,8 @@ public static string ToLongString(this char value, int count = 250) /// The mock object. /// The . /// The with no result. - public static IReturnsResult ReturnsWebApiAgentResultAsync(this IReturns>> mock, HttpStatusCode statusCode = HttpStatusCode.NoContent) where TMock : class - { - return mock.ReturnsAsync(() => new WebApiAgentResult(new HttpResponseMessage() { StatusCode = statusCode })); - } + public static IReturnsResult ReturnsWebApiAgentResultAsync(this IReturns>> mock, HttpStatusCode statusCode = HttpStatusCode.NoContent) where TMock : class => + mock.ReturnsAsync(() => new WebApiAgentResult(new HttpResponseMessage() { StatusCode = statusCode })); /// /// Extends to simplify the return of a mocked with an entity result. @@ -62,10 +54,8 @@ public static IReturnsResult ReturnsWebApiAgentResultAsyncThe entity value. /// The . /// The with an entity result. - public static IReturnsResult ReturnsWebApiAgentResultAsync(this IReturns>> mock, TEntity entity, HttpStatusCode statusCode = HttpStatusCode.OK) where TMock : class - { - return mock.ReturnsAsync(() => new WebApiAgentResult(new HttpResponseMessage(statusCode), entity)); - } + public static IReturnsResult ReturnsWebApiAgentResultAsync(this IReturns>> mock, TEntity entity, HttpStatusCode statusCode = HttpStatusCode.OK) where TMock : class => + mock.ReturnsAsync(() => new WebApiAgentResult(new HttpResponseMessage(statusCode), entity)); /// /// Removes all items from the for the specified . @@ -81,5 +71,50 @@ public static bool Remove(this IServiceCollection services) where TSer var descriptor = services.FirstOrDefault(d => d.ServiceType == typeof(TService)); return descriptor != null && services.Remove(descriptor); } + + /// + /// Replaces (where existing), or adds, a singleton service . + /// + /// The service . + /// The . + /// The instance value. + public static void ReplaceSingleton(this IServiceCollection services, TService instance) where TService : class + { + if (services == null) + throw new ArgumentNullException(nameof(services)); + + services.Remove(); + services.AddSingleton(instance); + } + + /// + /// Replaces (where existing), or adds, a scoped service . + /// + /// The service . + /// The . + /// The instance value. + public static void ReplaceScoped(this IServiceCollection services, TService instance) where TService : class + { + if (services == null) + throw new ArgumentNullException(nameof(services)); + + services.Remove(); + services.AddScoped(_ => instance); + } + + /// + /// Replaces (where existing), or adds, a transient service . + /// + /// The service . + /// The . + /// The instance value. + public static void ReplaceTransient(this IServiceCollection services, TService instance) where TService : class + { + if (services == null) + throw new ArgumentNullException(nameof(services)); + + services.Remove(); + services.AddTransient(_ => instance); + } } } \ No newline at end of file diff --git a/tools/Beef.Test.NUnit/Tests/EventSubscriberTest.cs b/tools/Beef.Test.NUnit/Tests/EventSubscriberTest.cs index a52079859..8e762dc32 100644 --- a/tools/Beef.Test.NUnit/Tests/EventSubscriberTest.cs +++ b/tools/Beef.Test.NUnit/Tests/EventSubscriberTest.cs @@ -128,7 +128,8 @@ await Task.Run(async () => try { ExecutionContext.Reset(); - tesh = new EventSubscriberTestHost(EventSubscriberHostArgs.Create(scope.ServiceProvider, typeof(TSubscriber)).UseLoggerForAuditing()); + var args = EventSubscriberHostArgs.Create(typeof(TSubscriber)).UseServiceProvider(scope.ServiceProvider).UseLoggerForAuditing(); + tesh = new EventSubscriberTestHost(args); sw = Stopwatch.StartNew(); await tesh.ReceiveAsync(@event).ConfigureAwait(false); sw.Stop(); diff --git a/tools/Beef.Test.NUnit/Tests/ExpectValidationExceptionTest.cs b/tools/Beef.Test.NUnit/Tests/ExpectValidationExceptionTest.cs index 14eca066d..902c12add 100644 --- a/tools/Beef.Test.NUnit/Tests/ExpectValidationExceptionTest.cs +++ b/tools/Beef.Test.NUnit/Tests/ExpectValidationExceptionTest.cs @@ -189,11 +189,11 @@ public void CompareExpectedVsActual(MessageItemCollection expectedMessages, Vali public void CompareExpectedVsActual(MessageItemCollection? expectedMessages, MessageItemCollection? actualMessages) { var exp = (from e in expectedMessages ?? new MessageItemCollection() - where !actualMessages.Any(a => a.Type == e.Type && a.Text == e.Text && (e.Property == null || a.Property == e.Property)) + where !(actualMessages ?? new MessageItemCollection()).Any(a => a.Type == e.Type && a.Text == e.Text && (e.Property == null || a.Property == e.Property)) select e).ToList(); var act = (from a in actualMessages ?? new MessageItemCollection() - where !expectedMessages.Any(e => a.Type == e.Type && a.Text == e.Text && (e.Property == null || a.Property == e.Property)) + where !(expectedMessages ?? new MessageItemCollection()).Any(e => a.Type == e.Type && a.Text == e.Text && (e.Property == null || a.Property == e.Property)) select a).ToList(); var sb = new StringBuilder(); diff --git a/tools/Beef.Test.NUnit/ValidationTester.cs b/tools/Beef.Test.NUnit/ValidationTester.cs new file mode 100644 index 000000000..4826f075f --- /dev/null +++ b/tools/Beef.Test.NUnit/ValidationTester.cs @@ -0,0 +1,270 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Beef.Entities; +using Beef.Test.NUnit.Tests; +using Beef.Validation; +using NUnit.Framework; +using System; + +namespace Beef.Test.NUnit +{ + /// + /// Manages the testing of a validator outside of an API execution context with integrated mocking of services as required. + /// + /// is required to enable the reference data to function correctly. + public sealed class ValidationTester : TesterBase + { + private readonly string? _username; + private readonly object? _args; + private OperationType _operationType = Beef.OperationType.Unspecified; + private ErrorType? _expectedErrorType; + private string? _expectedErrorMessage; + private MessageItemCollection? _expectedMessages; + + /// + /// Create a new for a named . + /// + /// The username (null indicates to use the ). + /// Optional argument that can be referenced within the test. + /// A instance. + public static ValidationTester Test(string? username = null, object? args = null) => new ValidationTester(username, args); + + /// + /// Create a new for a named (converted using ). + /// + /// The user identifier (null indicates to use the ). + /// Optional argument that can be referenced within the test. + /// A instance. + public static ValidationTester Test(object? userIdentifier, object? args = null) => new ValidationTester(TestSetUp.ConvertUsername(userIdentifier), args); + + /// + /// Initializes a new instance of the class. + /// + /// The username (null indicates to use the ). + /// Optional argument that can be referenced within the test. + private ValidationTester(string? username = null, object? args = null) : base(true) + { + _username = username; + _args = args; + } + + /// + /// Adds, or replaces (where existing), a singleton service . + /// + /// The service . + /// The instance value. + public ValidationTester AddSingletonService(TService mockInstance) where TService : class + { + ConfigureLocalServices(sc => sc.ReplaceSingleton(mockInstance)); + return this; + } + + /// + /// Adds, or replaces (where existing), a singleton service . + /// + /// The service . + /// The mock. + public ValidationTester AddSingletonService(Moq.Mock mock) where TService : class + { + ConfigureLocalServices(sc => sc.ReplaceSingleton(Check.NotNull(mock, nameof(mock)).Object)); + return this; + } + + /// + /// Adds, or replaces (where existing), a scoped service . + /// + /// The service . + /// The instance value. + public ValidationTester AddScopedService(TService mockInstance) where TService : class + { + ConfigureLocalServices(sc => sc.ReplaceScoped(mockInstance)); + return this; + } + + /// + /// Adds, or replaces (where existing), a scoped service . + /// + /// The service . + /// The mock. + public ValidationTester AddScopedService(Moq.Mock mock) where TService : class + { + ConfigureLocalServices(sc => sc.ReplaceScoped(Check.NotNull(mock, nameof(mock)).Object)); + return this; + } + + /// + /// Adds, or replaces (where existing), a transient service . + /// + /// The service . + /// The instance value. + public ValidationTester AddTransientService(TService mockInstance) where TService : class + { + ConfigureLocalServices(sc => sc.ReplaceTransient(mockInstance)); + return this; + } + + /// + /// Adds, or replaces (where existing), a transient service . + /// + /// The service . + /// The mock. + public ValidationTester AddTransientService(Moq.Mock mock) where TService : class + { + ConfigureLocalServices(sc => sc.ReplaceTransient(Check.NotNull(mock, nameof(mock)).Object)); + return this; + } + + /// + /// Sets the to the specified . + /// + /// The . + /// The instance to support fluent/chaining usage. + public ValidationTester OperationType(OperationType operationType) + { + _operationType = operationType; + return this; + } + + /// + /// Expect the specified . + /// + /// The expected . + /// The expected error message text; where not specified the error message text will not be checked. + /// The instance to support fluent/chaining usage. + public ValidationTester ExpectErrorType(ErrorType errorType, string? errorMessage = null) + { + _expectedErrorType = errorType; + _expectedErrorMessage = errorMessage; + return this; + } + + /// + /// Expect the specified messages. + /// + /// An array of expected message texts. + /// The instance to support fluent/chaining usage. + public ValidationTester ExpectMessages(params string[] messages) + { + var mic = new MessageItemCollection(); + foreach (var text in messages) + { + mic.AddError(text); + } + + ExpectMessages(mic); + return this; + } + + /// + /// Expect the specified messages. + /// + /// The collection. + /// Will only check the where specified (not null). + /// The instance to support fluent/chaining usage. + public ValidationTester ExpectMessages(MessageItemCollection messages) + { + Check.NotNull(messages, nameof(messages)); + if (_expectedMessages == null) + _expectedMessages = new MessageItemCollection(); + + _expectedMessages.AddRange(messages); + return this; + } + + /// + /// Indicates whether any errors are expected. + /// + private bool IsExpectingError => _expectedErrorType.HasValue || _expectedErrorMessage != null || _expectedMessages != null; + + /// + /// Runs the checking against the expected outcomes. + /// + /// The function to execute. + /// The resulting where applicable; otherwise, null. + public IValidationContextBase? Run(Func func) + { + PrepareExecutionContext(_username, _args); + ExecutionContext.Current.OperationType = _operationType; + + if (IsExpectingError && !_expectedErrorType.HasValue) + _expectedErrorType = ErrorType.ValidationError; + + try + { + var vc = Check.NotNull(func, nameof(func))(); + if (vc == null) + Assert.Fail("The validation function returned a null IValidationContext result."); + + if (vc!.HasErrors) + LogStatus(ErrorType.ValidationError, new LText("Beef.ValidationException"), vc.Messages); + else + LogStatus(null, null, null); + + if (_expectedErrorType.HasValue) + { + if (!vc!.HasErrors && _expectedErrorType != ErrorType.ValidationError) + Assert.Fail($"Expected ErrorType was '{_expectedErrorType}'; actual was '{ErrorType.ValidationError}'."); + + if (_expectedErrorMessage != null) + { + var message = new LText("Beef.ValidationException"); + if (_expectedErrorMessage != message) + Assert.Fail($"Expected ErrorMessage was '{_expectedErrorMessage}'; actual was '{message}'."); + } + } + + if (_expectedMessages != null) + ExpectValidationException.CompareExpectedVsActual(_expectedMessages, vc!.Messages); + + return vc; + } + catch (AssertionException) { throw; } + catch (Exception ex) + { + bool errorTypeOK = false; + if (ex is IBusinessException ibex) + { + LogStatus(ibex.ErrorType, ex.Message, ex is ValidationException vex ? vex.Messages : null); + + if (_expectedErrorType.HasValue && _expectedErrorType != ibex.ErrorType) + Assert.Fail($"Expected ErrorType was '{_expectedErrorType}'; actual was '{ibex.ErrorType}'."); + + if (_expectedErrorMessage != null && _expectedErrorMessage != ex.Message) + Assert.Fail($"Expected ErrorMessage was '{_expectedErrorMessage}'; actual was '{ex.Message}'."); + + errorTypeOK = true; + if (_expectedMessages != null) + ExpectValidationException.CompareExpectedVsActual(_expectedMessages, ex is ValidationException vexx ? vexx.Messages : null); + } + + if (IsExpectingError && errorTypeOK) + return null; + + throw; + } + } + + /// + /// Log the status. + /// + private static void LogStatus(ErrorType? errorType, string? errorMessage, MessageItemCollection? mic) + { + TestContext.Out.WriteLine(""); + TestContext.Out.WriteLine("VALIDATION TESTER..."); + TestContext.Out.WriteLine(""); + TestContext.Out.WriteLine($"ErrorType: {(errorType == null ? "none" : errorType.ToString())}"); + TestContext.Out.WriteLine($"ErrorMessage: {(errorMessage ?? "none")}"); + TestContext.Out.WriteLine($"Messages: {(mic == null || mic.Count == 0 ? "none" : "")}"); + + if (mic != null && mic.Count > 0) + { + foreach (var m in mic) + { + TestContext.Out.WriteLine($" {m.Type}: {m.Text} {(m.Property == null ? "" : "(" + m.Property + ")")}"); + } + + TestContext.Out.WriteLine(""); + } + } + } +} \ No newline at end of file