CRUD Operations
Store a document
Section titled “Store a document”// Auto-generated GUID key — returns the IDvar id = await store.Set(new User { Name = "Alice", Age = 25 }, ctx.User);
// Explicit keyawait store.Set("user-1", new User { Name = "Alice", Age = 25 }, ctx.User);Upsert with JSON Merge Patch
Section titled “Upsert with JSON Merge Patch”Upsert uses SQLite’s json_patch() (RFC 7396 JSON Merge Patch) to deep-merge a partial patch into an existing document. If the document doesn’t exist, it is inserted as-is. Unlike Set, which replaces the entire document, Upsert only overwrites the fields present in the patch.
// Insert a full documentawait store.Set("user-1", new User { Name = "Alice", Age = 25, Email = "alice@test.com" }, ctx.User);
// Merge patch — only update Name and Age, preserve Emailawait store.Upsert("user-1", new User { Name = "Alice", Age = 30 }, ctx.User);
var user = await store.Get<User>("user-1", ctx.User);// user.Name == "Alice", user.Age == 30, user.Email == "alice@test.com" (preserved)How it works:
- On insert (new ID): the patch is stored as the full document.
- On conflict (existing ID):
json_patch(existing, patch)deep-merges the patch into the stored JSON. Objects are recursively merged; scalars and arrays are replaced. - Null properties are excluded from the patch automatically. In C#, unset nullable properties (e.g.
string? Email) serialize asnull, which would remove the key under RFC 7396. The library strips these so that unset fields are preserved rather than deleted.
Update a single property (SetProperty)
Section titled “Update a single property (SetProperty)”SetProperty updates a single scalar field in-place using SQLite’s json_set() — no deserialization, no full document replacement. Returns true if the document was found and updated, false if not found.
// Update a scalar fieldawait store.SetProperty<User>("user-1", u => u.Age, 31, ctx.User);
// Update a string fieldawait store.SetProperty<User>("user-1", u => u.Email, "newemail@test.com", ctx.User);
// Set a field to nullawait store.SetProperty<User>("user-1", u => u.Email, null, ctx.User);
// Nested propertyawait store.SetProperty<Order>("order-1", o => o.ShippingAddress.City, "Portland", ctx.Order);
// Check if document existedbool updated = await store.SetProperty<User>("user-1", u => u.Age, 31, ctx.User);How it works: The expression u => u.Age is resolved to the JSON path $.age (respecting [JsonPropertyName] attributes and naming policies). The SQL executed is:
UPDATE documentsSET Data = json_set(Data, '$.age', json('31')), UpdatedAt = @nowWHERE Id = @id AND TypeName = @typeName;Supported value types: string, int, long, double, float, decimal, bool, and null. To replace a collection or nested object, use Set (full replacement) or Upsert (merge patch).
Remove a single property (RemoveProperty)
Section titled “Remove a single property (RemoveProperty)”RemoveProperty strips a field from the stored JSON using SQLite’s json_remove(). Returns true if the document was found and updated, false if not found. The removed field will have its C# default value on next read.
// Remove a nullable fieldawait store.RemoveProperty<User>("user-1", u => u.Email, ctx.User);
// Remove a nested propertyawait store.RemoveProperty<Order>("order-1", o => o.ShippingAddress.City, ctx.Order);
// Remove a collection property (removes the entire array)await store.RemoveProperty<Order>("order-1", o => o.Tags, ctx.Order);Unlike SetProperty, RemoveProperty works on any property type — scalar, nested object, or collection — because it simply removes the key from the JSON.
Choosing an update strategy
Section titled “Choosing an update strategy”| Operation | Use when | Scope | Collections |
|---|---|---|---|
SetProperty | Changing one scalar field | Single field via json_set | Scalar values only |
RemoveProperty | Stripping a field from the document | Single field via json_remove | Any property type |
Upsert | Patching multiple fields at once | Deep merge via json_patch | Replaces arrays (RFC 7396) |
Set | Replacing the entire document | Full replacement | Full control |
Get a document
Section titled “Get a document”var user = await store.Get<User>("user-1", ctx.User);Get all documents of a type
Section titled “Get all documents of a type”var users = await store.GetAll<User>(ctx.User);Remove a document
Section titled “Remove a document”// By IDbool deleted = await store.Remove<User>("user-1");
// By expression — returns number deletedint deleted = await store.Remove<User>(u => u.Age < 18, ctx.User);Clear all documents of a type
Section titled “Clear all documents of a type”int deletedCount = await store.Clear<User>();