--- title: "Type Converters" description: "Convert between model types and database provider types with RoomSharp converters." canonical: "https://roomsharp.dev/docs/v0.5.4/type-converters" source: "src/content/v0.5.4/type-converters.mdx" --- # Type Converters Type converters map a model property type to a database-compatible provider type. They are used by generated DAO code when binding insert/update parameters and when materializing query results. ## Converter Shape Implement `ITypeConverter` or derive from `TypeConverter`. - `TProvider` is the database-facing type. - `TModel` is the C# property type. - `ToProvider(...)` runs when RoomSharp writes a model value to the database. - `FromProvider(...)` runs when RoomSharp reads a database value back into the model. ```csharp using RoomSharp.Converters; public sealed class DateTimeTicksConverter : TypeConverter { public override DateTime FromProvider(long provider) => new(provider, DateTimeKind.Utc); public override long ToProvider(DateTime model) => model.ToUniversalTime().Ticks; } ``` ## Applying A Converter Use `[TypeConverter]` on an entity property. ```csharp using RoomSharp.Attributes; [Entity(TableName = "posts")] public class Post { [PrimaryKey(AutoGenerate = true)] public long Id { get; set; } [TypeConverter(ConverterType = typeof(DateTimeTicksConverter))] public DateTime PublishedAt { get; set; } } ``` You can also use the constructor form: ```csharp [TypeConverter(typeof(DateTimeTicksConverter))] public DateTime PublishedAt { get; set; } ``` RoomSharp currently exposes the non-generic `[TypeConverter(...)]` attribute. Use `typeof(...)` to point to the converter type. ## What The Generator Does When a property has a converter, the source generator records the converter type and emits converter-aware code: - Insert and update bindings call `ToProvider(...)`. - Query materialization calls `FromProvider(...)`. - Generated converter fields are reused instead of constructing a converter for every row. This keeps conversion out of reflection-heavy runtime paths. For schema generation, use `[ColumnInfo(TypeAffinity = "...")]` when the physical column type must follow the converter provider type exactly. This is especially useful for provider-specific storage choices such as integer ticks, JSON text, binary payloads, or database-native GUID columns. ## Built-In Converters RoomSharp includes a small set of built-in converters. ### Enum Converters Namespace: ```csharp using RoomSharp.Converters.BuiltIn.Enums; ``` | Converter | Provider Type | Model Type | Use Case | |-----------|---------------|------------|----------| | `EnumToStringConverter` | `string` | `TEnum` | Store enum names as text. | | `DynamicEnumToStringConverter` | `string` | `TEnum` | Parse enum names case-insensitively. | | `EnumToIntConverter` | `int` | `TEnum` | Store enum values as integers. | | `EnumToShortConverter` | `short` | `TEnum` | Store enum values as shorts. | | `EnumToByteConverter` | `byte` | `TEnum` | Store enum values as bytes. | Example: ```csharp public enum UserStatus { Active, Suspended, Deleted } [TypeConverter(ConverterType = typeof(EnumToStringConverter))] public UserStatus Status { get; set; } ``` ### Primitive Converters Namespace: ```csharp using RoomSharp.Converters.BuiltIn.Primitives; ``` | Converter | Provider Type | Model Type | Use Case | |-----------|---------------|------------|----------| | `BoolToIntConverter` | `int` | `bool` | Store booleans as `0` / `1`. | | `GuidToStringConverter` | `string` | `Guid` | Store GUID values as text. | | `IntToByteConverter` | `byte` | `int` | Store an integer through a byte provider value. | Example: ```csharp [TypeConverter(ConverterType = typeof(GuidToStringConverter))] public Guid ExternalId { get; set; } ``` ### DateTime Converters Namespace: ```csharp using RoomSharp.Converters.BuiltIn.DateTime; ``` | Converter | Provider Type | Model Type | Use Case | |-----------|---------------|------------|----------| | `DateTimeToLongConverter` | `long` | `DateTime` | Store `DateTime.Ticks` in an integer column. | | `DateTimeToIntConverter` | `int` | `DateTime` | Store `DateTime.Ticks` through a checked `int` conversion when values are known to fit. | | `StringToDateTimeConverter` | `string` | `DateTime` | Store a formatted date string. | `DateTimeToLongConverter` and `DateTimeToIntConverter` store `DateTime.Ticks`, not Unix time. The `int` converter is only appropriate for constrained values because ordinary `DateTime` tick values usually exceed `int.MaxValue`. ### JSON List Converter Namespace: ```csharp using RoomSharp.Converters.Json; ``` | Converter | Provider Type | Model Type | Use Case | |-----------|---------------|------------|----------| | `ListToJsonConverter` | `string` | `List` | Store a list as JSON text. | Example: ```csharp [TypeConverter(ConverterType = typeof(ListToJsonConverter))] public List Tags { get; set; } = []; ``` `Utf8JsonListStringConverter` is a low-level helper for `List` serialization and deserialization. For entity properties, use `ListToJsonConverter`. ## Custom JSON Converter For object graphs that are not lists, create an explicit converter for your model type: ```csharp using System.Text.Json; using RoomSharp.Converters; public sealed class UserSettingsConverter : TypeConverter { public override UserSettings FromProvider(string provider) => JsonSerializer.Deserialize(provider)!; public override string ToProvider(UserSettings model) => JsonSerializer.Serialize(model); } [TypeConverter(ConverterType = typeof(UserSettingsConverter))] public UserSettings Settings { get; set; } = new(); ``` This is preferable to relying on a generic catch-all JSON converter because it keeps the conversion contract explicit per model type. ## Runtime Registration RoomSharp registers converters discovered on entity properties during database initialization. You can also register converters manually through `RoomDatabase.Converters`: ```csharp var db = RoomDatabase.Builder() .UseSqlite("app.db") .Build(); db.Converters.Register(new DateTimeTicksConverter()); ``` The registry can also be used directly: ```csharp if (db.Converters.TryGet(out var converter)) { var stored = converter.ToProvider(DateTime.UtcNow); } ``` Generated DAO paths use converter metadata from your model attributes. Runtime registration is most useful for application-level conversion utilities or shared conversion code. ## Nullable Values If the model property and provider value can both be null, make that explicit in the converter type: ```csharp public sealed class NullableDateTimeTicksConverter : TypeConverter { public override DateTime? FromProvider(long? provider) => provider.HasValue ? new DateTime(provider.Value, DateTimeKind.Utc) : null; public override long? ToProvider(DateTime? model) => model?.ToUniversalTime().Ticks; } ``` Then apply it to a nullable property: ```csharp [TypeConverter(ConverterType = typeof(NullableDateTimeTicksConverter))] public DateTime? PublishedAt { get; set; } ``` ## Default Type Mapping Without a converter, RoomSharp maps common CLR types to provider-specific column types when it generates schema SQL. The exact SQL type depends on the active database provider. | C# Type | SQLite | SQL Server | MySQL | PostgreSQL | |---------|--------|------------|-------|------------| | `int` | `INTEGER` | `INT` | `INT` | `INTEGER` | | `long` | `INTEGER` | `BIGINT` | `BIGINT` | `BIGINT` | | `short` | `INTEGER` | `SMALLINT` | `SMALLINT` | `SMALLINT` | | `byte` | `INTEGER` | `TINYINT` | `TINYINT` | `SMALLINT` | | `bool` | `INTEGER` | `BIT` | `TINYINT(1)` | `BOOLEAN` | | `string` | `TEXT` | `NVARCHAR(255)` | `VARCHAR(255)` | `TEXT` | | `float`, `double` | `REAL` | `FLOAT` | `DOUBLE` | `DOUBLE PRECISION` | | `decimal` | `REAL` | `DECIMAL(18,6)` | `DECIMAL(18,6)` | `NUMERIC(18,6)` | | `byte[]` | `BLOB` | `VARBINARY(MAX)` | `BLOB` | `BYTEA` | | `DateTime` | `TEXT` | `DATETIME2` | `DATETIME` | `TIMESTAMP` | | `Guid` | `TEXT` | `UNIQUEIDENTIFIER` | `CHAR(36)` | `UUID` | Use a converter when you need a different storage representation or when the property type is not directly supported by the provider. Use `[ColumnInfo(TypeAffinity = "...")]` when a table column needs an explicit provider-specific SQL type. ## Checklist - Use `TypeConverter` for most custom converters. - Keep `TProvider` database-compatible: `string`, numeric primitives, `byte[]`, or another provider-supported type. - Apply converters with `[TypeConverter(ConverterType = typeof(...))]` or `[TypeConverter(typeof(...))]`. - Use built-in enum, primitive, DateTime, and list JSON converters where they match your storage needs. - Write explicit converters for complex JSON objects. - Use nullable provider/model types when nulls are expected. ## Next Steps - [Entities](/docs/v0.5.4/entities) - Apply converters to entity properties. - [Attributes Reference](/docs/v0.5.4/attributes) - See the full attribute list. - [Generated Code](/docs/v0.5.4/generated-code) - Understand how converters appear in generated DAO code.