# Mapping and Entities DynamORM mapping is a core feature, not just a convenience layer. It controls: - class-to-table resolution - property-to-column resolution - key semantics for updates/deletes - schema override details (`DbType`, size, precision, scale) - lifecycle operations (`Insert`, `Update`, `Delete`, `Refresh`, `Save`) ## Mapping Resolution Model At runtime, `DynamicMapperCache` builds and caches `DynamicTypeMap` for each type. `DynamicTypeMap` is used by: - typed query execution (`Execute()`) - dynamic projection to classes - entity lifecycle operations in `DynamicEntityBase` - procedure result mapping - validation (`ValidateObject`) ## `TableAttribute` ```csharp [Table(Name = "users", Owner = "dbo", Override = true)] public class User { // ... } ``` Fields: - `Name`: table name override. - `Owner`: schema/owner segment. - `Override`: prefer attribute schema over database schema (important when provider schema is limited). ## `ColumnAttribute` ```csharp public class User { [Column("id", isKey: true)] public int Id { get; set; } [Column("email")] public string Email { get; set; } [Column("created_at", false, DbType.DateTime)] public DateTime CreatedAt { get; set; } } ``` Important flags: - `IsKey` - `AllowNull` - `IsNoInsert` - `IsNoUpdate` - `Type`, `Size`, `Precision`, `Scale` These influence generated parameters and update/insert behavior. ## Advanced Mapping Example ```csharp [Table(Name = "users", Override = true)] public class UserEntity : DynamicEntityBase { [Column("id", true, DbType.Int32)] public int Id { get; set; } [Column("code", false, DbType.String, 32)] public string Code { get; set; } [Column("email", false, DbType.String, 256)] public string Email { get; set; } [Column("created_at", false, DbType.DateTime)] [Ignore] public DateTime CreatedAtInternal { get; set; } } ``` ## Ignore and Partial Mapping Use `[Ignore]` when: - property is computed locally - property is not persisted - property should not participate in auto update/insert ## Validation (`RequiredAttribute`) `RequiredAttribute` can enforce range and null/empty semantics. ```csharp public class Profile { [Required(1f, 10f)] public int Rank { get; set; } [Required(2, true)] [Required(7, 18, ElementRequirement = true)] public decimal[] Scores { get; set; } } var issues = DynamicMapperCache.GetMapper().ValidateObject(profile); ``` Validation scenarios are verified in `DynamORM.Tests/Helpers/Validation/ObjectValidationTest.cs`. ## `DynamicEntityBase` Lifecycle `DynamicEntityBase` tracks state and changed fields. State-driven `Save(db)` behavior: - `New` => `Insert` - `Existing` + modified => `Update` - `ToBeDeleted` => `Delete` - `Deleted` => throws for write operations - `Unknown` => throws (explicit state needed) Example entity with property change tracking: ```csharp public class UserEntity : DynamicEntityBase { private string _first; [Column("id", true)] public int Id { get; set; } [Column("first")] public string First { get => _first; set { OnPropertyChanging(nameof(First), _first, value); _first = value; } } } ``` Typical flow: ```csharp var user = db.From() .Where(x => x.id == 19) .Execute() .First(); user.SetDynamicEntityState(DynamicEntityState.Existing); user.First = "Yuri"; user.Save(db); // updates only changed fields when possible ``` ## Refresh and Key Requirements `Refresh(db)`, `Update(db)`, and `Delete(db)` rely on key columns. If no key mapping is available, entity update/delete operations throw. Always map at least one key (`IsKey = true`) for mutable entities. ## Repository Layer (`DynamicRepositoryBase`) `DynamicRepositoryBase` provides: - `GetAll()` - `GetByQuery(...)` - `Insert(...)` - `Update(...)` - `Delete(...)` - `Save(...)` It validates that query tables match mapped type `T` before executing typed enumeration. ## Practical Recommendations - Always define keys explicitly in mapped models. - Use `Override = true` when provider schema metadata is weak or inconsistent. - Keep entities focused on persistence fields; use `[Ignore]` for non-persisted members. - Use validation on boundary objects before persisting.