Repository navigation
Renamed properties ([Column]/[BsonProperty]/[JsonPropertyName]) fail on insert: field name not registered #184
Description
Version: BLite 5.2.3 (main at cb8a521), .NET 10.0.12, macOS 27 arm64 (the code path is platform-independent)
What happens
A typed entity with a property renamed through [Column("...")], [BsonProperty("...")] or [JsonPropertyName("...")] can't be inserted:
System.InvalidOperationException : BSON Key 'full_name' not found in dictionary cache. Ensure all keys are registered before serialization.
at BLite.Bson.BsonSpanWriter.WriteElementHeader(BsonType type, String name)
at BLite.Bson.BsonSpanWriter.WriteString(String name, String value)
at ..._CustomerMapper.Serialize(Customer entity, BsonSpanWriter writer)
at BLite.Core.Collections.DocumentCollection`2.SerializeWithRetry(T entity, Byte[]& rentedBuffer)
at BLite.Core.Collections.DocumentCollection`2.InsertCore(T entity, ITransaction transaction)
The insert only works if the new name happens to be another property's conventional name, which is already registered.
Cause
The two sides disagree on the field names:
- Writing: the mapper generator honours the attributes.
EntityAnalyzersetsBsonFieldNamefrom[BsonProperty]/[JsonPropertyName]/[Column]and falls back toprop.Name.ToLowerInvariant()(EntityAnalyzer.cs:162-175). The generatedSerializewrites that name (CodeGenerator.cs:291), e.g.writer.WriteString("full_name", entity.Name). - Registering: the key dictionary is filled from
_mapper.UsedKeys(DocumentCollection.cs:852).UsedKeyscomes fromGetSchema(), i.e.BsonSchemaGenerator.FromType<T>()(BaseMappers.cs:26-29), which names every fieldname.ToLowerInvariant()and ignores the attributes (BsonSchemaGenerator.cs:74-79). So"name"is registered,"full_name"is not, andBsonSpanWriter.WriteElementHeaderthrows (BsonSpanWriter.cs:106).
The query side makes the same lowercase assumption: BsonExpressionEvaluator (:129 and the similar branches after it) and ProjectionAnalyzer.ToBsonName (:77-79). Once inserts work, BSON-level filters and projections on a renamed property would look in the wrong field. They would find nothing, or another property's value when its conventional name is the renamed one. I haven't verified this end to end, since the insert fails first.
Repro
An entity and context in tests/BLite.Shared (so the mapper is generated), and a test in tests/BLite.Tests:
// tests/BLite.Shared
using System.ComponentModel.DataAnnotations.Schema;
using System.Text.Json.Serialization;
public class Customer
{
public ObjectId Id { get; set; }
[Column("full_name")] public string Name { get; set; } = "";
[JsonPropertyName("mail")] public string Email { get; set; } = "";
}
public partial class RenameReproContext : DocumentDbContext
{
public DocumentCollection<ObjectId, Customer> Customers { get; set; } = null!;
public RenameReproContext(string path) : base(path) => InitializeCollections();
protected override void OnModelCreating(ModelBuilder modelBuilder)
=> modelBuilder.Entity<Customer>().ToCollection("customers");
}
// tests/BLite.Tests
[Fact]
public async Task Renamed_Properties_Can_Be_Inserted()
{
var path = Path.Combine(Path.GetTempPath(), $"rename_{Guid.NewGuid():N}.db");
using var db = new RenameReproContext(path);
await db.Customers.InsertAsync(new Customer { Name = "Ada", Email = "ada@example.com" }); // throws
}The generated mapper contains writer.WriteString("full_name", entity.Name) and writer.WriteString("mail", entity.Email).
Possible fix
Give the runtime one source of truth for the field names, and use it for key registration, BSON predicates and projections:
- Option 1: the generator emits a
UsedKeys(orGetSchema) override with theBsonFieldNames, plus a property-name → field-name map. - Option 2:
BsonSchemaGeneratorreads the same three attributes the generator does. That fixes registration but not the query side, which works from member names.
Option 1 seems more robust, since it can't drift from what Serialize writes. I'm happy to send a PR for whichever you prefer.
(Found while working on #183, which skips its raw-BSON aggregate path for renamed properties for this reason.)
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
fix(generator): report members stored under the same field as BLITE001 (
fix(mapper): register, query and project renamed properties under the…