Skip to content

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
});

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
});

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 configured
var user = new User { Name = "Alice", Age = 25 };
await store.Set(user); // user.Id is auto-generated
var 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);

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.