AOT Setup
For AOT/trimming compatibility, create a source-generated JSON context:
[JsonSerializable(typeof(User))][JsonSerializable(typeof(Order))][JsonSerializable(typeof(Address))][JsonSerializable(typeof(OrderLine))]public partial class AppJsonContext : JsonSerializerContext;Create an instance with your desired options and attach it to the store:
var ctx = new AppJsonContext(new JsonSerializerOptions{ PropertyNamingPolicy = JsonNamingPolicy.CamelCase});
var store = new SqliteDocumentStore(new DocumentStoreOptions{ ConnectionString = "Data Source=mydata.db", JsonSerializerOptions = ctx.Options, UseReflectionFallback = false // recommended for AOT});Multiple JSON contexts
Section titled “Multiple JSON contexts”If your types are spread across multiple JsonSerializerContext classes, use TypeInfoResolverChain to combine them. The chain is tried in order — the first context that knows about the requested type wins.
var options = new JsonSerializerOptions{ PropertyNamingPolicy = JsonNamingPolicy.CamelCase};options.TypeInfoResolverChain.Add(UserJsonContext.Default);options.TypeInfoResolverChain.Add(OrderJsonContext.Default);
var store = new SqliteDocumentStore(new DocumentStoreOptions{ ConnectionString = "Data Source=mydata.db", JsonSerializerOptions = options, UseReflectionFallback = false});Optional JsonTypeInfo<T> parameters
Section titled “Optional JsonTypeInfo<T> parameters”All JsonTypeInfo<T> parameters across the entire API are optional (= null default). When omitted, type info is automatically resolved from the configured JsonSerializerOptions.TypeInfoResolver. You can configure a JsonSerializerContext once and skip passing JsonTypeInfo<T> on every call — while retaining full AOT safety.
With explicit JsonTypeInfo<T> | With auto-resolution (recommended) |
|---|---|
store.Set(user, ctx.User) | store.Set(user) |
store.Get("id", ctx.User) | store.Get<User>("id") |
store.Upsert(patch, ctx.User) | store.Upsert(patch) |
store.SetProperty("id", (User u) => u.Age, 31, ctx.User) | store.SetProperty<User>("id", u => u.Age, 31) |
store.RemoveProperty("id", (User u) => u.Email, ctx.User) | store.RemoveProperty<User>("id", u => u.Email) |
store.Query(ctx.User) | store.Query<User>() |
store.Query<User>("sql", ctx.User, parms) | store.Query<User>("sql", parameters: parms) |
store.QueryStream<User>("sql", ctx.User, parms) | store.QueryStream<User>("sql", parameters: parms) |
// All of these are AOT-safe when ctx.Options is configuredvar user = new User { Name = "Alice", Age = 25 };await store.Set(user); // user.Id is auto-generatedvar fetched = await store.Get<User>(user.Id);var all = await store.Query<User>().ToList();await store.Upsert(new User { Id = user.Id, Name = "Alice", Age = 30 });
var results = await store.Query<User>( "json_extract(Data, '$.age') > @minAge", parameters: new { minAge = 30 });
await foreach (var u in store.Query<User>().ToAsyncEnumerable()) Console.WriteLine(u.Name);Disabling reflection fallback
Section titled “Disabling reflection fallback”By default (UseReflectionFallback = true), if no resolver is configured or the type isn’t registered, methods fall back to reflection-based serialization. This preserves backwards compatibility.
For AOT deployments, set UseReflectionFallback = false. Reflection-based serialization produces hard-to-diagnose errors under trimming and AOT. With this flag disabled, you get a clear InvalidOperationException at the point of use:
InvalidOperationException: No JsonTypeInfo registered for type 'MyApp.UnregisteredType'.Register it in your JsonSerializerContext or pass a JsonTypeInfo<UnregisteredType> explicitly.This tells you exactly which type is missing and what to do about it. Every type must either be registered in your JsonSerializerContext via [JsonSerializable(typeof(T))] or passed with an explicit JsonTypeInfo<T> parameter.