Skip to content

Model mapping

Mirko Da Corte edited this page Aug 25, 2026 · 9 revisions

A model map tells Scrinium how a type is serialized to and from a document. You register maps in IModelMapsCollectors and, from there, on the engine's MapRegistry. Customization uses the MongoDB driver's BsonClassMap, so if you know driver class mapping you already know most of this.

Collectors

Implement IModelMapsCollector and register maps in Register, which receives the engine:

using Etherna.Scrinium.Core;
using Etherna.Scrinium.Core.Serialization;

class CatMap : IModelMapsCollector
{
    public void Register(IDbContextEngine dbContextEngine)
    {
        dbContextEngine.MapRegistry.AddModelMap<Cat>("cd37bafa-a36d-4b1f-815a-deb50c49d030");
    }
}

List your collectors on the context so they're registered at build:

protected override IEnumerable<IModelMapsCollector> ModelMapsCollectors =>
    [new ModelBaseMap(), new CatMap()];

Tip. With many models, discover collectors by reflection over a namespace instead of listing them by hand. One collector per aggregate keeps maps organized.

AddModelMap

IModelMapBuilder<TModel> AddModelMap<TModel>(
    string activeModelMapSchemaId,
    Action<BsonClassMap<TModel>>? activeModelMapSchemaInitializer = null);
  • activeModelMapSchemaId — an immutable string stamped into every document this map writes, identifying the schema that produced it (see Schema id rules); GUIDs are convenient. This is what makes Versioned schemas possible.
  • The initializer receives a BsonClassMap<TModel> (the driver's class map). Without it, Scrinium applies AutoMap().

The returned IModelMapBuilder<TModel> is where you add secondary and fallback schemas.

Customizing with BsonClassMap

Inside the initializer you use the standard driver class-mapping API:

dbContextEngine.MapRegistry.AddModelMap<EntityModelBase<string>>(
    "81dd8b35-a0af-44d9-80b4-ab7ae9844eb5",
    map =>
    {
        map.AutoMap();

        // store a string Id as an ObjectId, generated on insert
        map.IdMemberMap.SetSerializer(new StringSerializer(BsonType.ObjectId))
                       .SetIdGenerator(new StringObjectIdGenerator());
    });

Common customizations: AutoMap(), MapMember(x => x.Prop) / UnmapMember(...), GetMemberMap(x => x.Prop).SetElementName("db_name"), .SetSerializer(...), .SetDefaultValue(...), .SetIgnoreIfNull(true). To attach a reference serializer to a member, set it with SetSerializer here. Dates: on each persisted date member set map.GetMemberMap(x => x.SomeDate).SetSerializer(new DateTimeOffsetSerializer(BsonType.DateTime)) to store a plain, indexable BSON Date — with no configuration the driver default writes a {DateTime, Ticks, Offset} document. See Domain models for the date convention.

Note. The sample names the initializer parameter schema; it is a BsonClassMap<TModel>. We use map here to avoid confusion with a schema (a versioned map — see below).

Schema id rules

Schema ids must be unique across the whole database context, not just within one map. The id fallback is reserved for fallback schemas. A duplicate id fails fast at engine build with a detailed ScriniumDuplicateSchemaIdException, so collisions surface at startup, never in production data. (Reference serializer configurations are separate id spaces and aren't part of this check.)

Custom serializer maps

For a value type you'd rather serialize with your own IBsonSerializer (money as Decimal128, an encrypted string, a domain primitive), register it with AddCustomSerializerMap instead of a class map:

dbContextEngine.MapRegistry.AddCustomSerializerMap(new EthAddressSerializer());

A custom serialized type can also key an entity, serving its Id: register the custom serializer map before the model maps of the entities it keys. See Custom serializers for what Scrinium provides, how to write your own, and custom serialized entity ids.


Next: Versioned schemas to evolve a type over time, References and denormalization to relate documents, or Custom serializers for member-level serialization.

Clone this wiki locally