⚠️ Preview Release: RoomSharp is currently in Preview. Learn more →

Type Converters

Handle custom type mappings between C# and database

Overview

Type converters allow you to customize how C# types are stored in and retrieved from the database. This is useful for types that don't have direct database equivalents or when you need custom serialization logic.

Creating a Type Converter

Implement ITypeConverter

C#
public interface ITypeConverter<TFrom, TTo>
{
    TTo Convert(TFrom value);
    TFrom ConvertBack(TTo value);
}

Or Extend TypeConverter Base Class

C#
public class DateTimeTicksConverter : TypeConverter<DateTime, long>
{
    public override long Convert(DateTime value)
    {
        return value.ToUniversalTime().Ticks;
    }

    public override DateTime ConvertBack(long value)
    {
        return new DateTime(value, DateTimeKind.Utc);
    }
}

Using Type Converters

Per-Property Converters

Apply the [TypeConverter] attribute to individual properties:

C#
[Entity(TableName = "posts")]
public class Post
{
    [PrimaryKey(AutoGenerate = true)]
    public long Id { get; set; }

    public string Title { get; set; }

    [TypeConverter(ConverterType = typeof(DateTimeTicksConverter))]
    public DateTime PublishedAt { get; set; }
    
    [TypeConverter(ConverterType = typeof(JsonConverter<List<string>>))]
    public List<string> Tags { get; set; }
}

Global Converters

Register converters globally to apply them across all entities:

C#
var db = RoomDatabase.Builder<AppDatabaseImpl>()
    .UseSqlite("app.db")
    .Build();

// Register global converters
db.Converters.Register(new GuidToStringConverter());
db.Converters.Register(new DateTimeOffsetConverter());

Common Use Cases

1. DateTime to Ticks

C#
public class DateTimeTicksConverter : TypeConverter<DateTime, long>
{
    public override long Convert(DateTime value) 
        => value.ToUniversalTime().Ticks;

    public override DateTime ConvertBack(long value) 
        => new DateTime(value, DateTimeKind.Utc);
}

2. Guid to String

C#
public class GuidToStringConverter : TypeConverter<Guid, string>
{
    public override string Convert(Guid value) 
        => value.ToString("N");

    public override Guid ConvertBack(string value) 
        => Guid.Parse(value);
}

3. JSON Serialization

C#
public class JsonConverter<T> : TypeConverter<T, string>
{
    public override string Convert(T value)
    {
        return JsonSerializer.Serialize(value);
    }

    public override T ConvertBack(string value)
    {
        return JsonSerializer.Deserialize<T>(value) 
            ?? throw new InvalidOperationException("Deserialization failed");
    }
}

// Usage
[Entity(TableName = "settings")]
public class AppSettings
{
    [PrimaryKey]
    public int Id { get; set; }

    [TypeConverter(ConverterType = typeof(JsonConverter<UserPreferences>))]
    public UserPreferences Preferences { get; set; }
}

4. Enum to String

C#
public class EnumToStringConverter<TEnum> : TypeConverter<TEnum, string> 
    where TEnum : struct, Enum
{
    public override string Convert(TEnum value) 
        => value.ToString();

    public override TEnum ConvertBack(string value) 
        => Enum.Parse<TEnum>(value);
}

Built-in Converters

RoomSharp includes built-in converters for common types:

  • DateTimelong (Unix ticks)
  • boolint (0 or 1)
  • Guidstring
  • TimeSpanlong (ticks)
  • DateTimeOffsetlong (Unix milliseconds)

Converter Discovery

RoomSharp automatically discovers converters in this order:

  1. Property-level [TypeConverter] attributes
  2. Globally registered converters via db.Converters.Register()
  3. Built-in converters

Best Practices

  • Keep converters simple and stateless
  • Handle null values appropriately
  • Consider using existing serialization libraries (JSON, MessagePack)
  • Register global converters during application startup
  • Document custom converters for your team
  • Test converters with edge cases (null, empty, max values)

Performance Considerations

Type converters are called for every read/write operation:

  • Keep Convert and ConvertBack methods fast
  • Avoid expensive operations (file I/O, network calls)
  • Cache serializer instances when possible
  • Consider using Span<T> for large data conversions

Next Steps