Skip to content

Add EF Core MoneyConvention for auto-mapping Money types#314

Merged
xavierjohn merged 2 commits intomainfrom
dev/xavier/ef_money
Mar 2, 2026
Merged

Add EF Core MoneyConvention for auto-mapping Money types#314
xavierjohn merged 2 commits intomainfrom
dev/xavier/ef_money

Conversation

@xavierjohn
Copy link
Owner

Introduces MoneyConvention to Trellis.EntityFrameworkCore, enabling automatic mapping of Money properties as owned types with standardized column naming (e.g., Price, PriceCurrency) and correct precision. Updates ApplyTrellisConventions to register this convention. Modifies Money value object for EF Core compatibility. Adds documentation and a comprehensive test suite to verify convention behavior and override support. This eliminates the need for manual configuration of Money properties in EF Core entities.

Introduces MoneyConvention to Trellis.EntityFrameworkCore, enabling automatic mapping of Money properties as owned types with standardized column naming (e.g., Price, PriceCurrency) and correct precision. Updates ApplyTrellisConventions to register this convention. Modifies Money value object for EF Core compatibility. Adds documentation and a comprehensive test suite to verify convention behavior and override support. This eliminates the need for manual configuration of Money properties in EF Core entities.
@github-actions
Copy link

github-actions bot commented Mar 2, 2026

Test Results

3 222 tests  +9   3 222 ✅ +9   2m 6s ⏱️ -54s
   14 suites ±0       0 💤 ±0 
   14 files   ±0       0 ❌ ±0 

Results for commit 9a320d8. ± Comparison against base commit bdad68e.

♻️ This comment has been updated with latest results.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class EF Core convention support for Money so consumers can persist Money properties without manual OwnsOne configuration, while updating documentation and tests to describe and validate the behavior.

Changes:

  • Introduces MoneyConvention and registers it via ApplyTrellisConventions to auto-map Money as an owned type with standardized column naming.
  • Updates Money to support EF Core materialization (private parameterless ctor + private setters).
  • Adds a new MoneyConventionTests suite and updates docs/README/changelog/API reference to document the convention and override behavior.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
Trellis.EntityFrameworkCore/src/MoneyConvention.cs New EF Core convention to register Money as owned and configure column naming/precision/length.
Trellis.EntityFrameworkCore/src/ModelConfigurationBuilderExtensions.cs Registers the new MoneyConvention inside ApplyTrellisConventions.
Trellis.Primitives/src/Primitives/Money.cs Enables EF Core materialization via private setters + private parameterless constructor.
Trellis.EntityFrameworkCore/tests/MoneyConventionTests.cs Adds tests validating column naming, precision/length, round-trips, and explicit override precedence.
docs/docfx_project/articles/integration-ef.md Adds a “Money Property Convention” section to the EF Core integration guide.
Trellis.EntityFrameworkCore/README.md Documents the Money convention in the package README.
Trellis.EntityFrameworkCore/NUGET_README.md Documents the Money convention for NuGet package description.
trellis-api-reference.md Updates API reference to mention Money auto-mapping via conventions.
CHANGELOG.md Notes the addition of the Money convention and Money EF materialization support.
.github/copilot-instructions.md Adds internal guidance documenting Money EF Core behavior.

| `Price` | `Price` | `PriceCurrency` |
| `ShippingCost` | `ShippingCost` | `ShippingCostCurrency` |

Amount columns: `decimal(18,2)`. Currency columns: `nvarchar(3)` (ISO 4217).
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line states amount columns are decimal(18,2), but Money supports some 3-decimal currencies. Update this guidance to match the convention’s final precision/scale decision, or document the rounding/truncation limitation.

Suggested change
Amount columns: `decimal(18,2)`. Currency columns: `nvarchar(3)` (ISO 4217).
By default, amount columns are mapped as `decimal(18,2)`. This works for most currencies but will round or truncate 3-decimal currencies to 2 decimal places; configure `HasPrecision` (for example, `HasPrecision(18,3)` or higher) on the amount property if you need full 3-decimal support. Currency columns: `nvarchar(3)` (ISO 4217).

Copilot uses AI. Check for mistakes.
Comment on lines +67 to +70
amount.Builder.HasAnnotation("Relational:ColumnName", navigationName);
amount.Builder.HasPrecision(18);
amount.Builder.HasScale(2);
}
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The convention forces Money.Amount to scale 2, but Money supports currencies with 3 decimal places (e.g., BHD/KWD/OMR/TND). On providers that enforce scale, this will round/truncate persisted values and break round-trips. Consider using a scale that can represent the maximum supported decimals (e.g., 3) or persisting minor units instead.

Copilot uses AI. Check for mistakes.
Comment on lines +67 to +68
amount.Builder.HasAnnotation("Relational:ColumnName", navigationName);
amount.Builder.HasPrecision(18);
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Relational:ColumnName" is used as a magic-string annotation key (twice in this method). Prefer EF Core’s relational constants/APIs (e.g., RelationalAnnotationNames.ColumnName / relational HasColumnName) so this doesn’t depend on an internal string key.

Copilot uses AI. Check for mistakes.
Comment on lines +967 to +968
| `Price` | `Price` | `PriceCurrency` | `decimal(18,2)` | `nvarchar(3)` |
| `ShippingCost` | `ShippingCost` | `ShippingCostCurrency` | `decimal(18,2)` | `nvarchar(3)` |
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This table lists Money.Amount as decimal(18,2), but Money supports currencies with 3 decimal places (e.g., BHD/KWD/OMR). Either update the convention (and this table) to support the max scale, or explicitly document the rounding/truncation limitation for those currencies.

Suggested change
| `Price` | `Price` | `PriceCurrency` | `decimal(18,2)` | `nvarchar(3)` |
| `ShippingCost` | `ShippingCost` | `ShippingCostCurrency` | `decimal(18,2)` | `nvarchar(3)` |
| `Price` | `Price` | `PriceCurrency` | `decimal(18,3)` | `nvarchar(3)` |
| `ShippingCost` | `ShippingCost` | `ShippingCostCurrency` | `decimal(18,3)` | `nvarchar(3)` |

Copilot uses AI. Check for mistakes.
| `Price` | `Price` | `PriceCurrency` |
| `ShippingCost` | `ShippingCost` | `ShippingCostCurrency` |

Amount columns use `decimal(18,2)` precision. Currency columns use `nvarchar(3)` (ISO 4217).
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs state amount columns use decimal(18,2), but Money supports some 3-decimal currencies. Consider updating the convention/docs to a scale that can represent the max supported decimals, or call out the rounding/truncation limitation explicitly.

Suggested change
Amount columns use `decimal(18,2)` precision. Currency columns use `nvarchar(3)` (ISO 4217).
By default, amount columns are configured as `decimal(18,2)`, which is sufficient for currencies with 2 decimal places but will round or truncate 3-decimal currencies. Use a higher scale (for example, `decimal(18,3)` or `HasPrecision(19, 4)` as shown below) if you need to store 3-decimal currencies without loss. Currency columns use `nvarchar(3)` (ISO 4217).

Copilot uses AI. Check for mistakes.
| `Price` | `Price` | `PriceCurrency` |
| `ShippingCost` | `ShippingCost` | `ShippingCostCurrency` |

Amount columns use `decimal(18,2)` precision. Currency columns use `nvarchar(3)` (ISO 4217).
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section says amount columns use decimal(18,2), but Money supports some 3-decimal currencies. Consider updating the convention/docs to support the max scale (or document the rounding/truncation limitation).

Suggested change
Amount columns use `decimal(18,2)` precision. Currency columns use `nvarchar(3)` (ISO 4217).
By default, amount columns use `decimal(18,2)` precision. This supports most currencies, but three-decimal currencies (for example, JOD, KWD, BHD) will be rounded or truncated to two decimal places unless you override the precision via `OwnsOne` (see below). Currency columns use `nvarchar(3)` (ISO 4217).

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +50
| `Price` | `Price` | `PriceCurrency` | `decimal(18,2)` | `nvarchar(3)` |
| `ShippingCost` | `ShippingCost` | `ShippingCostCurrency` | `decimal(18,2)` | `nvarchar(3)` |
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table lists the amount type as decimal(18,2), but Money supports currencies with 3 decimal places. Either adjust the convention (and this table) to support the max scale, or explicitly document the persistence rounding behavior for 3-decimal currencies.

Suggested change
| `Price` | `Price` | `PriceCurrency` | `decimal(18,2)` | `nvarchar(3)` |
| `ShippingCost` | `ShippingCost` | `ShippingCostCurrency` | `decimal(18,2)` | `nvarchar(3)` |
| `Price` | `Price` | `PriceCurrency` | `decimal(18,3)` | `nvarchar(3)` |
| `ShippingCost` | `ShippingCost` | `ShippingCostCurrency` | `decimal(18,3)` | `nvarchar(3)` |

Copilot uses AI. Check for mistakes.
CHANGELOG.md Outdated

#### Trellis.EntityFrameworkCore — Money Convention

- **`MoneyConvention`** — `ApplyTrellisConventions` now automatically maps `Money` properties as owned types with `{PropertyName}` (decimal 18,2) + `{PropertyName}Currency` (nvarchar 3) columns. No `OwnsOne` configuration needed. Explicit `OwnsOne` takes precedence.
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changelog claims {PropertyName} is stored as decimal 18,2, but Money supports some 3-decimal currencies. If you change the convention to a higher scale to avoid truncation, update this entry; otherwise consider noting the limitation/rounding behavior.

Suggested change
- **`MoneyConvention`**`ApplyTrellisConventions` now automatically maps `Money` properties as owned types with `{PropertyName}` (decimal 18,2) + `{PropertyName}Currency` (nvarchar 3) columns. No `OwnsOne` configuration needed. Explicit `OwnsOne` takes precedence.
- **`MoneyConvention`**`ApplyTrellisConventions` now automatically maps `Money` properties as owned types with `{PropertyName}` (decimal 18,2) + `{PropertyName}Currency` (nvarchar 3) columns. Note: this default mapping uses two decimal places; currencies requiring three decimal places will be rounded by the database provider to two decimal places. No `OwnsOne` configuration needed. Explicit `OwnsOne` takes precedence.

Copilot uses AI. Check for mistakes.
@codecov
Copy link

codecov bot commented Mar 2, 2026

Codecov Report

❌ Patch coverage is 95.23810% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 85.57%. Comparing base (bdad68e) to head (9a320d8).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
Trellis.EntityFrameworkCore/src/MoneyConvention.cs 94.11% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #314      +/-   ##
==========================================
+ Coverage   85.51%   85.57%   +0.05%     
==========================================
  Files         158      159       +1     
  Lines        4930     4949      +19     
  Branches     1012     1019       +7     
==========================================
+ Hits         4216     4235      +19     
+ Misses        419      418       -1     
- Partials      295      296       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

MoneyConvention now maps Money.Amount columns as decimal(18,3)
(instead of decimal(18,2)) to support all ISO 4217 minor units,
including currencies with three decimal places (e.g., KWD, BHD).
Docs and tests updated to reflect this change. Implementation
now uses RelationalAnnotationNames.ColumnName and sets scale to 3.
Added test for three-decimal currency round-trip. Switched
package references to Microsoft.EntityFrameworkCore.Relational.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Comment on lines 955 to +958
// In OnModelCreating or ConfigureConventions
configurationBuilder.ApplyTrellisConventions(typeof(Order).Assembly);
// Auto-registers converters for all IScalarValue and RequiredEnum types
// Auto-maps Money properties as owned types (Amount + Currency columns)
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says ApplyTrellisConventions can be used in OnModelCreating, but the API is an extension on ModelConfigurationBuilder and is only callable from ConfigureConventions. Update this snippet/comment to avoid misleading usage.

Suggested change
// In OnModelCreating or ConfigureConventions
configurationBuilder.ApplyTrellisConventions(typeof(Order).Assembly);
// Auto-registers converters for all IScalarValue and RequiredEnum types
// Auto-maps Money properties as owned types (Amount + Currency columns)
// In your DbContext.ConfigureConventions override
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.ApplyTrellisConventions(typeof(Order).Assembly);
// Auto-registers converters for all IScalarValue and RequiredEnum types
// Auto-maps Money properties as owned types (Amount + Currency columns)
}

Copilot uses AI. Check for mistakes.
@xavierjohn xavierjohn merged commit 7f1fda4 into main Mar 2, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants