Skip to content

Commit 7b7cb49

Browse files
authored
feat: enhance configurability of the library through MSBuild and additional database providers (#17)
* refactor: improve task execution and logging structure - Refactored task execution to utilize a decorator pattern for better exception handling and logging. - Simplified process execution with a new `ProcessRunner` class for consistent logging and error handling. - Enhanced resource resolution chains for files and directories, consolidating logic and improving maintainability. - Updated various tasks to leverage the new logging and execution patterns. * feat(config): add support for MSBuild property overrides in efcpt-config.json * feat(providers): add multi-database provider support for connection string mode Add support for all efcpt-supported database providers in connection string mode: - PostgreSQL (Npgsql) - MySQL/MariaDB (MySqlConnector) - SQLite (Microsoft.Data.Sqlite) - Oracle (Oracle.ManagedDataAccess.Core) - Firebird (FirebirdSql.Data.FirebirdClient) - Snowflake (Snowflake.Data) Key changes: - Create DatabaseProviderFactory for connection/reader creation - Implement provider-specific schema readers using GetSchema() API - Add SQLite sample demonstrating connection string mode - Add comprehensive samples README documenting all usage patterns - Fix MSBuild target condition timing for connection string mode - Add 77 new unit tests for schema reader parsing logic - Update documentation with provider configuration examples * refactor: address PR review comments - Use explicit LINQ filtering instead of foreach with continue - Simplify GetExistingColumn methods using FirstOrDefault - Use pattern matching switch for version parsing logic - Remove unused isPrimaryKey variable in SqliteSchemaReader - Simplify nullable boolean expressions in tests * fix: use string.Equals for fingerprint comparisons in integration tests Replace == and != operators with string.Equals() using StringComparison.Ordinal to address "comparison of identical values" code analysis warnings. * test: add coverage for NullBuildLog, Firebird, and Oracle schema readers - Add NullBuildLog unit tests to cover all IBuildLog methods - Add Testcontainers-based integration tests for FirebirdSchemaReader - Add Testcontainers-based integration tests for OracleSchemaReader - Add Testcontainers.FirebirdSql and Testcontainers.Oracle packages Note: Snowflake integration tests cannot be added as it is a cloud-only service requiring a real account. The existing unit tests cover parsing logic. * docs: add security documentation for SQLite EscapeIdentifier method Address PR review comment by documenting: - Why PRAGMA commands require embedded identifiers (no parameterized query support) - Security context: identifier values come from SQLite's internal metadata - The escaping mechanism protects against special characters in names * fix: pin Testcontainers to 4.4.0 and improve integration test assertions - Downgrade all Testcontainers packages to 4.4.0 for cross-package compatibility (Testcontainers.FirebirdSql 4.4.0 requires matching versions for core library) - Update Firebird and Oracle integration test assertions to use >= 3 instead of == 3 (some database containers may include additional tables beyond the test schema) - Add explicit checks for test tables to ensure schema reader works correctly * feat: add Snowflake integration tests with LocalStack emulator - Add SnowflakeSchemaIntegrationTests using LocalStack Snowflake emulator - Tests skip automatically when LOCALSTACK_AUTH_TOKEN is not set - Add Xunit.SkippableFact package for runtime test skipping - Tests cover schema reading, fingerprinting, and factory patterns Note: LocalStack Snowflake requires a paid token. Tests will run when LOCALSTACK_AUTH_TOKEN environment variable is set, otherwise skip gracefully. * docs: update documentation for multi-database and multi-SDK support - Update samples/README.md to clarify both Microsoft.Build.Sql and MSBuild.Sdk.SqlProj SDKs are supported for DACPAC mode - Fix main README.md: remove outdated "Phase 1 supports SQL Server only" references and update provider support table to show all 7 supported databases (SQL Server, PostgreSQL, MySQL, SQLite, Oracle, Firebird, Snowflake) - Update getting-started.md with multi-database provider examples - Update core-concepts.md with SQL SDK comparison table * refactor: use StringExtensions consistently across schema readers Replace verbose string comparison patterns with extension methods: - `string.Equals(a, b, StringComparison.OrdinalIgnoreCase)` → `a.EqualsIgnoreCase(b)` - `row["col"].ToString() == "YES"` → `row.GetString("col").EqualsIgnoreCase("YES")` Updated files: - SqlServerSchemaReader.cs - PostgreSqlSchemaReader.cs - MySqlSchemaReader.cs - OracleSchemaReader.cs - FirebirdSchemaReader.cs - SnowflakeSchemaReader.cs
1 parent 518a7b2 commit 7b7cb49

65 files changed

Lines changed: 10411 additions & 753 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -596,15 +596,25 @@ When multiple connection string sources are present, this priority order is used
596596

597597
### Database Provider Support
598598

599-
**Currently Supported:**
600-
- **SQL Server** (`mssql`) - Fully supported
601-
602-
**Planned for Future Versions:**
603-
- ⏳ PostgreSQL (`postgresql`)
604-
- ⏳ MySQL (`mysql`)
605-
- ⏳ MariaDB (`mariadb`)
606-
- ⏳ Oracle (`oracle`)
607-
- ⏳ SQLite (`sqlite`)
599+
JD.Efcpt.Build supports all database providers that EF Core Power Tools supports:
600+
601+
| Provider | Value | Aliases | Notes |
602+
|----------|-------|---------|-------|
603+
| SQL Server | `mssql` | `sqlserver`, `sql-server` | Default provider |
604+
| PostgreSQL | `postgres` | `postgresql`, `pgsql` | Uses Npgsql |
605+
| MySQL/MariaDB | `mysql` | `mariadb` | Uses MySqlConnector |
606+
| SQLite | `sqlite` | `sqlite3` | Single-file databases |
607+
| Oracle | `oracle` | `oracledb` | Uses Oracle.ManagedDataAccess.Core |
608+
| Firebird | `firebird` | `fb` | Uses FirebirdSql.Data.FirebirdClient |
609+
| Snowflake | `snowflake` | `sf` | Uses Snowflake.Data |
610+
611+
**Example:**
612+
```xml
613+
<PropertyGroup>
614+
<EfcptProvider>postgres</EfcptProvider>
615+
<EfcptConnectionString>Host=localhost;Database=mydb;Username=user;Password=pass</EfcptConnectionString>
616+
</PropertyGroup>
617+
```
608618

609619
### Security Best Practices
610620

@@ -949,7 +959,7 @@ When `EfcptConnectionString` is set (or when a connection string can be resolved
949959
| `EfcptAppSettings` | *(empty)* | Optional `appsettings.json` path used to resolve connection strings |
950960
| `EfcptAppConfig` | *(empty)* | Optional `app.config`/`web.config` path used to resolve connection strings |
951961
| `EfcptConnectionStringName` | `DefaultConnection` | Connection string name/key to read from configuration files |
952-
| `EfcptProvider` | `mssql` | Provider identifier for schema querying and efcpt (Phase 1 supports SQL Server only) |
962+
| `EfcptProvider` | `mssql` | Database provider (mssql, postgres, mysql, sqlite, oracle, firebird, snowflake) |
953963

954964
#### Tool Configuration
955965

@@ -1043,7 +1053,7 @@ Queries database schema metadata and computes a deterministic schema fingerprint
10431053
**Parameters:**
10441054
- `ConnectionString` (required) - Database connection string
10451055
- `OutputDir` (required) - Output directory (writes `schema-model.json` for diagnostics)
1046-
- `Provider` - Provider identifier (default: `mssql`; Phase 1 supports SQL Server only)
1056+
- `Provider` - Database provider identifier (mssql, postgres, mysql, sqlite, oracle, firebird, snowflake)
10471057
- `LogVerbosity` - Logging level
10481058

10491059
**Outputs:**
@@ -1289,8 +1299,8 @@ The behavior of the pipeline is controlled by a set of MSBuild properties. You c
12891299
- Connection string name/key to read from configuration files.
12901300

12911301
- `EfcptProvider` (default: `mssql`)
1292-
- Provider identifier passed to schema querying and efcpt.
1293-
- Phase 1 supports SQL Server only.
1302+
- Database provider identifier.
1303+
- Supported values: `mssql`, `postgres`, `mysql`, `sqlite`, `oracle`, `firebird`, `snowflake`.
12941304

12951305
- `EfcptConfig`
12961306
- Optional override for the EF Core Power Tools configuration file (defaults to `efcpt-config.json` in the project directory when present).

docs/user-guide/api-reference.md

Lines changed: 162 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ These targets are executed as part of the build pipeline:
1212
| `EfcptQuerySchemaMetadata` | Queries database schema (connection string mode) | After resolve |
1313
| `EfcptEnsureDacpac` | Builds `.sqlproj` to DACPAC (DACPAC mode) | After resolve |
1414
| `EfcptStageInputs` | Stages config and templates | After DACPAC/schema |
15-
| `EfcptComputeFingerprint` | Detects if regeneration needed | After staging |
15+
| `EfcptApplyConfigOverrides` | Applies MSBuild property overrides to staged config | After staging |
16+
| `EfcptComputeFingerprint` | Detects if regeneration needed | After overrides |
1617
| `EfcptGenerateModels` | Runs `efcpt` CLI | When fingerprint changes |
1718
| `EfcptAddToCompile` | Adds `.g.cs` files to compilation | Before C# compile |
1819

@@ -86,7 +87,7 @@ Queries database schema metadata and computes a fingerprint (connection string m
8687
|-----------|----------|-------------|
8788
| `ConnectionString` | Yes | Database connection string |
8889
| `OutputDir` | Yes | Output directory (writes `schema-model.json`) |
89-
| `Provider` | No | Provider identifier (default: `mssql`) |
90+
| `Provider` | No | Provider identifier: `mssql`, `postgres`, `mysql`, `sqlite`, `oracle`, `firebird`, `snowflake` (default: `mssql`) |
9091
| `LogVerbosity` | No | Logging level |
9192

9293
**Outputs:**
@@ -180,6 +181,87 @@ Renames generated `.cs` files to `.g.cs`.
180181
| `GeneratedDir` | Yes | Directory containing generated files |
181182
| `LogVerbosity` | No | Logging level |
182183

184+
### ApplyConfigOverrides
185+
186+
Applies MSBuild property overrides to the staged `efcpt-config.json` file. This task enables configuration via MSBuild properties without editing JSON files directly.
187+
188+
**Control Parameters:**
189+
190+
| Parameter | Required | Description |
191+
|-----------|----------|-------------|
192+
| `StagedConfigPath` | Yes | Path to the staged efcpt-config.json file |
193+
| `ApplyOverrides` | No | Whether to apply overrides to user-provided configs (default: `true`) |
194+
| `IsUsingDefaultConfig` | No | Whether using library default config (default: `false`) |
195+
| `LogVerbosity` | No | Logging level |
196+
197+
**Names Section Parameters:**
198+
199+
| Parameter | JSON Property | Description |
200+
|-----------|---------------|-------------|
201+
| `RootNamespace` | `root-namespace` | Root namespace for generated code |
202+
| `DbContextName` | `dbcontext-name` | Name of the DbContext class |
203+
| `DbContextNamespace` | `dbcontext-namespace` | Namespace for the DbContext class |
204+
| `ModelNamespace` | `model-namespace` | Namespace for entity model classes |
205+
206+
**File Layout Section Parameters:**
207+
208+
| Parameter | JSON Property | Description |
209+
|-----------|---------------|-------------|
210+
| `OutputPath` | `output-path` | Output path for generated files |
211+
| `DbContextOutputPath` | `output-dbcontext-path` | Output path for the DbContext file |
212+
| `SplitDbContext` | `split-dbcontext-preview` | Enable split DbContext generation |
213+
| `UseSchemaFolders` | `use-schema-folders-preview` | Use schema-based folders |
214+
| `UseSchemaNamespaces` | `use-schema-namespaces-preview` | Use schema-based namespaces |
215+
216+
**Code Generation Section Parameters:**
217+
218+
| Parameter | JSON Property | Description |
219+
|-----------|---------------|-------------|
220+
| `EnableOnConfiguring` | `enable-on-configuring` | Add OnConfiguring method |
221+
| `GenerationType` | `type` | Type of files to generate |
222+
| `UseDatabaseNames` | `use-database-names` | Use database names |
223+
| `UseDataAnnotations` | `use-data-annotations` | Use DataAnnotation attributes |
224+
| `UseNullableReferenceTypes` | `use-nullable-reference-types` | Use nullable reference types |
225+
| `UseInflector` | `use-inflector` | Pluralize/singularize names |
226+
| `UseLegacyInflector` | `use-legacy-inflector` | Use EF6 Pluralizer |
227+
| `UseManyToManyEntity` | `use-many-to-many-entity` | Preserve many-to-many entity |
228+
| `UseT4` | `use-t4` | Use T4 templates |
229+
| `UseT4Split` | `use-t4-split` | Use T4 with EntityTypeConfiguration |
230+
| `RemoveDefaultSqlFromBool` | `remove-defaultsql-from-bool-properties` | Remove SQL default from bool |
231+
| `SoftDeleteObsoleteFiles` | `soft-delete-obsolete-files` | Cleanup obsolete files |
232+
| `DiscoverMultipleResultSets` | `discover-multiple-stored-procedure-resultsets-preview` | Discover multiple result sets |
233+
| `UseAlternateResultSetDiscovery` | `use-alternate-stored-procedure-resultset-discovery` | Use alternate discovery |
234+
| `T4TemplatePath` | `t4-template-path` | Path to T4 templates |
235+
| `UseNoNavigations` | `use-no-navigations-preview` | Remove navigation properties |
236+
| `MergeDacpacs` | `merge-dacpacs` | Merge .dacpac files |
237+
| `RefreshObjectLists` | `refresh-object-lists` | Refresh object lists |
238+
| `GenerateMermaidDiagram` | `generate-mermaid-diagram` | Generate Mermaid diagram |
239+
| `UseDecimalAnnotationForSprocs` | `use-decimal-data-annotation-for-sproc-results` | Use decimal annotation |
240+
| `UsePrefixNavigationNaming` | `use-prefix-navigation-naming` | Use prefix navigation naming |
241+
| `UseDatabaseNamesForRoutines` | `use-database-names-for-routines` | Use database names for routines |
242+
| `UseInternalAccessForRoutines` | `use-internal-access-modifiers-for-sprocs-and-functions` | Use internal access modifiers |
243+
244+
**Type Mappings Section Parameters:**
245+
246+
| Parameter | JSON Property | Description |
247+
|-----------|---------------|-------------|
248+
| `UseDateOnlyTimeOnly` | `use-DateOnly-TimeOnly` | Map to DateOnly/TimeOnly |
249+
| `UseHierarchyId` | `use-HierarchyId` | Map hierarchyId type |
250+
| `UseSpatial` | `use-spatial` | Map spatial columns |
251+
| `UseNodaTime` | `use-NodaTime` | Use NodaTime types |
252+
253+
**Replacements Section Parameters:**
254+
255+
| Parameter | JSON Property | Description |
256+
|-----------|---------------|-------------|
257+
| `PreserveCasingWithRegex` | `preserve-casing-with-regex` | Preserve casing with regex |
258+
259+
**Override Behavior:**
260+
261+
- When `IsUsingDefaultConfig` is `true`, overrides are always applied regardless of `ApplyOverrides`
262+
- When using a user-provided config, overrides are only applied if `ApplyOverrides` is `true`
263+
- Empty or whitespace-only parameter values are treated as "no override"
264+
183265
## MSBuild Properties Reference
184266

185267
### Core Properties
@@ -234,6 +316,76 @@ Renames generated `.cs` files to `.g.cs`.
234316
| `EfcptFingerprintFile` | `$(EfcptOutput)fingerprint.txt` | Fingerprint cache location |
235317
| `EfcptStampFile` | `$(EfcptOutput).efcpt.stamp` | Generation stamp file |
236318

319+
### Config Override Properties
320+
321+
These properties override values in `efcpt-config.json` without editing the JSON file directly.
322+
323+
| Property | Default | Description |
324+
|----------|---------|-------------|
325+
| `EfcptApplyMsBuildOverrides` | `true` | Whether to apply MSBuild property overrides |
326+
327+
#### Names Section
328+
329+
| Property | JSON Property | Description |
330+
|----------|---------------|-------------|
331+
| `EfcptConfigRootNamespace` | `root-namespace` | Root namespace for generated code |
332+
| `EfcptConfigDbContextName` | `dbcontext-name` | Name of the DbContext class |
333+
| `EfcptConfigDbContextNamespace` | `dbcontext-namespace` | Namespace for the DbContext class |
334+
| `EfcptConfigModelNamespace` | `model-namespace` | Namespace for entity model classes |
335+
336+
#### File Layout Section
337+
338+
| Property | JSON Property | Description |
339+
|----------|---------------|-------------|
340+
| `EfcptConfigOutputPath` | `output-path` | Output path for generated files |
341+
| `EfcptConfigDbContextOutputPath` | `output-dbcontext-path` | Output path for DbContext |
342+
| `EfcptConfigSplitDbContext` | `split-dbcontext-preview` | Split DbContext generation |
343+
| `EfcptConfigUseSchemaFolders` | `use-schema-folders-preview` | Use schema-based folders |
344+
| `EfcptConfigUseSchemaNamespaces` | `use-schema-namespaces-preview` | Use schema-based namespaces |
345+
346+
#### Code Generation Section
347+
348+
| Property | JSON Property | Description |
349+
|----------|---------------|-------------|
350+
| `EfcptConfigEnableOnConfiguring` | `enable-on-configuring` | Add OnConfiguring method |
351+
| `EfcptConfigGenerationType` | `type` | Type of files to generate |
352+
| `EfcptConfigUseDatabaseNames` | `use-database-names` | Use database names |
353+
| `EfcptConfigUseDataAnnotations` | `use-data-annotations` | Use DataAnnotation attributes |
354+
| `EfcptConfigUseNullableReferenceTypes` | `use-nullable-reference-types` | Use nullable reference types |
355+
| `EfcptConfigUseInflector` | `use-inflector` | Pluralize/singularize names |
356+
| `EfcptConfigUseLegacyInflector` | `use-legacy-inflector` | Use EF6 Pluralizer |
357+
| `EfcptConfigUseManyToManyEntity` | `use-many-to-many-entity` | Preserve many-to-many entity |
358+
| `EfcptConfigUseT4` | `use-t4` | Use T4 templates |
359+
| `EfcptConfigUseT4Split` | `use-t4-split` | Use T4 with EntityTypeConfiguration |
360+
| `EfcptConfigRemoveDefaultSqlFromBool` | `remove-defaultsql-from-bool-properties` | Remove SQL default from bool |
361+
| `EfcptConfigSoftDeleteObsoleteFiles` | `soft-delete-obsolete-files` | Cleanup obsolete files |
362+
| `EfcptConfigDiscoverMultipleResultSets` | `discover-multiple-stored-procedure-resultsets-preview` | Discover multiple result sets |
363+
| `EfcptConfigUseAlternateResultSetDiscovery` | `use-alternate-stored-procedure-resultset-discovery` | Use alternate discovery |
364+
| `EfcptConfigT4TemplatePath` | `t4-template-path` | Path to T4 templates |
365+
| `EfcptConfigUseNoNavigations` | `use-no-navigations-preview` | Remove navigation properties |
366+
| `EfcptConfigMergeDacpacs` | `merge-dacpacs` | Merge .dacpac files |
367+
| `EfcptConfigRefreshObjectLists` | `refresh-object-lists` | Refresh object lists |
368+
| `EfcptConfigGenerateMermaidDiagram` | `generate-mermaid-diagram` | Generate Mermaid diagram |
369+
| `EfcptConfigUseDecimalAnnotationForSprocs` | `use-decimal-data-annotation-for-sproc-results` | Use decimal annotation |
370+
| `EfcptConfigUsePrefixNavigationNaming` | `use-prefix-navigation-naming` | Use prefix navigation naming |
371+
| `EfcptConfigUseDatabaseNamesForRoutines` | `use-database-names-for-routines` | Use database names for routines |
372+
| `EfcptConfigUseInternalAccessForRoutines` | `use-internal-access-modifiers-for-sprocs-and-functions` | Use internal access modifiers |
373+
374+
#### Type Mappings Section
375+
376+
| Property | JSON Property | Description |
377+
|----------|---------------|-------------|
378+
| `EfcptConfigUseDateOnlyTimeOnly` | `use-DateOnly-TimeOnly` | Map to DateOnly/TimeOnly |
379+
| `EfcptConfigUseHierarchyId` | `use-HierarchyId` | Map hierarchyId type |
380+
| `EfcptConfigUseSpatial` | `use-spatial` | Map spatial columns |
381+
| `EfcptConfigUseNodaTime` | `use-NodaTime` | Use NodaTime types |
382+
383+
#### Replacements Section
384+
385+
| Property | JSON Property | Description |
386+
|----------|---------------|-------------|
387+
| `EfcptConfigPreserveCasingWithRegex` | `preserve-casing-with-regex` | Preserve casing with regex |
388+
237389
## Configuration File Schemas
238390

239391
### efcpt-config.json
@@ -733,16 +885,20 @@ Renames generated `.cs` files to `.g.cs`.
733885
3. EfcptStageInputs
734886
└── Copies config, renaming, templates to obj/efcpt/
735887
736-
4. EfcptComputeFingerprint
737-
└── Computes XxHash64 of all inputs
888+
4. EfcptApplyConfigOverrides
889+
└── Applies MSBuild property overrides to staged config
890+
└── Uses typed model for all 37 config properties
891+
892+
5. EfcptComputeFingerprint
893+
└── Computes XxHash64 of all inputs (including overrides)
738894
└── Compares with cached fingerprint
739895
740-
5. EfcptGenerateModels (only if fingerprint changed)
896+
6. EfcptGenerateModels (only if fingerprint changed)
741897
└── Executes efcpt CLI
742898
└── Renames files to .g.cs
743899
└── Updates fingerprint cache
744900
745-
6. EfcptAddToCompile
901+
7. EfcptAddToCompile
746902
└── Adds *.g.cs to Compile item group
747903
```
748904

0 commit comments

Comments
 (0)