Cosmos: Warn when decimals is mapped without a value converter (imprecise arithmetic)
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
Description
### Bug description
You are able to model an entity with a decimal property, while cosmos stores all floating point numbers as doubles. This can cause data and accuracy loss, aswell as unexpected rounding errors in mathematical expressions.
### Your code
```csharp
using Microsoft.EntityFrameworkCore;
var options = new DbContextOptionsBuilder()
.EnableSensitiveDataLogging()
.UseCosmos("AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==", "test")
.Options;
using var context = new MyDbContext(options);
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
var entity = new Product
{
TaxRate = 1/6m,
Compound1 = 0.1m,
Compound2 = 0.2m,
BigDecimal = 12345678901234567890.0m
};
context.Products.Add(entity);
await context.SaveChangesAsync();
context.ChangeTracker.Clear();
// Round-trip the entity and observe accuracy loss
var loaded = await context.Products.SingleAsync();
Console.WriteLine();
Console.WriteLine($"Original TaxRate: {entity.TaxRate}");
Console.WriteLine($"Loaded TaxRate: {loaded.TaxRate}");
Console.WriteLine($"TaxRate match: {loaded.TaxRate == entity.TaxRate}"); // False
// Projection with mathematical operations suffers from rounding errors
var projected = await context.Products
.Select(p => (p.Compound1 + p.Compound2) * 100000000000000000)
.SingleAsync();
var expectedProjected = (0.1m + 0.2m) * 100000000000000000;
Console.WriteLine();
Console.WriteLine($"Expected projection (decimal math): {expectedProjected}");
Console.WriteLine($"Projected (Cosmos math): {projected}");
Console.WriteLine($"Projection match: {projected == expectedProjected}"); // False
Console.WriteLine();
Console.WriteLine("Big decimal not stored correctly:");
Console.WriteLine($"Original BigDecimal: {entity.BigDecimal}");
Console.WriteLine($"Loaded BigDecimal: {loaded.BigDecimal}");
Console.WriteLine($"BigDecimal match: {loaded.BigDecimal == entity.BigDecimal}"); // False
public class MyDbContext(DbContextOptions options) : DbContext(options)
{
public DbSet Products { get; set; } = null!;
}
public class Product
{
public int Id { get; set; }
public decimal TaxRate { get; set; }
public decimal Compound1 { get; set; }
public decimal Compound2 { get; set; }
public decimal BigDecimal { get; set; }
}
```
### Output
```text
Original TaxRate: 0.1666666666666666666666666667
Loaded TaxRate: 0.166666666666667
TaxRate match: False
Expected projection (decimal math): 30000000000000000.0
Projected (Cosmos math): 30000000000000004
Projection match: False
Big decimal not stored correctly:
Original BigDecimal: 12345678901234567890.0
Loaded BigDecimal: 12345678901234600000
BigDecimal match: False
```
### EF Core version
10.0.0
### Database provider
Microsoft.EntityFrameworkCore.Cosmos
### Target framework
.NET 10
### Operating system
W11
### IDE
VS2026
Contributor guide
Assessment
This issue has not been assessed yet.