From 06df96fd1a156ab1930a18efa82bf3e9c33b8d43 Mon Sep 17 00:00:00 2001 From: chullybun Date: Tue, 20 Feb 2024 10:22:16 -0800 Subject: [PATCH] Added PostgreSQL support. --- Beef.sln | 7 ++ CHANGELOG.md | 6 ++ Common.targets | 2 +- README.md | 6 +- docs/Database-CodeGeneration-Config.md | 10 +++ nuget-publish.ps1 | 1 + .../Cdr.Banking.Api/Cdr.Banking.Api.csproj | 2 +- .../Cdr.Banking.Business.csproj | 6 +- .../Cdr.Banking.Common.csproj | 2 +- .../Cdr.Banking.Test/Cdr.Banking.Test.csproj | 6 +- .../Demo/Beef.Demo.Api/Beef.Demo.Api.csproj | 2 +- .../Beef.Demo.Business.csproj | 16 ++--- .../Data/EfModel/Generated/Contact.cs | 2 +- .../Data/EfModel/Generated/EyeColor.cs | 2 +- .../Data/EfModel/Generated/Gender.cs | 2 +- .../Data/EfModel/Generated/Person.cs | 2 +- .../Data/EfModel/Generated/Status.cs | 2 +- .../Data/EfModel/Generated/Table.cs | 2 +- .../Beef.Demo.Common/Beef.Demo.Common.csproj | 4 +- .../Demo/Beef.Demo.Test/Beef.Demo.Test.csproj | 8 +-- samples/My.Hr/My.Hr.Api/My.Hr.Api.csproj | 2 +- .../Data/EfModel/Generated/Employee.cs | 2 +- .../Data/EfModel/Generated/Gender.cs | 2 +- .../EfModel/Generated/PerformanceOutcome.cs | 2 +- .../EfModel/Generated/PerformanceReview.cs | 2 +- .../EfModel/Generated/RelationshipType.cs | 2 +- .../EfModel/Generated/TerminationReason.cs | 2 +- .../Data/EfModel/Generated/USState.cs | 2 +- .../My.Hr.Business/My.Hr.Business.csproj | 8 +-- .../My.Hr/My.Hr.Common/My.Hr.Common.csproj | 2 +- samples/My.Hr/My.Hr.Test/My.Hr.Test.csproj | 8 +-- .../MyEf.Hr/MyEf.Hr.Api/MyEf.Hr.Api.csproj | 6 +- samples/MyEf.Hr/MyEf.Hr.Api/Startup.cs | 5 +- .../EfModel/Generated/EmergencyContact.cs | 2 +- .../Data/EfModel/Generated/Employee.cs | 2 +- .../Data/EfModel/Generated/Gender.cs | 2 +- .../EfModel/Generated/PerformanceOutcome.cs | 2 +- .../EfModel/Generated/PerformanceReview.cs | 2 +- .../EfModel/Generated/RelationshipType.cs | 2 +- .../EfModel/Generated/TerminationReason.cs | 2 +- .../Data/EfModel/Generated/USState.cs | 2 +- .../MyEf.Hr.Business/MyEf.Hr.Business.csproj | 8 +-- .../MyEf.Hr.Common/MyEf.Hr.Common.csproj | 2 +- .../MyEf.Hr.Security.Subscriptions.csproj | 12 ++-- .../MyEf.Hr.Security.Subscriptions/Startup.cs | 2 +- .../EmployeeTerminatedSubcriber.cs | 13 +--- .../MyEf.Hr.Security.Test.csproj | 6 +- .../MyEf.Hr/MyEf.Hr.Test/MyEf.Hr.Test.csproj | 8 +-- .../MyEf.Hr/docs/10-Service-Bus-Subscribe.md | 19 ++---- samples/MyEf.Hr/docs/5-Employee-Search.md | 2 +- samples/MyEf.Hr/docs/7-Performance-Review.md | 7 +- templates/Beef.Template.Solution/README.md | 5 +- .../content/.template.config/template.json | 45 ++++++++++--- .../Company.AppName.Api.csproj | 2 + .../Company.AppName.Api/GlobalUsings.cs | 16 +++-- .../content/Company.AppName.Api/Program.cs | 29 ++++---- .../content/Company.AppName.Api/Startup.cs | 13 ++-- .../Company.AppName.Api/appsettings.json | 6 ++ .../Company.AppName.Business.csproj | 6 ++ .../Data/AppNameDb.cs | 13 +++- .../Data/AppNameEfDbContext.cs | 3 + .../Data/PersonData.cs | 11 +++- .../Company.AppName.Business/GlobalUsings.cs | 6 ++ .../entity.beef-5.yaml | 5 +- .../refdata.beef-5.yaml | 8 +++ .../Company.AppName.Database.csproj | 3 + .../Data/RefData.yaml | 7 +- .../20190101-000001-create-AppName-schema.sql | 2 - .../20190101-000001-create-gender.sql | 8 +-- ...20190101-000001-create-lowerapp-schema.sql | 7 ++ .../20190101-000002-create-AppName-Gender.sql | 16 ----- ...20190101-000002-create-lowerapp-gender.sql | 27 ++++++++ .../20190101-000002-create-person.sql | 10 +-- .../20190101-000003-create-AppName-Person.sql | 18 ----- ...20190101-000003-create-lowerapp-person.sql | 27 ++++++++ .../Company.AppName.Database/Program.cs | 9 ++- .../database.beef-5.yaml | 23 +++++-- .../Company.AppName.Test/Apis/FixtureSetup.cs | 3 + .../Company.AppName.Test/Apis/PersonTest.cs | 62 +++++++++--------- .../Company.AppName.Test/Data/Data.yaml | 5 +- .../Company.AppName.Test/GlobalUsings.cs | 3 + .../A280_GetByArgs_RefDataText-Response.json | 8 +-- .../Validators/PersonValidatorTest.cs | 4 +- .../Beef.Template.Solution.UnitTest.csproj | 2 +- .../TemplateTest.cs | 9 ++- .../Beef.CodeGen.Core.csproj | 2 +- .../Config/Database/CodeGenConfig.cs | 46 ++++++++++--- .../Schema/database.beef-5.json | 8 +++ .../Beef.Database.Core.csproj | 4 +- .../Templates/DbEfModel_cs.hbs | 2 +- .../Beef.Database.MySql.csproj | 4 +- .../Beef.Database.Postgres.csproj | 37 +++++++++++ .../PostgresMigration.cs | 59 +++++++++++++++++ .../PostgresMigrationConsole.cs | 60 +++++++++++++++++ .../Scripts/Database.yaml | 4 ++ .../strong-name-key.snk | Bin 0 -> 596 bytes .../Beef.Database.SqlServer.csproj | 4 +- tools/Beef.Test.NUnit/Beef.Test.NUnit.csproj | 2 +- 98 files changed, 618 insertions(+), 265 deletions(-) delete mode 100644 templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000001-create-AppName-schema.sql create mode 100644 templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000001-create-lowerapp-schema.sql delete mode 100644 templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000002-create-AppName-Gender.sql create mode 100644 templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000002-create-lowerapp-gender.sql delete mode 100644 templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000003-create-AppName-Person.sql create mode 100644 templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000003-create-lowerapp-person.sql create mode 100644 tools/Beef.Database.Postgres/Beef.Database.Postgres.csproj create mode 100644 tools/Beef.Database.Postgres/PostgresMigration.cs create mode 100644 tools/Beef.Database.Postgres/PostgresMigrationConsole.cs create mode 100644 tools/Beef.Database.Postgres/Scripts/Database.yaml create mode 100644 tools/Beef.Database.Postgres/strong-name-key.snk diff --git a/Beef.sln b/Beef.sln index 60f77a891..85c6a26cf 100644 --- a/Beef.sln +++ b/Beef.sln @@ -201,6 +201,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyEf.Hr.Security.Subscripti EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyEf.Hr.Security.Test", "samples\MyEf.Hr\MyEf.Hr.Security.Test\MyEf.Hr.Security.Test.csproj", "{7AE6E1D4-8621-4FC5-8A1F-448FA70DB257}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Beef.Database.Postgres", "tools\Beef.Database.Postgres\Beef.Database.Postgres.csproj", "{1FC9C576-99C9-44B2-AD86-9B8A0C76D79E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -339,6 +341,10 @@ Global {7AE6E1D4-8621-4FC5-8A1F-448FA70DB257}.Debug|Any CPU.Build.0 = Debug|Any CPU {7AE6E1D4-8621-4FC5-8A1F-448FA70DB257}.Release|Any CPU.ActiveCfg = Release|Any CPU {7AE6E1D4-8621-4FC5-8A1F-448FA70DB257}.Release|Any CPU.Build.0 = Release|Any CPU + {1FC9C576-99C9-44B2-AD86-9B8A0C76D79E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1FC9C576-99C9-44B2-AD86-9B8A0C76D79E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1FC9C576-99C9-44B2-AD86-9B8A0C76D79E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1FC9C576-99C9-44B2-AD86-9B8A0C76D79E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -393,6 +399,7 @@ Global {D9DF805A-DD48-4036-B383-F1BC895C317C} = {5FC1E59C-A8C9-4CD7-9DAB-344E877BE8EC} {EB2AD65F-BE16-4334-BFBE-719A88D118D7} = {D9DF805A-DD48-4036-B383-F1BC895C317C} {7AE6E1D4-8621-4FC5-8A1F-448FA70DB257} = {D9DF805A-DD48-4036-B383-F1BC895C317C} + {1FC9C576-99C9-44B2-AD86-9B8A0C76D79E} = {2733948C-DAB3-45B6-BE60-64BA3A5289F5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {42D71086-61E6-4D31-B4B8-BFC8CC471428} diff --git a/CHANGELOG.md b/CHANGELOG.md index f8e8d65b5..0ee7dc84a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ Represents the **NuGet** versions. +## v5.10.0 +- *Enhancement:* Added [PostgreSQL](https://www.postgresql.org/) database support: + - Leverages both `CoreEx.Database.Postgres` (runtime) and `DbEx.Postgres` (migration) packages; encapsulates the `Npgsql` package. + - The `Npgsql.EntityFrameworkCore.PostgreSQL` packages is used for the entity framework provider. + - The `dotnet new beef` template updated to support new `datasource` option of `postgres`. + ## v5.9.1 - *Fixed:* Simplified YAML specification where _only_ a custom manager is required to be implemented. For an `operation` set `type: CustomManagerOnly`, this is a shorthand for `type: Custom, managerCustom: true, excludeIDataSvc: true, excludeDataSvc: true, excludeIData: true, excludeData: true` (i.e. these other properties will no longer need to be set explicitly). diff --git a/Common.targets b/Common.targets index 963397824..ad021ced7 100644 --- a/Common.targets +++ b/Common.targets @@ -1,6 +1,6 @@ - 5.9.1 + 5.10.0 preview Avanade Avanade diff --git a/README.md b/README.md index 8e25ab34c..0837db94a 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ The key concepts are as follows: - **Channel-agnostic** - the APIs are based around the key entities and the operations that can be performed on them: - APIs represent the key trust boundary; as such, they make no assumptions on the consumer. The APIs will always validate the request data, and house the application’s functional business and orchestration rules. - APIs should not be developed to service a specific user interface interaction; as the APIs are agnostic to the consumer. The consumer has the responsibility of coordinating across API calls. + - **Domain-based** – the APIs are based around, and encapsulate, the capabilities for a functional domain: - Outcome of a [Domain-Driven Design](https://en.wikipedia.org/wiki/Domain-driven_design); divides capapabilities into different [Bounded Contexts](https://www.martinfowler.com/bliki/BoundedContext.html). - Encourages micro vs monolithic services. @@ -167,8 +168,9 @@ Assembly | Description | NuGet -|-|- [`Beef.CodeGen.Core`](./tools/Beef.CodeGen.Core) | Code generation console tool. | [![NuGet version](https://badge.fury.io/nu/Beef.CodeGen.Core.svg)](https://badge.fury.io/nu/Beef.CodeGen.Core) [`Beef.Database.Core`](./tools/Beef.Database.Core) | Database and data management console tool. | [![NuGet version](https://badge.fury.io/nu/Beef.Database.Core.svg)](https://badge.fury.io/nu/Beef.Database.Core) -[`Beef.Database.SqlServer`](./tools/Beef.Database.SqlServer) | SQL Server database and data management console tool. | [![NuGet version](https://badge.fury.io/nu/Beef.Database.SqlServer.svg)](https://badge.fury.io/nu/Beef.Database.SqlServer) [`Beef.Database.MySql`](./tools/Beef.Database.MySql) | MySQL database and data management console tool. | [![NuGet version](https://badge.fury.io/nu/Beef.Database.MySql.svg)](https://badge.fury.io/nu/Beef.Database.MySql) +[`Beef.Database.Postgres`](./tools/Beef.Database.Postgres) | PostgreSQL database and data management console tool. | [![NuGet version](https://badge.fury.io/nu/Beef.Database.Postgres.svg)](https://badge.fury.io/nu/Beef.Database.Postgres) +[`Beef.Database.SqlServer`](./tools/Beef.Database.SqlServer) | SQL Server database and data management console tool. | [![NuGet version](https://badge.fury.io/nu/Beef.Database.SqlServer.svg)](https://badge.fury.io/nu/Beef.Database.SqlServer) [`Beef.Template.Solution`](./templates/Beef.Template.Solution) | Solution and projects template. | [![NuGet version](https://badge.fury.io/nu/Beef.Template.Solution.svg)](https://badge.fury.io/nu/Beef.Template.Solution) The following is provided to support a level of version 4.x backwards compatibility. @@ -241,6 +243,8 @@ The following are references to additional documentation (these are all accessib - Execute - [YAML/JSON](./docs/Database-Execute-Config.md) - Relationship (EF) - [YAML/JSON](./docs/Database-Relationship-Config.md) +
+ ### External links of potential interest - Versioning - [article](https://mathieu.fenniak.net/aint-nobody-got-time-for-that-api-versioning/), [implementation](https://github.com/dotnet/aspnet-api-versioning) - _Beef_ has no specific support or opinion with respect to versioning approach and/or implementation. diff --git a/docs/Database-CodeGeneration-Config.md b/docs/Database-CodeGeneration-Config.md index abfc02814..9416468d9 100644 --- a/docs/Database-CodeGeneration-Config.md +++ b/docs/Database-CodeGeneration-Config.md @@ -10,6 +10,7 @@ The `CodeGeneration` object supports a number of properties that control the gen Category | Description -|- [`Infer`](#Infer) | Provides the _special Column Name inference_ configuration. +[`Columns`](#Columns) | Provides the _Columns_ configuration. [`Path`](#Path) | Provides the _Path (Directory)_ configuration for the generated artefacts. [`DotNet`](#DotNet) | Provides the _.NET_ configuration. [`EntityFramework`](#EntityFramework) | Provides the _Entity Framework (EF) model_ configuration. @@ -43,6 +44,15 @@ Property | Description
+## Columns +Provides the _Columns_ configuration. + +Property | Description +-|- +`aliasColumns` | The list of `Column` and `Alias` pairs (split by a `^` lookup character) to enable column aliasing/renaming.
† Each alias value should be formatted as `Column` + `^` + `Alias`; e.g. `PCODE^ProductCode`. + +
+ ## Path Provides the _Path (Directory)_ configuration for the generated artefacts. diff --git a/nuget-publish.ps1 b/nuget-publish.ps1 index 66657bbfc..49bb8b891 100644 --- a/nuget-publish.ps1 +++ b/nuget-publish.ps1 @@ -51,6 +51,7 @@ param( "tools\Beef.CodeGen.Core", "tools\Beef.Database.Core", "tools\Beef.Database.MySql", + "tools\Beef.Database.Postgres", "tools\Beef.Database.SqlServer", "tools\Beef.Test.NUnit", "templates\Beef.Template.Solution") diff --git a/samples/Cdr.Banking/Cdr.Banking.Api/Cdr.Banking.Api.csproj b/samples/Cdr.Banking/Cdr.Banking.Api/Cdr.Banking.Api.csproj index 6d72eb9a9..c14fba137 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Api/Cdr.Banking.Api.csproj +++ b/samples/Cdr.Banking/Cdr.Banking.Api/Cdr.Banking.Api.csproj @@ -5,7 +5,7 @@ true
- + diff --git a/samples/Cdr.Banking/Cdr.Banking.Business/Cdr.Banking.Business.csproj b/samples/Cdr.Banking/Cdr.Banking.Business/Cdr.Banking.Business.csproj index 8c7750120..2cae0b47c 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Business/Cdr.Banking.Business.csproj +++ b/samples/Cdr.Banking/Cdr.Banking.Business/Cdr.Banking.Business.csproj @@ -11,8 +11,8 @@ - - - + + +
\ No newline at end of file diff --git a/samples/Cdr.Banking/Cdr.Banking.Common/Cdr.Banking.Common.csproj b/samples/Cdr.Banking/Cdr.Banking.Common/Cdr.Banking.Common.csproj index 543ed8ce2..07494c51f 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Common/Cdr.Banking.Common.csproj +++ b/samples/Cdr.Banking/Cdr.Banking.Common/Cdr.Banking.Common.csproj @@ -8,6 +8,6 @@ - + \ No newline at end of file diff --git a/samples/Cdr.Banking/Cdr.Banking.Test/Cdr.Banking.Test.csproj b/samples/Cdr.Banking/Cdr.Banking.Test/Cdr.Banking.Test.csproj index 926c3e41b..5f41dded7 100644 --- a/samples/Cdr.Banking/Cdr.Banking.Test/Cdr.Banking.Test.csproj +++ b/samples/Cdr.Banking/Cdr.Banking.Test/Cdr.Banking.Test.csproj @@ -34,12 +34,12 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/samples/Demo/Beef.Demo.Api/Beef.Demo.Api.csproj b/samples/Demo/Beef.Demo.Api/Beef.Demo.Api.csproj index fb0f0e6a0..d0dffacbf 100644 --- a/samples/Demo/Beef.Demo.Api/Beef.Demo.Api.csproj +++ b/samples/Demo/Beef.Demo.Api/Beef.Demo.Api.csproj @@ -12,7 +12,7 @@ - + diff --git a/samples/Demo/Beef.Demo.Business/Beef.Demo.Business.csproj b/samples/Demo/Beef.Demo.Business/Beef.Demo.Business.csproj index 7f2c92956..7687fb01e 100644 --- a/samples/Demo/Beef.Demo.Business/Beef.Demo.Business.csproj +++ b/samples/Demo/Beef.Demo.Business/Beef.Demo.Business.csproj @@ -15,14 +15,14 @@ - - - - - - - - + + + + + + + + diff --git a/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Contact.cs b/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Contact.cs index 479decfc9..e0417c82f 100644 --- a/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Contact.cs +++ b/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Contact.cs @@ -7,7 +7,7 @@ namespace Beef.Demo.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Demo].[Contact]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Demo].[Contact]. /// public partial class Contact : ILogicallyDeleted { diff --git a/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/EyeColor.cs b/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/EyeColor.cs index d2c84a074..7ecc49503 100644 --- a/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/EyeColor.cs +++ b/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/EyeColor.cs @@ -7,7 +7,7 @@ namespace Beef.Demo.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Ref].[EyeColor]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Ref].[EyeColor]. /// public partial class EyeColor { diff --git a/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Gender.cs b/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Gender.cs index 2c94cd300..b3d99ef30 100644 --- a/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Gender.cs +++ b/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Gender.cs @@ -7,7 +7,7 @@ namespace Beef.Demo.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Ref].[Gender]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Ref].[Gender]. /// public partial class Gender { diff --git a/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Person.cs b/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Person.cs index 65cc2c426..a3e6c2d34 100644 --- a/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Person.cs +++ b/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Person.cs @@ -7,7 +7,7 @@ namespace Beef.Demo.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Demo].[Person]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Demo].[Person]. /// public partial class Person { diff --git a/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Status.cs b/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Status.cs index 90d98d04e..1841aa07a 100644 --- a/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Status.cs +++ b/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Status.cs @@ -7,7 +7,7 @@ namespace Beef.Demo.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Ref].[Status]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Ref].[Status]. /// public partial class Status { diff --git a/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Table.cs b/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Table.cs index fb27348a0..fd2dbb89d 100644 --- a/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Table.cs +++ b/samples/Demo/Beef.Demo.Business/Data/EfModel/Generated/Table.cs @@ -7,7 +7,7 @@ namespace Beef.Demo.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Test].[Table]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Test].[Table]. /// public partial class Table : ILogicallyDeleted, ITenantId { diff --git a/samples/Demo/Beef.Demo.Common/Beef.Demo.Common.csproj b/samples/Demo/Beef.Demo.Common/Beef.Demo.Common.csproj index 6cc75b5f1..b2fb73a1e 100644 --- a/samples/Demo/Beef.Demo.Common/Beef.Demo.Common.csproj +++ b/samples/Demo/Beef.Demo.Common/Beef.Demo.Common.csproj @@ -7,8 +7,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/samples/Demo/Beef.Demo.Test/Beef.Demo.Test.csproj b/samples/Demo/Beef.Demo.Test/Beef.Demo.Test.csproj index dc2a74639..b658ac063 100644 --- a/samples/Demo/Beef.Demo.Test/Beef.Demo.Test.csproj +++ b/samples/Demo/Beef.Demo.Test/Beef.Demo.Test.csproj @@ -52,8 +52,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -61,8 +61,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/samples/My.Hr/My.Hr.Api/My.Hr.Api.csproj b/samples/My.Hr/My.Hr.Api/My.Hr.Api.csproj index 42442e9c1..0116e190e 100644 --- a/samples/My.Hr/My.Hr.Api/My.Hr.Api.csproj +++ b/samples/My.Hr/My.Hr.Api/My.Hr.Api.csproj @@ -5,7 +5,7 @@ true - + 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 index f58e948f0..0ea01900f 100644 --- a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/Employee.cs +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/Employee.cs @@ -7,7 +7,7 @@ namespace My.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[Employee]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[Employee]. /// public partial class Employee { 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 index 545781729..f70d7919a 100644 --- a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/Gender.cs +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/Gender.cs @@ -7,7 +7,7 @@ namespace My.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[Gender]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[Gender]. /// public partial class Gender { 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 index 04f3f852e..f3b1d1300 100644 --- a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/PerformanceOutcome.cs +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/PerformanceOutcome.cs @@ -7,7 +7,7 @@ namespace My.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[PerformanceOutcome]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[PerformanceOutcome]. /// public partial class PerformanceOutcome { 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 index d2e609a33..38f348463 100644 --- a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/PerformanceReview.cs +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/PerformanceReview.cs @@ -7,7 +7,7 @@ namespace My.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[PerformanceReview]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[PerformanceReview]. /// public partial class PerformanceReview { 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 index 9fb41eb99..eb12963d5 100644 --- a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/RelationshipType.cs +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/RelationshipType.cs @@ -7,7 +7,7 @@ namespace My.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[RelationshipType]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[RelationshipType]. /// public partial class RelationshipType { 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 index 9d1c3ee92..07672192c 100644 --- a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/TerminationReason.cs +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/TerminationReason.cs @@ -7,7 +7,7 @@ namespace My.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[TerminationReason]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[TerminationReason]. /// public partial class TerminationReason { 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 index 2782cbdd7..8439b8edb 100644 --- a/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/USState.cs +++ b/samples/My.Hr/My.Hr.Business/Data/EfModel/Generated/USState.cs @@ -7,7 +7,7 @@ namespace My.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[USState]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[USState]. /// public partial class USState { diff --git a/samples/My.Hr/My.Hr.Business/My.Hr.Business.csproj b/samples/My.Hr/My.Hr.Business/My.Hr.Business.csproj index b18509098..3a93a3e81 100644 --- a/samples/My.Hr/My.Hr.Business/My.Hr.Business.csproj +++ b/samples/My.Hr/My.Hr.Business/My.Hr.Business.csproj @@ -5,10 +5,10 @@ true - - - - + + + + \ 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 index e1b92e8ca..750a7eae6 100644 --- a/samples/My.Hr/My.Hr.Common/My.Hr.Common.csproj +++ b/samples/My.Hr/My.Hr.Common/My.Hr.Common.csproj @@ -4,6 +4,6 @@ enable - + \ 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 index 1a31bd87c..5a18ac25d 100644 --- a/samples/My.Hr/My.Hr.Test/My.Hr.Test.csproj +++ b/samples/My.Hr/My.Hr.Test/My.Hr.Test.csproj @@ -32,17 +32,17 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/samples/MyEf.Hr/MyEf.Hr.Api/MyEf.Hr.Api.csproj b/samples/MyEf.Hr/MyEf.Hr.Api/MyEf.Hr.Api.csproj index 4e3c4d169..54595fc4a 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Api/MyEf.Hr.Api.csproj +++ b/samples/MyEf.Hr/MyEf.Hr.Api/MyEf.Hr.Api.csproj @@ -6,11 +6,11 @@ True - - + + - + diff --git a/samples/MyEf.Hr/MyEf.Hr.Api/Startup.cs b/samples/MyEf.Hr/MyEf.Hr.Api/Startup.cs index e4e1654ae..87008bf69 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Api/Startup.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Api/Startup.cs @@ -21,9 +21,6 @@ public class Startup /// The . public void ConfigureServices(IServiceCollection services) { - if (services == null) - throw new ArgumentNullException(nameof(services)); - // Add the core services. services.AddSettings() .AddExecutionContext() @@ -81,7 +78,7 @@ public void ConfigureServices(IServiceCollection services) services.AddHttpClient(); // Add Azure monitor open telemetry. - services.AddOpenTelemetry().UseAzureMonitor(); + services.AddOpenTelemetry().UseAzureMonitor().WithTracing(b => b.AddSource()); services.Configure(options => options.SetDbStatementForText = true); services.ConfigureOpenTelemetryTracerProvider((sp, builder) => builder.AddSource("CoreEx.*", "MyEf.Hr.*", "Microsoft.EntityFrameworkCore.*", "EntityFrameworkCore.*")); diff --git a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/EmergencyContact.cs b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/EmergencyContact.cs index 0e68cbb75..617f83dad 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/EmergencyContact.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/EmergencyContact.cs @@ -7,7 +7,7 @@ namespace MyEf.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[EmergencyContact]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[EmergencyContact]. /// public partial class EmergencyContact { diff --git a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/Employee.cs b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/Employee.cs index f5959d009..d2ed45105 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/Employee.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/Employee.cs @@ -7,7 +7,7 @@ namespace MyEf.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[Employee]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[Employee]. /// public partial class Employee { diff --git a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/Gender.cs b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/Gender.cs index 54fdebca0..134f496d9 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/Gender.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/Gender.cs @@ -7,7 +7,7 @@ namespace MyEf.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[Gender]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[Gender]. /// public partial class Gender { diff --git a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/PerformanceOutcome.cs b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/PerformanceOutcome.cs index ec0007179..5abe58edf 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/PerformanceOutcome.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/PerformanceOutcome.cs @@ -7,7 +7,7 @@ namespace MyEf.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[PerformanceOutcome]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[PerformanceOutcome]. /// public partial class PerformanceOutcome { diff --git a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/PerformanceReview.cs b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/PerformanceReview.cs index d9831cd7d..6e7758d94 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/PerformanceReview.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/PerformanceReview.cs @@ -7,7 +7,7 @@ namespace MyEf.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[PerformanceReview]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[PerformanceReview]. /// public partial class PerformanceReview { diff --git a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/RelationshipType.cs b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/RelationshipType.cs index d246ac1c1..2330e5320 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/RelationshipType.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/RelationshipType.cs @@ -7,7 +7,7 @@ namespace MyEf.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[RelationshipType]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[RelationshipType]. /// public partial class RelationshipType { diff --git a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/TerminationReason.cs b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/TerminationReason.cs index ba47ae85b..a0c6dd673 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/TerminationReason.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/TerminationReason.cs @@ -7,7 +7,7 @@ namespace MyEf.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[TerminationReason]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[TerminationReason]. /// public partial class TerminationReason { diff --git a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/USState.cs b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/USState.cs index d4a149465..6784250b8 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/USState.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Business/Data/EfModel/Generated/USState.cs @@ -7,7 +7,7 @@ namespace MyEf.Hr.Business.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '[Hr].[USState]'. +/// Represents the Entity Framework (EF) model for SqlServer database object [Hr].[USState]. /// public partial class USState { diff --git a/samples/MyEf.Hr/MyEf.Hr.Business/MyEf.Hr.Business.csproj b/samples/MyEf.Hr/MyEf.Hr.Business/MyEf.Hr.Business.csproj index 36853e3dc..3a229ab85 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Business/MyEf.Hr.Business.csproj +++ b/samples/MyEf.Hr/MyEf.Hr.Business/MyEf.Hr.Business.csproj @@ -5,10 +5,10 @@ true - - - - + + + + diff --git a/samples/MyEf.Hr/MyEf.Hr.Common/MyEf.Hr.Common.csproj b/samples/MyEf.Hr/MyEf.Hr.Common/MyEf.Hr.Common.csproj index e1b92e8ca..750a7eae6 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Common/MyEf.Hr.Common.csproj +++ b/samples/MyEf.Hr/MyEf.Hr.Common/MyEf.Hr.Common.csproj @@ -4,6 +4,6 @@ enable - + \ No newline at end of file diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/MyEf.Hr.Security.Subscriptions.csproj b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/MyEf.Hr.Security.Subscriptions.csproj index c95ac931c..a4419beba 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/MyEf.Hr.Security.Subscriptions.csproj +++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/MyEf.Hr.Security.Subscriptions.csproj @@ -15,12 +15,12 @@ - - - - - - + + + + + + diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Startup.cs b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Startup.cs index ea455214f..b25ad840b 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Startup.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Startup.cs @@ -28,7 +28,7 @@ public override void ConfigureServices(IServiceCollection services) }) .AddAzureServiceBusOrchestratedSubscriber((_, o) => { - o.EventDataDeserializationErrorHandling = ErrorHandling.Handle; + o.EventDataDeserializationErrorHandling = ErrorHandling.HandleBySubscriber; }) .AddTypedHttpClient("OktaApi"); } diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Subscribers/EmployeeTerminatedSubcriber.cs b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Subscribers/EmployeeTerminatedSubcriber.cs index 11a8f387c..f5142da09 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Subscribers/EmployeeTerminatedSubcriber.cs +++ b/samples/MyEf.Hr/MyEf.Hr.Security.Subscriptions/Subscribers/EmployeeTerminatedSubcriber.cs @@ -1,22 +1,15 @@ namespace MyEf.Hr.Security.Subscriptions.Subscribers; [EventSubscriber("MyEf.Hr.Employee", "Terminated")] -public class EmployeeTerminatedSubcriber : SubscriberBase +public class EmployeeTerminatedSubcriber(OktaHttpClient okta, ILogger logger) : SubscriberBase(_employeeValidator) { private static readonly Validator _employeeValidator = Validator.Create() .HasProperty(x => x.Id, p => p.Mandatory()) .HasProperty(x => x.Email, p => p.Mandatory().Email()) .HasProperty(x => x.Termination, p => p.Mandatory()); - private readonly OktaHttpClient _okta; - private readonly ILogger _logger; - - public EmployeeTerminatedSubcriber(OktaHttpClient okta, ILogger logger) - { - _okta = okta ?? throw new ArgumentNullException(nameof(okta)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - ValueValidator = _employeeValidator; - } + private readonly OktaHttpClient _okta = okta.ThrowIfNull(); + private readonly ILogger _logger = logger.ThrowIfNull(); public override ErrorHandling SecurityHandling => ErrorHandling.Retry; diff --git a/samples/MyEf.Hr/MyEf.Hr.Security.Test/MyEf.Hr.Security.Test.csproj b/samples/MyEf.Hr/MyEf.Hr.Security.Test/MyEf.Hr.Security.Test.csproj index e5512da1b..e4879f827 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Security.Test/MyEf.Hr.Security.Test.csproj +++ b/samples/MyEf.Hr/MyEf.Hr.Security.Test/MyEf.Hr.Security.Test.csproj @@ -16,10 +16,10 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -27,7 +27,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/samples/MyEf.Hr/MyEf.Hr.Test/MyEf.Hr.Test.csproj b/samples/MyEf.Hr/MyEf.Hr.Test/MyEf.Hr.Test.csproj index 21c63c6e7..82b5dd2da 100644 --- a/samples/MyEf.Hr/MyEf.Hr.Test/MyEf.Hr.Test.csproj +++ b/samples/MyEf.Hr/MyEf.Hr.Test/MyEf.Hr.Test.csproj @@ -32,17 +32,17 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/samples/MyEf.Hr/docs/10-Service-Bus-Subscribe.md b/samples/MyEf.Hr/docs/10-Service-Bus-Subscribe.md index 28a2ca981..9724b293c 100644 --- a/samples/MyEf.Hr/docs/10-Service-Bus-Subscribe.md +++ b/samples/MyEf.Hr/docs/10-Service-Bus-Subscribe.md @@ -147,7 +147,7 @@ The aforementioned services registration code of interest is as follows. }) .AddAzureServiceBusOrchestratedSubscriber((_, o) => { - o.EventDataDeserializationErrorHandling = ErrorHandling.Handle; + o.EventDataDeserializationErrorHandling = ErrorHandling.HandleBySubscriber; }) .AddTypedHttpClient("OktaApi"); ``` @@ -321,22 +321,15 @@ To implement, create a new `Subscribers` folder witin the `MyEf.Hr.Security.Subs namespace MyEf.Hr.Security.Subscriptions.Subscribers; [EventSubscriber("MyEf.Hr.Employee", "Terminated")] -public class EmployeeTerminatedSubcriber : SubscriberBase +public class EmployeeTerminatedSubcriber(OktaHttpClient okta, ILogger logger) : SubscriberBase(_employeeValidator) { private static readonly Validator _employeeValidator = Validator.Create() .HasProperty(x => x.Id, p => p.Mandatory()) .HasProperty(x => x.Email, p => p.Mandatory().Email()) .HasProperty(x => x.Termination, p => p.Mandatory()); - private readonly OktaHttpClient _okta; - private readonly ILogger _logger; - - public EmployeeTerminatedSubcriber(OktaHttpClient okta, ILogger logger) - { - _okta = okta ?? throw new ArgumentNullException(nameof(okta)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - ValueValidator = _employeeValidator; - } + private readonly OktaHttpClient _okta = okta.ThrowIfNull(); + private readonly ILogger _logger = logger.ThrowIfNull(); public override ErrorHandling SecurityHandling => ErrorHandling.Retry; @@ -344,8 +337,8 @@ public class EmployeeTerminatedSubcriber : SubscriberBase public override Task ReceiveAsync(EventData @event, EventSubscriberArgs args, CancellationToken cancellationToken) => Result.GoAsync(_okta.GetUserAsync(@event.Value.Id, @event.Value.Email!)) - .When(user => !user.IsDeactivatable, user => _logger.LogWarning("Employee {EmployeeId} with email {Email} has User status of {UserStatus} and is therefore unable to be deactivated.", @event.Value.Id, @event.Value.Email, user.Status)) - .WhenAsAsync(user => user.IsDeactivatable, user => _okta.DeactivateUserAsync(user.Id!)); + .When(user => !user.IsDeactivatable, user => _logger.LogWarning("Employee {EmployeeId} with email {Email} has User status of {UserStatus} and is therefore unable to be deactivated.", @event.Value.Id, @event.Value.Email, user.Status)) + .WhenAsAsync(user => user.IsDeactivatable, user => _okta.DeactivateUserAsync(user.Id!)); } ``` diff --git a/samples/MyEf.Hr/docs/5-Employee-Search.md b/samples/MyEf.Hr/docs/5-Employee-Search.md index 668273646..a7490f26e 100644 --- a/samples/MyEf.Hr/docs/5-Employee-Search.md +++ b/samples/MyEf.Hr/docs/5-Employee-Search.md @@ -92,7 +92,7 @@ For query operations generally we do not implement using the custom `*OnImplemen Extensions within _Beef_ are leveraged by implementing the partial constructor method (`EmployeeDataCtor`) and providing an implementation for the requisite extension delegate (`_getByArgsOnQuery`). The `With` methods are enabled by _CoreEx_ to simplify the code logic to apply the filter only where the value is not `null`, plus specifically handle the likes of wildcards. Also note usage of [`IgnoreAutoIncludes`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.entityframeworkqueryableextensions.ignoreautoincludes) (a standard Entity Framework capability) to avoid the cost of loading related data that is not needed for this query. -Within the `MyEf.Hr.Business/Data` folder, create `EmployeeData.cs`and implement as follows: +Within the `MyEf.Hr.Business/Data` folder, create `EmployeeData.cs` and implement as follows: ``` csharp partial void EmployeeDataCtor() diff --git a/samples/MyEf.Hr/docs/7-Performance-Review.md b/samples/MyEf.Hr/docs/7-Performance-Review.md index 5dee5b081..22a14cbca 100644 --- a/samples/MyEf.Hr/docs/7-Performance-Review.md +++ b/samples/MyEf.Hr/docs/7-Performance-Review.md @@ -285,7 +285,10 @@ public class PerformanceReviewValidator : Validator ## End-to-End testing -For the purposes of this sample, copy the file and contents of [`PerformanceReviewTest.cs`](../MyEf.Hr.Test/Apis/PerformanceReviewTest.cs) (`MyEf.Hr.Test/Apis` folder) and [`PerformanceReviewValidatorTest.cs`](../MyEf.Hr.Test/Validators/PerformanceReviewValidatorTest.cs) (`MyEf.Hr.Test/Validators` folder) into local project. +For the purposes of this sample peform the following: + +- Copy the file and contents of [`PerformanceReviewTest.cs`](../MyEf.Hr.Test/Apis/PerformanceReviewTest.cs) into your test project `MyEf.Hr.Test/Apis` folder; +- Copy the file and contents of [`PerformanceReviewValidatorTest.cs`](../MyEf.Hr.Test/Validators/PerformanceReviewValidatorTest.cs) into your test project `MyEf.Hr.Test/Validators` folder. For the end-to-end testing to function the performance review related data must first be populated into the database; append the following into the existing `Data.yaml` (`MyEf.Hr.Test/Data`). @@ -306,7 +309,7 @@ At this stage we now have added and tested the performance review capabilities. To verify, build the solution and ensure no compilation errors. -Within test explorer, run the PerformanceReviewTest and PerformanceReviewValidatorTest set of tests and confirm they all pass. +Within test explorer, run the `PerformanceReviewTest` and `PerformanceReviewValidatorTest` set of tests and confirm they all pass. The following tests were newly added and should pass: diff --git a/templates/Beef.Template.Solution/README.md b/templates/Beef.Template.Solution/README.md index 862257dad..ade335ce3 100644 --- a/templates/Beef.Template.Solution/README.md +++ b/templates/Beef.Template.Solution/README.md @@ -50,6 +50,7 @@ Parameter | Description `SqlServer` | Microsoft SQL Server with Entity Framework (default). `SqlServerProcs` | Microsoft SQL Server with Stored Procedures. `MySQL` | Oracle MySQL with Entity Framework. +`Postgres` | PostgreSQL with Entity Framework. `Cosmos` | Azure Cosmos DB. `HttpAgent` | Backend HTTP. `None` | Empty solution/projects skeleton. @@ -58,10 +59,10 @@ The `dotnet new` command is used to create, e.g.: ``` dotnet new beef --company My.Company --appname Sales -dotnet new beef --company My.Company --appname Sales --datasource Cosmos +dotnet new beef --company My.Company --appname Sales --datasource SqlServer ``` - The following will be created: +The following will be created: ``` diff --git a/templates/Beef.Template.Solution/content/.template.config/template.json b/templates/Beef.Template.Solution/content/.template.config/template.json index 5b7ef91a2..7f5bbfdb7 100644 --- a/templates/Beef.Template.Solution/content/.template.config/template.json +++ b/templates/Beef.Template.Solution/content/.template.config/template.json @@ -35,15 +35,19 @@ "choices": [ { "choice": "SqlServer", - "description": "Indicates that the data source is a Microsoft SQL Server Database accessed via Entity Framework Core (default)." + "description": "Indicates that the data source is a Microsoft SQL Server database accessed via Entity Framework Core (default)." }, { "choice": "SqlServerProcs", - "description": "Indicates that the data source is a Microsoft SQL Server Database accessed via Stored Procedures." + "description": "Indicates that the data source is a Microsoft SQL Server database accessed via Stored Procedures." }, { "choice": "MySQL", - "description": "Indicates that the data source is a MySQL Database accessed via Entity Framework Core." + "description": "Indicates that the data source is a MySQL database accessed via Entity Framework Core." + }, + { + "choice": "Postgres", + "description": "Indicates that the data source is a PostgreSQL database accessed via Entity Framework Core." }, { "choice": "Cosmos", @@ -65,7 +69,7 @@ "type": "generated", "generator": "constant", "parameters": { - "value": "3.9.0" + "value": "3.12.0" }, "replaces": "CoreExVersion" }, @@ -73,7 +77,7 @@ "type": "generated", "generator": "constant", "parameters": { - "value": "5.9.0" + "value": "5.10.0" }, "replaces": "BeefVersion" }, @@ -93,9 +97,13 @@ "type": "computed", "value": "(datasource == \"MySQL\")" }, + "implement_postgres": { + "type": "computed", + "value": "(datasource == \"Postgres\")" + }, "implement_entityframework": { "type": "computed", - "value": "(datasource == \"SqlServer\" || datasource == \"MySQL\")" + "value": "(datasource == \"SqlServer\" || datasource == \"MySQL\" || datasource == \"Postgres\")" }, "implement_httpagent": { "type": "computed", @@ -111,7 +119,28 @@ "parameters": { "format": "yyyyMMdd" }, + "fileRename": "20190101", "replaces": "20190101" + }, + "appNameLower": { + "type": "generated", + "generator": "casing", + "parameters": { + "source": "appname", + "toLower": true + }, + "fileRename": "lowerapp", + "replaces": "lowerapp" + }, + "companyLower": { + "type": "generated", + "generator": "casing", + "parameters": { + "source": "company", + "toLower": true + }, + "fileRename": "lowercom", + "replaces": "lowercom" } }, "sources": [ @@ -126,12 +155,12 @@ "exclude": [ "Company.AppName.Database/**/*" ] }, { - "condition": "(implement_database || implement_sqlserver)", + "condition": "(implement_database || implement_sqlserver || implement_postgres)", "exclude": [ "Company.AppName.Database/Migrations/20190101-000001-create-gender.sql", "Company.AppName.Database/Migrations/20190101-000002-create-person.sql" ] }, { "condition": "(implement_mysql)", - "exclude": [ "Company.AppName.Database/Migrations/20190101-000001-create-AppName-schema.sql", "Company.AppName.Database/Migrations/20190101-000002-create-AppName-Gender.sql", "Company.AppName.Database/Migrations/20190101-000003-create-AppName-Person.sql" ] + "exclude": [ "Company.AppName.Database/Migrations/20190101-000001-create-lowerapp-schema.sql", "Company.AppName.Database/Migrations/20190101-000002-create-lowerapp-Gender.sql", "Company.AppName.Database/Migrations/20190101-000003-create-lowerapp-Person.sql" ] }, { "condition": "(!implement_entityframework)", 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 b9de53eb2..ae4d2b2b2 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 @@ -7,6 +7,8 @@ + + diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Api/GlobalUsings.cs b/templates/Beef.Template.Solution/content/Company.AppName.Api/GlobalUsings.cs index 891ef044d..26ef4fe20 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Api/GlobalUsings.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Api/GlobalUsings.cs @@ -1,4 +1,5 @@ -global using CoreEx; +global using Azure.Monitor.OpenTelemetry.AspNetCore; +global using CoreEx; global using CoreEx.AspNetCore.WebApis; global using CoreEx.Entities; global using CoreEx.Events; @@ -6,9 +7,6 @@ global using CoreEx.RefData; global using CoreEx.Validation; global using Microsoft.AspNetCore.Mvc; -#if (implement_cosmos) -global using AzCosmos = Microsoft.Azure.Cosmos; -#endif #if (implement_database || implement_sqlserver) global using Microsoft.Data.SqlClient; #endif @@ -17,10 +15,18 @@ #if (implement_mysql) global using MySql.Data.MySqlClient; #endif +#if (implement_postgres) +global using Npgsql; +#endif +global using OpenTelemetry.Trace; global using System.Net; global using System.Reflection; +global using Company.AppName.Api; global using Company.AppName.Business; global using Company.AppName.Business.Data; global using Company.AppName.Business.Entities; global using Company.AppName.Business.Validation; -global using RefDataNamespace = Company.AppName.Business.Entities; \ No newline at end of file +global using RefDataNamespace = Company.AppName.Business.Entities; +#if (implement_cosmos) +global using AzCosmos = Microsoft.Azure.Cosmos; +#endif \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Api/Program.cs b/templates/Beef.Template.Solution/content/Company.AppName.Api/Program.cs index 277848556..88e6ac237 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Api/Program.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Api/Program.cs @@ -1,16 +1,13 @@ -namespace Company.AppName.Api; - -/// -/// The Web API host/program. -/// -public static class Program -{ - /// - /// Main startup. - /// - /// The startup arguments. - public static void Main(string[] args) => Host.CreateDefaultBuilder() - .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup()) - .ConfigureAppConfiguration(c => c.AddEnvironmentVariables("AppName_").AddCommandLine(args)) - .Build().Run(); -} \ No newline at end of file +Host.CreateDefaultBuilder() + .ConfigureWebHostDefaults(b => b.UseStartup()) + .ConfigureAppConfiguration(c => c.AddEnvironmentVariables("AppName_").AddCommandLine(args)) + .ConfigureServices(s => + { +#if (implement_entityframework) + s.AddOpenTelemetry().UseAzureMonitor().WithTracing(b => b.AddEntityFrameworkCoreInstrumentation().AddSource("CoreEx.*", "Company.AppName.*")); +#else + s.AddOpenTelemetry().UseAzureMonitor().WithTracing(b => b.AddSource("CoreEx.*", "Company.AppName.*")); +#endif + }) + .Build() + .Run(); \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Api/Startup.cs b/templates/Beef.Template.Solution/content/Company.AppName.Api/Startup.cs index 4098bb863..880f5d040 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Api/Startup.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Api/Startup.cs @@ -22,6 +22,7 @@ public void ConfigureServices(IServiceCollection services) .AddRequestCache() .AddValidationTextProvider() .AddValidators() + .AddMappers() .AddSingleton(); #if (implement_database || implement_sqlserver) @@ -33,6 +34,11 @@ public void ConfigureServices(IServiceCollection services) // Add the database services (scoped per request/connection). services.AddDatabase(sp => new AppNameDb(() => new MySqlConnection(sp.GetRequiredService().DatabaseConnectionString), sp.GetRequiredService>())); +#endif +#if (implement_postgres) + // Add the database services (scoped per request/connection). + services.AddDatabase(sp => new AppNameDb(() => new NpgsqlConnection(sp.GetRequiredService().DatabaseConnectionString), sp.GetRequiredService>())); + #endif #if (implement_entityframework) // Add the entity framework services (scoped per request/connection). @@ -65,12 +71,7 @@ public void ConfigureServices(IServiceCollection services) .AddGeneratedDataSvcServices() .AddGeneratedDataServices(); -#if (!implement_database) - // Add type-to-type mapping services using reflection. - services.AddMappers(); - -#endif - // Add the event publishing; this will need to be updated from the logger publisher to the actual as appropriate. + // Add the event publishing; this will need to be updated from the null publisher to the actual as appropriate. services.AddEventDataFormatter() .AddNullEventPublisher(); diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Api/appsettings.json b/templates/Beef.Template.Solution/content/Company.AppName.Api/appsettings.json index 2f1c01e81..47380fedf 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Api/appsettings.json +++ b/templates/Beef.Template.Solution/content/Company.AppName.Api/appsettings.json @@ -17,6 +17,12 @@ "Database": "Server=localhost; Port=3306; Database=Company.AppName; Uid=dbuser; Pwd=dbpassword; AllowUserVariables=true; UseAffectedRows=false" }, //#endif + //#if (implement_postgres) + // Set using environment variable: 'AppName_ConnectionStrings__Database' + "ConnectionStrings": { + "Database": "Server=localhost; Database=Company.AppName; Username=postgres; Password=dbpassword;" + }, + //#endif //#if (implement_cosmos) // Set using environment variables: 'AppName_CosmosDb__ConnectionString' and 'AppName_CosmosDb__DatabaseId' (keeps values out of config file). "CosmosConnectionString": "AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==;AccountEndpoint=https://localhost:8081", 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 b0ec19790..ce7737da7 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 @@ -15,6 +15,9 @@ + + + @@ -25,5 +28,8 @@ + + + \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/AppNameDb.cs b/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/AppNameDb.cs index c5f4dbd51..38ac820d6 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/AppNameDb.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/AppNameDb.cs @@ -25,8 +25,19 @@ public class AppNameDb : MySqlDatabase /// /// Initializes a new instance of the class. /// - /// The factory to create the . + /// The factory to create the . /// The optional . public AppNameDb(Func create, ILogger? logger = null) : base(create, logger) { } } +#endif +#if (implement_postgres) +public class AppNameDb : PostgresDatabase +{ + /// + /// Initializes a new instance of the class. + /// + /// The factory to create the . + /// The optional . + public AppNameDb(Func create, ILogger? logger = null) : base(create, logger) { } +} #endif \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/AppNameEfDbContext.cs b/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/AppNameEfDbContext.cs index f9caba7b7..f6070df24 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/AppNameEfDbContext.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Business/Data/AppNameEfDbContext.cs @@ -34,6 +34,9 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) #endif #if (implement_mysql) optionsBuilder.UseMySql(BaseDatabase.GetConnection(), ServerVersion.Create(new Version(8, 0, 33), Pomelo.EntityFrameworkCore.MySql.Infrastructure.ServerType.MySql)); +#endif +#if (implement_postgres) + optionsBuilder.UseNpgsql(BaseDatabase.GetConnection()); #endif } 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 8f9a427fb..405eec1df 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 @@ -21,7 +21,7 @@ private void GetByArgsOnQuery(DatabaseParameterCollection p, PersonArgs? args) .TableValuedParamWith(args?.Genders, "GenderCodes", () => _db.CreateTableValuedParameter(args!.Genders!.ToCodeList())); } #endif -#if (implement_entityframework) +#if (implement_sqlserver || implement_mysql) private IQueryable GetByArgsOnQuery(IQueryable q, PersonArgs? args) { _ef.WithWildcard(args?.FirstName, (w) => q = q.Where(x => EF.Functions.Like(x.FirstName!, w))); @@ -30,6 +30,15 @@ private void GetByArgsOnQuery(DatabaseParameterCollection p, PersonArgs? args) return q.OrderBy(x => x.LastName).ThenBy(x => x.FirstName); } #endif +#if (implement_postgres) + private IQueryable GetByArgsOnQuery(IQueryable q, PersonArgs? args) + { + _ef.WithWildcard(args?.FirstName, (w) => q = q.Where(x => EF.Functions.ILike(x.FirstName!, w))); + _ef.WithWildcard(args?.LastName, (w) => q = q.Where(x => EF.Functions.ILike(x.LastName!, w))); + _ef.With(args?.Genders, () => q = q.Where(x => args!.Genders!.ToCodeList().Contains(x.GenderCode))); + return q.OrderBy(x => x.LastName).ThenBy(x => x.FirstName); + } +#endif #if (implement_cosmos) private IQueryable GetByArgsOnQuery(IQueryable q, PersonArgs? args) { diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Business/GlobalUsings.cs b/templates/Beef.Template.Solution/content/Company.AppName.Business/GlobalUsings.cs index db378dc99..b9cbcbe2d 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Business/GlobalUsings.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Business/GlobalUsings.cs @@ -18,6 +18,9 @@ #if (implement_mysql) global using CoreEx.Database.MySql; #endif +#if (implement_postgres) +global using CoreEx.Database.Postgres; +#endif global using CoreEx.Entities; global using CoreEx.Entities.Extended; #if (implement_entityframework) @@ -49,6 +52,9 @@ #if (implement_mysql) global using MySql.Data.MySqlClient; #endif +#if (implement_postgres) +global using Npgsql; +#endif global using System; global using System.Collections.Generic; #if (implement_database || implement_entityframework) diff --git a/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/entity.beef-5.yaml b/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/entity.beef-5.yaml index baea7feb9..0c1ec7eb7 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/entity.beef-5.yaml +++ b/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/entity.beef-5.yaml @@ -45,6 +45,9 @@ autoImplement: EntityFramework //#if (implement_mysql) etagDefaultMapperConverter: EncodedStringToDateTimeConverter //#endif +//#if (implement_postgres) +etagDefaultMapperConverter: EncodedStringToUInt32Converter +//#endif refDataText: true entities: # The following is an example Entity with CRUD operations defined accessing a database using EntityFramework. @@ -53,7 +56,7 @@ entities: //#if (implement_sqlserver) { name: Id, type: Guid, primaryKey: true, dataName: PersonId }, //#endif -//#if (implement_mysql) +//#if (implement_mysql || implement_postgres) { name: Id, type: int, primaryKey: true, dataName: PersonId }, //#endif { name: FirstName }, diff --git a/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/refdata.beef-5.yaml b/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/refdata.beef-5.yaml index 8faddfdce..138c746e8 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/refdata.beef-5.yaml +++ b/templates/Beef.Template.Solution/content/Company.AppName.CodeGen/refdata.beef-5.yaml @@ -22,6 +22,14 @@ entities: # The following is an example read-only reference data Entity accessing a SQL Database using EntityFramework. - { name: Gender, entityFrameworkModel: EfModel.Gender } //#endif +//#if (implement_postgres) +refDataType: int +autoImplement: EntityFramework +etagDefaultMapperConverter: EncodedStringToUInt32Converter +entities: + # The following is an example read-only reference data Entity accessing a SQL Database using EntityFramework. + - { name: Gender, entityFrameworkModel: EfModel.Gender } +//#endif //#if (implement_cosmos) cosmosName: ICosmos refDataType: Guid 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 a61327561..d78e02504 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 @@ -14,6 +14,9 @@ + + + diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Database/Data/RefData.yaml b/templates/Beef.Template.Solution/content/Company.AppName.Database/Data/RefData.yaml index cfc6bb068..d60db1934 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Database/Data/RefData.yaml +++ b/templates/Beef.Template.Solution/content/Company.AppName.Database/Data/RefData.yaml @@ -1,8 +1,9 @@ -AppName: -//#if (implement_database || implement_sqlserver) +//#if (implement_database || implement_sqlserver) +AppName: - $Gender: //#endif -//#if (implement_mysql) +//#if (implement_mysql || implement_postgres) +lowerapp: - $gender: //#endif - M: Male diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000001-create-AppName-schema.sql b/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000001-create-AppName-schema.sql deleted file mode 100644 index 4c44aac9f..000000000 --- a/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000001-create-AppName-schema.sql +++ /dev/null @@ -1,2 +0,0 @@ -CREATE SCHEMA [AppName] - AUTHORIZATION [dbo]; \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000001-create-gender.sql b/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000001-create-gender.sql index b090e7367..fa9a5765b 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000001-create-gender.sql +++ b/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000001-create-gender.sql @@ -1,6 +1,4 @@ -START TRANSACTION; - -CREATE TABLE `gender` ( +CREATE TABLE `gender` ( `gender_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `code` VARCHAR(50) NOT NULL UNIQUE, `text` VARCHAR(250) NULL, @@ -11,6 +9,4 @@ CREATE TABLE `gender` ( `created_date` DATETIME(6) NULL, `updated_by` VARCHAR(250) NULL, `updated_date` DATETIME(6) NULL -); - -COMMIT WORK; \ No newline at end of file +); \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000001-create-lowerapp-schema.sql b/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000001-create-lowerapp-schema.sql new file mode 100644 index 000000000..d6922cab9 --- /dev/null +++ b/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000001-create-lowerapp-schema.sql @@ -0,0 +1,7 @@ +//#if (implement_sqlserver || implement_database) +CREATE SCHEMA [AppName] + AUTHORIZATION [dbo]; +//#endif +//#if (implement_postgres) +CREATE SCHEMA "lowerapp"; +//#endif \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000002-create-AppName-Gender.sql b/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000002-create-AppName-Gender.sql deleted file mode 100644 index 98274d63d..000000000 --- a/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000002-create-AppName-Gender.sql +++ /dev/null @@ -1,16 +0,0 @@ -BEGIN TRANSACTION - -CREATE TABLE [AppName].[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 diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000002-create-lowerapp-gender.sql b/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000002-create-lowerapp-gender.sql new file mode 100644 index 000000000..a9e8c19a1 --- /dev/null +++ b/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000002-create-lowerapp-gender.sql @@ -0,0 +1,27 @@ +//#if (implement_sqlserver || implement_database) +CREATE TABLE [AppName].[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 +); +//#endif +//#if (implement_postgres) +CREATE TABLE "lowerapp"."gender" ( + "gender_id" SERIAL PRIMARY KEY, + "code" VARCHAR(50) NOT NULL UNIQUE, + "text" VARCHAR(250) NULL, + "is_active" BOOLEAN NULL, + "sort_order" INT NULL, + "created_by" VARCHAR(250) NULL, + "created_date" TIMESTAMPTZ NULL, + "updated_by" VARCHAR(250) NULL, + "updated_date" TIMESTAMPTZ NULL +); +//#endif \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000002-create-person.sql b/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000002-create-person.sql index 5f6537070..5e8a7a85b 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000002-create-person.sql +++ b/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000002-create-person.sql @@ -1,8 +1,4 @@ --- Migration Script - -START TRANSACTION; - -CREATE TABLE `person` ( +CREATE TABLE `person` ( `person_id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY, `first_name` VARCHAR(100) NULL, `last_name` VARCHAR(100) NULL, @@ -13,6 +9,4 @@ CREATE TABLE `person` ( `created_date` DATETIME(6) NULL, `updated_by` VARCHAR(250) NULL, `updated_date` DATETIME(6) NULL -); - -COMMIT WORK; \ No newline at end of file +); \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000003-create-AppName-Person.sql b/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000003-create-AppName-Person.sql deleted file mode 100644 index 1791dffbb..000000000 --- a/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000003-create-AppName-Person.sql +++ /dev/null @@ -1,18 +0,0 @@ --- Migration Script - -BEGIN TRANSACTION - -CREATE TABLE [AppName].[Person]( - [PersonId] UNIQUEIDENTIFIER NOT NULL PRIMARY KEY, - [FirstName] NVARCHAR(100) NULL, - [LastName] NVARCHAR(100) NULL, - [GenderCode] NVARCHAR(50) NULL, - [Birthday] DATE 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/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000003-create-lowerapp-person.sql b/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000003-create-lowerapp-person.sql new file mode 100644 index 000000000..ac709425e --- /dev/null +++ b/templates/Beef.Template.Solution/content/Company.AppName.Database/Migrations/20190101-000003-create-lowerapp-person.sql @@ -0,0 +1,27 @@ +//#if (implement_sqlserver || implement_database) +CREATE TABLE [AppName].[Person]( + [PersonId] UNIQUEIDENTIFIER NOT NULL PRIMARY KEY, + [FirstName] NVARCHAR(100) NULL, + [LastName] NVARCHAR(100) NULL, + [GenderCode] NVARCHAR(50) NULL, + [Birthday] DATE NULL, + [RowVersion] TIMESTAMP NOT NULL, + [CreatedBy] NVARCHAR(250) NULL, + [CreatedDate] DATETIME2 NULL, + [UpdatedBy] NVARCHAR(250) NULL, + [UpdatedDate] DATETIME2 NULL, +) +//#endif +//#if (implement_postgres) +CREATE TABLE "lowerapp"."person" ( + "person_id" SERIAL PRIMARY KEY, + "first_name" VARCHAR(100) NULL, + "last_name" VARCHAR(100) NULL, + "gender_code" VARCHAR(50) NULL, + "birthday" DATE NULL, + "created_by" VARCHAR(250) NULL, + "created_date" TIMESTAMPTZ NULL, + "updated_by" VARCHAR(250) NULL, + "updated_date" TIMESTAMPTZ NULL +); +//#endif \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Database/Program.cs b/templates/Beef.Template.Solution/content/Company.AppName.Database/Program.cs index 8a5767aeb..f2141ddc4 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Database/Program.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Database/Program.cs @@ -5,6 +5,9 @@ #if (implement_mysql) using Beef.Database.MySql; #endif +#if (implement_postgres) +using Beef.Database.Postgres; +#endif using System.Threading.Tasks; namespace Company.AppName.Database; @@ -26,6 +29,10 @@ public static Task Main(string[] args) => SqlServerMigrationConsole #if (implement_mysql) public static Task Main(string[] args) => MySqlMigrationConsole .Create("Server=localhost; Port=3306; Database=Company.AppName; Uid=dbuser; Pwd=dbpassword;", "Company", "AppName") +#endif +#if (implement_postgres) + public static Task Main(string[] args) => PostgresMigrationConsole + .Create("Server=localhost; Database=Company.AppName; Username=postgres; Password=dbpassword;", "Company", "AppName") #endif .Configure(c => ConfigureMigrationArgs(c.Args)) .RunAsync(args); @@ -39,7 +46,7 @@ public static Task Main(string[] args) => MySqlMigrationConsole #if (implement_database || implement_sqlserver) public static MigrationArgs ConfigureMigrationArgs(MigrationArgs args) => args.AddAssembly().UseBeefSchema(); #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) public static MigrationArgs ConfigureMigrationArgs(MigrationArgs args) => args.AddAssembly(); #endif } \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Database/database.beef-5.yaml b/templates/Beef.Template.Solution/content/Company.AppName.Database/database.beef-5.yaml index 38db9ab25..fe180f3db 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Database/database.beef-5.yaml +++ b/templates/Beef.Template.Solution/content/Company.AppName.Database/database.beef-5.yaml @@ -23,21 +23,36 @@ tables: //#endif //#if (implement_sqlserver) schema: AppName +efModel: true tables: # Reference data tables/models. -- { name: Gender, efModel: true } +- name: Gender # Master data tables/models. -- { name: Person, efModel: true } +- name: Person //#endif //#if (implement_mysql) schema: '' +efModel: true autoDotNetRename: SnakeKebabToPascalCase columnNameRowVersion: row_version tables: # Reference data tables/models. -- { name: gender, efModel: true } +- name: gender # Master data tables/models. -- { name: person, efModel: true } +- name: person +//#endif +//#if (implement_postgres) +schema: lowerapp +efModel: true +autoDotNetRename: SnakeKebabToPascalCase +columnNameRowVersion: xmin +aliasColumns: [ xmin^RowVersion ] +tables: + # Reference data tables/models. +- name: gender + + # Master data tables/models. +- name: person //#endif \ No newline at end of file diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Test/Apis/FixtureSetup.cs b/templates/Beef.Template.Solution/content/Company.AppName.Test/Apis/FixtureSetup.cs index 6d4309ea7..8f5deb0f1 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Test/Apis/FixtureSetup.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Test/Apis/FixtureSetup.cs @@ -20,6 +20,9 @@ public void OneTimeSetUp() #endif #if (implement_mysql) return await new MySqlMigration(args).MigrateAndLogAsync(ct).ConfigureAwait(false); +#endif +#if (implement_postgres) + return await new PostgresMigration(args).MigrateAndLogAsync(ct).ConfigureAwait(false); #endif }); } diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Test/Apis/PersonTest.cs b/templates/Beef.Template.Solution/content/Company.AppName.Test/Apis/PersonTest.cs index d92d59782..043c9da12 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Test/Apis/PersonTest.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Test/Apis/PersonTest.cs @@ -24,10 +24,10 @@ public void A110_Get_NotFound() #endif Agent() .ExpectStatusCode(HttpStatusCode.NotFound) -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) .Run(a => a.GetAsync(404.ToGuid())); #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) .Run(a => a.GetAsync(404)); #endif } @@ -50,10 +50,10 @@ public void A120_Get_Found() .IgnoreETag() .ExpectValue(_ => new Person { -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) Id = 1.ToGuid(), #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) Id = 1, #endif FirstName = "Wendy", @@ -61,10 +61,10 @@ public void A120_Get_Found() Gender = "F", Birthday = new DateTime(1985, 03, 18) }) -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) .Run(a => a.GetAsync(1.ToGuid())); #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) .Run(a => a.GetAsync(1)); #endif } @@ -78,10 +78,10 @@ public void A120_Get_Modified_NotModified() { var v = Agent() .ExpectStatusCode(HttpStatusCode.OK) -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) .Run(a => a.GetAsync(1.ToGuid(), new HttpRequestOptions { ETag = TestSetUp.Default.ConcurrencyErrorETag })).Value!; #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) .Run(a => a.GetAsync(1, new HttpRequestOptions { ETag = TestSetUp.Default.ConcurrencyErrorETag })).Value!; #endif @@ -89,10 +89,10 @@ public void A120_Get_Modified_NotModified() Agent() .ExpectStatusCode(HttpStatusCode.NotModified) -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) .Run(a => a.GetAsync(1.ToGuid(), new HttpRequestOptions { ETag = v.ETag })); #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) .Run(a => a.GetAsync(1, new HttpRequestOptions { ETag = v.ETag })); #endif } @@ -231,7 +231,7 @@ public void B110_Create() // Check the value was created properly. Agent() .ExpectStatusCode(HttpStatusCode.OK) -#if (implement_mysql) +#if (implement_mysql || implement_postgres) .ExpectValue(_ => v, "changeLog") #else .ExpectValue(_ => v) @@ -249,20 +249,20 @@ public void C110_Update_NotFound() // Get an existing value. var v = Agent() .ExpectStatusCode(HttpStatusCode.OK) -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) .Run(a => a.GetAsync(2.ToGuid())).Value!; #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) .Run(a => a.GetAsync(2)).Value!; #endif // Try updating with an invalid identifier. Agent() .ExpectStatusCode(HttpStatusCode.NotFound) -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) .Run(a => a.UpdateAsync(v, 404.ToGuid())); #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) .Run(a => a.UpdateAsync(v, 404)); #endif } @@ -271,10 +271,10 @@ public void C110_Update_NotFound() public void C120_Update_Concurrency() { // Get an existing value. -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) var id = 2.ToGuid(); #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) var id = 2; #endif var v = Agent() @@ -297,10 +297,10 @@ public void C120_Update_Concurrency() public void C130_Update() { // Get an existing value. -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) var id = 2.ToGuid(); #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) var id = 2; #endif var v = Agent() @@ -324,7 +324,7 @@ public void C130_Update() // Check the value was updated properly. Agent() .ExpectStatusCode(HttpStatusCode.OK) -#if (implement_mysql) +#if (implement_mysql || implement_postgres) .ExpectValue(_ => v, "changeLog") #else .ExpectValue(_ => v) @@ -342,20 +342,20 @@ public void D110_Patch_NotFound() // Get an existing value. var v = Agent() .ExpectStatusCode(HttpStatusCode.OK) -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) .Run(a => a.GetAsync(2.ToGuid())).Value!; #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) .Run(a => a.GetAsync(2)).Value!; #endif // Try patching with an invalid identifier. Agent() .ExpectStatusCode(HttpStatusCode.NotFound) -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) .Run(a => a.PatchAsync(HttpPatchOption.MergePatch, "{ \"lastName\": \"Smithers\" }", 404.ToGuid())); #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) .Run(a => a.PatchAsync(HttpPatchOption.MergePatch, "{ \"lastName\": \"Smithers\" }", 404)); #endif } @@ -364,10 +364,10 @@ public void D110_Patch_NotFound() public void D120_Patch_Concurrency() { // Get an existing value. -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) var id = 2.ToGuid(); #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) var id = 2; #endif var v = Agent() @@ -389,10 +389,10 @@ public void D120_Patch_Concurrency() public void D130_Patch() { // Get an existing value. -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) var id = 2.ToGuid(); #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) var id = 2; #endif var v = Agent() @@ -415,7 +415,7 @@ public void D130_Patch() // Check the value was updated properly. Agent() .ExpectStatusCode(HttpStatusCode.OK) -#if (implement_mysql) +#if (implement_mysql || implement_postgres) .ExpectValue(_ => v, "changeLog") #else .ExpectValue(_ => v) @@ -431,10 +431,10 @@ public void D130_Patch() public void E110_Delete() { // Check value exists. -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) var id = 4.ToGuid(); #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) var id = 4; #endif Agent() diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Test/Data/Data.yaml b/templates/Beef.Template.Solution/content/Company.AppName.Test/Data/Data.yaml index 9cccb8023..32db57d8a 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Test/Data/Data.yaml +++ b/templates/Beef.Template.Solution/content/Company.AppName.Test/Data/Data.yaml @@ -1,12 +1,13 @@ -AppName: //#if (implement_database || implement_sqlserver) +AppName: - Person: - { PersonId: 1, FirstName: Wendy, LastName: Jones, GenderCode: F, Birthday: 1985-03-18 } - { PersonId: 2, FirstName: Brian, LastName: Smith, GenderCode: M, Birthday: 1994-11-07 } - { PersonId: 3, FirstName: Rachael, LastName: Browne, GenderCode: F, Birthday: 1972-06-28 } - { PersonId: 4, FirstName: Waylon, LastName: Smithers, GenderCode: M, Birthday: 1952-02-21 } //#endif -//#if (implement_mysql) +//#if (implement_mysql || implement_postgres) +lowerapp: - person: - { first_name: Wendy, last_name: Jones, gender_code: F, birthday: 1985-03-18 } - { first_name: Brian, last_name: Smith, gender_code: M, birthday: 1994-11-07 } diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Test/GlobalUsings.cs b/templates/Beef.Template.Solution/content/Company.AppName.Test/GlobalUsings.cs index cda3d50e2..0b3642e1e 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Test/GlobalUsings.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Test/GlobalUsings.cs @@ -7,6 +7,9 @@ #if (implement_mysql) global using Beef.Database.MySql; #endif +#if (implement_postgres) +global using Beef.Database.Postgres; +#endif global using CoreEx; #if (implement_cosmos) global using CoreEx.Cosmos; diff --git a/templates/Beef.Template.Solution/content/Company.AppName.Test/Resources/A280_GetByArgs_RefDataText-Response.json b/templates/Beef.Template.Solution/content/Company.AppName.Test/Resources/A280_GetByArgs_RefDataText-Response.json index 2b668c700..5233d1374 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Test/Resources/A280_GetByArgs_RefDataText-Response.json +++ b/templates/Beef.Template.Solution/content/Company.AppName.Test/Resources/A280_GetByArgs_RefDataText-Response.json @@ -1,9 +1,9 @@ [ { - //#if (!implement_mysql) + //#if (!implement_mysql && !implement_postgres) "id": "00000003-0000-0000-0000-000000000000", //#endif - //#if (implement_mysql) + //#if (implement_mysql || implement_postgres) "id": 3, //#endif "firstName": "Rachael", @@ -13,10 +13,10 @@ "birthday": "1972-06-28T00:00:00" }, { - //#if (!implement_mysql) + //#if (!implement_mysql && !implement_postgres) "id": "00000001-0000-0000-0000-000000000000", //#endif - //#if (implement_mysql) + //#if (implement_mysql || implement_postgres) "id": 1, //#endif "firstName": "Wendy", 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 index fbf88f4f7..b97cb1c1c 100644 --- a/templates/Beef.Template.Solution/content/Company.AppName.Test/Validators/PersonValidatorTest.cs +++ b/templates/Beef.Template.Solution/content/Company.AppName.Test/Validators/PersonValidatorTest.cs @@ -11,10 +11,10 @@ public class PersonValidatorTest public void OneTimeSetUp() { var rd = new Mock(); -#if (!implement_mysql) +#if (!implement_mysql && !implement_postgres) rd.Setup(x => x.GenderGetAllAsync()).ReturnsAsync(new GenderCollection { new Gender { Id = Guid.NewGuid(), Code = "F" } }); #endif -#if (implement_mysql) +#if (implement_mysql || implement_postgres) rd.Setup(x => x.GenderGetAllAsync()).ReturnsAsync(new GenderCollection { new Gender { Id = 1, Code = "F" } }); #endif diff --git a/tests/Beef.Template.Solution.UnitTest/Beef.Template.Solution.UnitTest.csproj b/tests/Beef.Template.Solution.UnitTest/Beef.Template.Solution.UnitTest.csproj index cad66f6e5..4a2a6ca23 100644 --- a/tests/Beef.Template.Solution.UnitTest/Beef.Template.Solution.UnitTest.csproj +++ b/tests/Beef.Template.Solution.UnitTest/Beef.Template.Solution.UnitTest.csproj @@ -11,7 +11,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/tests/Beef.Template.Solution.UnitTest/TemplateTest.cs b/tests/Beef.Template.Solution.UnitTest/TemplateTest.cs index 1e4a8540f..bc314e3ac 100644 --- a/tests/Beef.Template.Solution.UnitTest/TemplateTest.cs +++ b/tests/Beef.Template.Solution.UnitTest/TemplateTest.cs @@ -126,6 +126,13 @@ public void MySQL() SolutionCreateGenerateTest("Foo.My", "Bar", "MySQL"); } + [Test] + public void Postgres() + { + OneTimeSetUp(); + SolutionCreateGenerateTest("Foo.Ps", "Bar", "Postgres"); + } + [Test] public void Cosmos() { @@ -155,7 +162,7 @@ private static void SolutionCreateGenerateTest(string company, string appName, s Assert.That(ExecuteCommand("dotnet", "run all", Path.Combine(dir, $"{company}.{appName}.CodeGen")).exitCode, Is.Zero, "dotnet run all [entity]"); // Database: Execute code-generation. - if (datasource == "SqlServerProcs" || datasource == "SqlServer" || datasource == "MySQL") + if (datasource == "SqlServerProcs" || datasource == "SqlServer" || datasource == "MySQL" || datasource == "Postgres") { Assert.That(ExecuteCommand("dotnet", "run drop --accept-prompts", Path.Combine(dir, $"{company}.{appName}.Database")).exitCode, Is.Zero, "dotnet run drop [database]"); Assert.That(ExecuteCommand("dotnet", "run all", Path.Combine(dir, $"{company}.{appName}.Database")).exitCode, Is.Zero, "dotnet run all [database]"); diff --git a/tools/Beef.CodeGen.Core/Beef.CodeGen.Core.csproj b/tools/Beef.CodeGen.Core/Beef.CodeGen.Core.csproj index 6e25c41d4..30b489310 100644 --- a/tools/Beef.CodeGen.Core/Beef.CodeGen.Core.csproj +++ b/tools/Beef.CodeGen.Core/Beef.CodeGen.Core.csproj @@ -32,7 +32,7 @@ - + diff --git a/tools/Beef.CodeGen.Core/Config/Database/CodeGenConfig.cs b/tools/Beef.CodeGen.Core/Config/Database/CodeGenConfig.cs index ff20ebf31..a89dd095a 100644 --- a/tools/Beef.CodeGen.Core/Config/Database/CodeGenConfig.cs +++ b/tools/Beef.CodeGen.Core/Config/Database/CodeGenConfig.cs @@ -24,6 +24,7 @@ namespace Beef.CodeGen.Config.Database Description = "The `CodeGeneration` object defines global properties that are used to drive the underlying database-driven code generation.", Markdown = "")] [CodeGenCategory("Infer", Title = "Provides the _special Column Name inference_ configuration.")] + [CodeGenCategory("Columns", Title = "Provides the _Columns_ configuration.")] [CodeGenCategory("Path", Title = "Provides the _Path (Directory)_ configuration for the generated artefacts.")] [CodeGenCategory("DotNet", Title = "Provides the _.NET_ configuration.")] [CodeGenCategory("EntityFramework", Title = "Provides the _Entity Framework (EF) model_ configuration.")] @@ -153,6 +154,18 @@ public class CodeGenConfig : ConfigRootBase, ISpecialColumnNames #endregion + #region Columns + + /// + /// Gets or sets the list of `Column` and `Alias` pairs to enable column renaming. + /// + [JsonPropertyName("aliasColumns")] + [CodeGenPropertyCollection("Columns", Title = "The list of `Column` and `Alias` pairs (split by a `^` lookup character) to enable column aliasing/renaming.", + Description = "Each alias value should be formatted as `Column` + `^` + `Alias`; e.g. `PCODE^ProductCode`.")] + public List? AliasColumns { get; set; } + + #endregion + #region DotNet /// @@ -361,6 +374,11 @@ public class CodeGenConfig : ConfigRootBase, ISpecialColumnNames /// public string? AppName => CodeGenArgs!.GetAppName(true); + /// + /// Gets the database provider name. + /// + public string? DatabaseProvider => Migrator?.Provider; + /// /// /// @@ -380,16 +398,16 @@ protected override async Task PrepareAsync() NamespaceBusiness = DefaultWhereNull(NamespaceBusiness, () => $"{NamespaceBase}.Business"); NamespaceOutbox = DefaultWhereNull(NamespaceOutbox, () => NamespaceBusiness); - ColumnNameIsDeleted = DefaultWhereNull(ColumnNameIsDeleted, () => "IsDeleted"); - ColumnNameTenantId = DefaultWhereNull(ColumnNameTenantId, () => "TenantId"); + ColumnNameIsDeleted = DefaultWhereNull(ColumnNameIsDeleted, () => Migrator!.DatabaseSchemaConfig.IsDeletedColumnName); + ColumnNameTenantId = DefaultWhereNull(ColumnNameTenantId, () => Migrator!.DatabaseSchemaConfig.TenantIdColumnName); ColumnNameOrgUnitId = DefaultWhereNull(ColumnNameOrgUnitId, () => "OrgUnitId"); - ColumnNameRowVersion = DefaultWhereNull(ColumnNameRowVersion, () => "RowVersion"); - ColumnNameCreatedBy = DefaultWhereNull(ColumnNameCreatedBy, () => "CreatedBy"); - ColumnNameCreatedDate = DefaultWhereNull(ColumnNameCreatedDate, () => "CreatedDate"); - ColumnNameUpdatedBy = DefaultWhereNull(ColumnNameUpdatedBy, () => "UpdatedBy"); - ColumnNameUpdatedDate = DefaultWhereNull(ColumnNameUpdatedDate, () => "UpdatedDate"); - ColumnNameDeletedBy = DefaultWhereNull(ColumnNameDeletedBy, () => "UpdatedBy"); - ColumnNameDeletedDate = DefaultWhereNull(ColumnNameDeletedDate, () => "UpdatedDate"); + ColumnNameRowVersion = DefaultWhereNull(ColumnNameRowVersion, () => Migrator!.DatabaseSchemaConfig.RowVersionColumnName); + ColumnNameCreatedBy = DefaultWhereNull(ColumnNameCreatedBy, () => Migrator!.DatabaseSchemaConfig.CreatedByColumnName); + ColumnNameCreatedDate = DefaultWhereNull(ColumnNameCreatedDate, () => Migrator!.DatabaseSchemaConfig.CreatedDateColumnName); + ColumnNameUpdatedBy = DefaultWhereNull(ColumnNameUpdatedBy, () => Migrator!.DatabaseSchemaConfig.UpdatedByColumnName); + ColumnNameUpdatedDate = DefaultWhereNull(ColumnNameUpdatedDate, () => Migrator!.DatabaseSchemaConfig.UpdatedDateColumnName); + ColumnNameDeletedBy = DefaultWhereNull(ColumnNameDeletedBy, () => Migrator!.DatabaseSchemaConfig.UpdatedByColumnName); + ColumnNameDeletedDate = DefaultWhereNull(ColumnNameDeletedDate, () => Migrator!.DatabaseSchemaConfig.UpdatedDateColumnName); OrgUnitJoinSql = DefaultWhereNull(OrgUnitJoinSql, () => "[Sec].[fnGetUserOrgUnits]()"); CheckUserPermissionSql = DefaultWhereNull(CheckUserPermissionSql, () => "[Sec].[spCheckUserHasPermission]"); @@ -460,6 +478,16 @@ private async Task LoadDbTablesConfigAsync() if (string.IsNullOrEmpty(name) || AutoDotNetRename == "None") return name; + // Try to find the global alias and use. + var ca = AliasColumns?.Where(x => x.StartsWith(name + "^", StringComparison.Ordinal)).FirstOrDefault(); + if (ca != null) + { + var parts = ca.Split("^", StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 2 && parts[1].Length > 0) + return parts[1]; + } + + // Now apply the rename according to specified convention. if (AutoDotNetRename == "PascalCase") return StringConverter.ToPascalCase(name); diff --git a/tools/Beef.CodeGen.Core/Schema/database.beef-5.json b/tools/Beef.CodeGen.Core/Schema/database.beef-5.json index 44d609428..7e5db21cf 100644 --- a/tools/Beef.CodeGen.Core/Schema/database.beef-5.json +++ b/tools/Beef.CodeGen.Core/Schema/database.beef-5.json @@ -77,6 +77,14 @@ "title": "The SQL function that is to be used for \u0060Permission\u0060 verification.", "description": "Defaults to \u0060[Sec].[fnGetUserHasPermission]\u0060." }, + "aliasColumns": { + "type": "array", + "title": "The list of \u0060Column\u0060 and \u0060Alias\u0060 pairs (split by a \u0060^\u0060 lookup character) to enable column aliasing/renaming.", + "description": "Each alias value should be formatted as \u0060Column\u0060 \u002B \u0060^\u0060 \u002B \u0060Alias\u0060; e.g. \u0060PCODE^ProductCode\u0060.", + "items": { + "type": "string" + } + }, "autoDotNetRename": { "type": "string", "title": "The option to automatically rename the SQL Tables and Columns for use in .NET.", diff --git a/tools/Beef.Database.Core/Beef.Database.Core.csproj b/tools/Beef.Database.Core/Beef.Database.Core.csproj index 07a8b6605..517f43e36 100644 --- a/tools/Beef.Database.Core/Beef.Database.Core.csproj +++ b/tools/Beef.Database.Core/Beef.Database.Core.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/tools/Beef.Database.Core/Templates/DbEfModel_cs.hbs b/tools/Beef.Database.Core/Templates/DbEfModel_cs.hbs index 1c8616353..f80f84a23 100644 --- a/tools/Beef.Database.Core/Templates/DbEfModel_cs.hbs +++ b/tools/Beef.Database.Core/Templates/DbEfModel_cs.hbs @@ -13,7 +13,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace {{Root.NamespaceBusiness}}.Data.EfModel; /// -/// Represents the Entity Framework (EF) model for database object '{{DbTable.QualifiedName}}'. +/// Represents the Entity Framework (EF) model for {{Root.DatabaseProvider}} database object {{DbTable.QualifiedName}}. /// public partial class {{EfModelName}}{{#ifval ColumnIsDeleted}} : ILogicallyDeleted{{#ifval ColumnTenantId}}, ITenantId{{/ifval}}{{else}}{{#ifval ColumnTenantId}} : ITenantId{{/ifval}}{{/ifval}} { diff --git a/tools/Beef.Database.MySql/Beef.Database.MySql.csproj b/tools/Beef.Database.MySql/Beef.Database.MySql.csproj index 52b077dfe..5db78d329 100644 --- a/tools/Beef.Database.MySql/Beef.Database.MySql.csproj +++ b/tools/Beef.Database.MySql/Beef.Database.MySql.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/tools/Beef.Database.Postgres/Beef.Database.Postgres.csproj b/tools/Beef.Database.Postgres/Beef.Database.Postgres.csproj new file mode 100644 index 000000000..1668a84e0 --- /dev/null +++ b/tools/Beef.Database.Postgres/Beef.Database.Postgres.csproj @@ -0,0 +1,37 @@ + + + + net6.0;net7.0;net8.0 + Beef.Database.Postgres + Business Entity Execution Framework (Beef) PostgreSQL Database tool. + beef database sql postgres postgresql dbup migration schema dbex + + + + true + + 1701;1702;CA1303 + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tools/Beef.Database.Postgres/PostgresMigration.cs b/tools/Beef.Database.Postgres/PostgresMigration.cs new file mode 100644 index 000000000..e14234c3b --- /dev/null +++ b/tools/Beef.Database.Postgres/PostgresMigration.cs @@ -0,0 +1,59 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using DbEx; +using OnRamp; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Beef.Database.Postgres +{ + /// + /// Provides the PostgreSQL migration orchestration extending + /// to further enable . + /// + public class PostgresMigration : DbEx.Postgres.Migration.PostgresMigration + { + /// + /// Initializes an instance of the class. + /// + /// The . + public PostgresMigration(MigrationArgs args) : base(args) + { + IsCodeGenEnabled = true; + + // Add in the beef schema stuff where requested. + if (args.BeefSchema) + args.AddAssemblyAfter(typeof(DbEx.Postgres.Migration.PostgresMigration).Assembly, typeof(PostgresMigration).Assembly); + + } + + /// + /// Gets the . + /// + public new MigrationArgs Args => (MigrationArgs)base.Args; + + /// + protected override Task<(bool Success, string? Statistics)> DatabaseCodeGenAsync(CancellationToken cancellationToken = default) + { + var yaml = Args.GetParameter("Param0"); + if (yaml is null) + return this.ExecuteCodeGenAsync(cancellationToken); + + var tables = new List(); + for (int i = 1; true; i++) + { + var table = Args.GetParameter($"Param{i}"); + if (table is null) + break; + + tables.Add(table); + } + + if (tables.Count == 0) + throw new CodeGenException($"A '{nameof(MigrationCommand.CodeGen)}' command for 'YAML' also requires at least one table argument to be specified."); + + return this.ExecuteYamlCodeGenAsync(null, [.. tables], cancellationToken); + } + } +} \ No newline at end of file diff --git a/tools/Beef.Database.Postgres/PostgresMigrationConsole.cs b/tools/Beef.Database.Postgres/PostgresMigrationConsole.cs new file mode 100644 index 000000000..1bd239066 --- /dev/null +++ b/tools/Beef.Database.Postgres/PostgresMigrationConsole.cs @@ -0,0 +1,60 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/Beef + +using Microsoft.Extensions.Logging; +using System; +using System.Reflection; + +namespace Beef.Database.Postgres +{ + /// + /// Provides the SQL Server migration console capability. + /// + /// The default that will be overridden/updated by the command-line argument values. + public class PostgresMigrationConsole(MigrationArgs? args = null) : MigrationConsoleBase(args) + { + /// + /// Creates a new using to default the probing . + /// + /// The . + /// The database connection string. + /// A new . + public static PostgresMigrationConsole Create(string connectionString) => new(new MigrationArgs { ConnectionString = connectionString }.AddAssembly(typeof(T).Assembly)); + + /// + /// Creates a new instance of the class using the specified parameters. + /// + /// The default connection string. + /// The company name. + /// The application/domain name. + /// The instance. + public static PostgresMigrationConsole Create(string connectionString, string company, string appName) + => new(new MigrationArgs { ConnectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString)) } + .AddParameter(CodeGen.CodeGenConsole.CompanyParamName, company ?? throw new ArgumentNullException(nameof(company))) + .AddParameter(CodeGen.CodeGenConsole.AppNameParamName, appName ?? throw new ArgumentNullException(nameof(appName)))); + + /// + /// Initializes a new instance of the class that provides a default for the . + /// + /// The database connection string. + public PostgresMigrationConsole(string connectionString) : this(new MigrationArgs { ConnectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString)) }) { } + + /// + protected override DbEx.Migration.DatabaseMigrationBase CreateMigrator() => new PostgresMigration(Args); + + /// + public override string AppTitle => base.AppTitle + " [PostgreSQL]"; + + /// + protected override void OnWriteHelp() + { + base.OnWriteHelp(); + new DbEx.Postgres.Console.PostgresMigrationConsole(new DbEx.Migration.MigrationArgs { Logger = Logger }).WriteScriptHelp(); + Logger?.LogInformation("{help}", string.Empty); + Logger?.LogInformation("{help}", "Extended CodeGen command and argument(s):"); + Logger?.LogInformation("{help}", " codegen yaml [
...] Creates a temporary Beef entity code-gen YAML file for the specified table(s)."); + Logger?.LogInformation("{help}", " - A table name with a prefix ! denotes that no CRUD operations are required."); + Logger?.LogInformation("{help}", " - A table name with a prefix * denotes that a 'GetByArgs' operation is required."); + Logger?.LogInformation("{help}", string.Empty); + } + } +} \ No newline at end of file diff --git a/tools/Beef.Database.Postgres/Scripts/Database.yaml b/tools/Beef.Database.Postgres/Scripts/Database.yaml new file mode 100644 index 000000000..514e09b82 --- /dev/null +++ b/tools/Beef.Database.Postgres/Scripts/Database.yaml @@ -0,0 +1,4 @@ +configType: Beef.CodeGen.Config.Database.CodeGenConfig, Beef.CodeGen.Core +generators: +- { type: 'Beef.CodeGen.Generators.DatabaseEfModelGenerator, Beef.CodeGen.Core', template: 'DbEfModel_cs', file: '{{EFModelName}}.cs', directory: '{{Root.PathBusiness}}/Data/EfModel/Generated', text: 'DatabaseEfModelGenerator: Business/Data/EfModel' } +- { type: 'Beef.CodeGen.Generators.DatabaseEfModelBuilderCodeGenerator, Beef.CodeGen.Core', template: 'DbEfModelBuilder_cs', file: 'ModelBuilderExtensions.cs', directory: '{{Root.PathBusiness}}/Data/EfModel/Generated', text: 'DatabaseEfModelBuilderCodeGenerator: Business/Data/EfModel' } \ No newline at end of file diff --git a/tools/Beef.Database.Postgres/strong-name-key.snk b/tools/Beef.Database.Postgres/strong-name-key.snk new file mode 100644 index 0000000000000000000000000000000000000000..5bced3906b9de4a27f90758305b30ed16b1c5f35 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50097f?o$f(*a46&Kxkf^(4*Ly9ozLqzCQL8 ze4lx`&2(hhFTIrh<4%ZNH{;**>J_B`(L zg2w#VAib4t`Xlh$Ungys?-x~y+Wvh;xRN67N}>kKsDKiPB*9Gb7j$PhcmQ=c&vKYs zYL8fK??~H;uqL2sxUhx*XOZqD()r`xgYyBHFhtcmx zuFz5&;wc29C`~$WpcEFk`IDWAVMFO_rZ|ClrE@jhq)yI?FC^;2`{-+TQ=dqDZn<_f zpR07zgt%W`u586(DhW&RiBZp%9klaKr>nIxgi$GNxEzgifyIE5#vcv12~t4f%y?2C zyPgfZL#XP7%!Awfy`;)A6cHg>7KRRs>#5 z2I-~)zZO-3_H1R-IV4e+D>_DCrav32(SMsi(9l&PtnFm8Hf7X>cyo?*41FrD5lbJf ipiUG-DI^tR9Mcea^C%Jh+%U*&_-v6R3hAfG$wP6upd;x3 literal 0 HcmV?d00001 diff --git a/tools/Beef.Database.SqlServer/Beef.Database.SqlServer.csproj b/tools/Beef.Database.SqlServer/Beef.Database.SqlServer.csproj index 26d37a636..59a3d43f0 100644 --- a/tools/Beef.Database.SqlServer/Beef.Database.SqlServer.csproj +++ b/tools/Beef.Database.SqlServer/Beef.Database.SqlServer.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/tools/Beef.Test.NUnit/Beef.Test.NUnit.csproj b/tools/Beef.Test.NUnit/Beef.Test.NUnit.csproj index 8a718dac2..1095567ce 100644 --- a/tools/Beef.Test.NUnit/Beef.Test.NUnit.csproj +++ b/tools/Beef.Test.NUnit/Beef.Test.NUnit.csproj @@ -8,7 +8,7 @@ - +