Skip to content

CRUD Operations

Tom Laird-McConnell edited this page May 24, 2026 · 3 revisions

CRUD Operations

POCO Operations

Operation Description
SaveAsync() Save (upsert) a single object
SaveManyAsync() Save (upsert) multiple objects in bulk
ChangeAsync() Read-modify-write with optimistic concurrency
GetAsync() Point-read a single object by key
GetManyAsync() Get multiple items from table storage
DeleteAsync() Delete a single object by key or entity
DeleteManyAsync() Delete by predicate, by entities, or all of a type

JSON Operations

Operation Description
SaveAsync(schemaName, doc) Save a JsonDocument
SaveManyAsync(schemaName, docs) Batch save JsonDocuments
GetAsync(schemaName, key) Point-read a JsonDocument
GetManyAsync(schemaName, filter?) List with optional OData filter
Search(schemaName, query?) Full-text search via Lucene
DeleteAsync(schemaName, key) Delete a single JsonDocument by key
DeleteManyAsync(schemaName, filter?) Delete by OData filter, or all of a type

See Storing JSON for full documentation.

SaveAsync()

Save a POCO object using upsert semantics. If the object has an ETag (from a previous read), performs a conditional write that throws ConcurrencyException on conflict. Otherwise, performs an unconditional upsert. See Object Metadata for details.

await db.SaveAsync(actor);

SaveManyAsync()

Save multiple objects in bulk. Table storage writes are batched transactionally (auto-flushed at 100 ops). On handlers run after each batch commit. Each entity is annotated with its ETag from the batch response.

await db.SaveManyAsync([actor1, actor2, note1]);

ChangeAsync()

Read-modify-write with optimistic concurrency. Fetches the object, applies the mutation, and commits with ETag concurrency. Retries automatically on conflicts.

Parallel ChangeAsync calls on the same key are safely serialized using per-key striped locking. The table storage write and Lucene index update are performed atomically under the lock, so there are no lost updates even under heavy concurrency.

await db.ChangeAsync<Actor>(key, actor => actor.Counter++);

// Safe to call in parallel -- no lost updates
await Task.WhenAll(
    db.ChangeAsync<Actor>(key, a => a.Counter++),
    db.ChangeAsync<Actor>(key, a => a.Counter++),
    db.ChangeAsync<Actor>(key, a => a.Counter++)
);

GetAsync()

Point-read a single object by key from table storage. The returned object is annotated with its key, ETag, and cached JSON via Object Metadata.

var actor = await db.GetAsync<Actor>("alice");
actor.GetKey();   // "alice"
actor.GetETag();  // for conditional writes
actor.GetJson();  // cached JSON snapshot

GetManyAsync()

Query table storage with an optional predicate filter. Filters on [Queryable] properties are executed server-side. Each returned object is annotated with its key and ETag.

// All actors
await foreach (var actor in db.GetManyAsync<Actor>()) { ... }

// Server-side filter
await foreach (var note in db.GetManyAsync<Note>(n => n.AuthorId == "alice")) { ... }

DeleteAsync() POCO

Delete a single object by key or entity.

await db.DeleteAsync<Note>(key);
await db.DeleteAsync<Note>(note);

DeleteManyAsync()

Delete multiple objects. Pass a predicate to delete matching objects, entities to delete specific ones, or no arguments to delete all objects of that type.

// Delete all notes by a specific author
await db.DeleteManyAsync<Note>(n => n.AuthorId == "alice");

// Delete specific entities
await db.DeleteManyAsync<Note>(notesToDelete);

// Delete ALL objects of a type
await db.DeleteManyAsync<Note>();

DeleteManyAsync JSON

Delete multiple JSON documents using JsonExpression predicates.

// Delete matching documents
await db.DeleteManyAsync(je => je["Age"] > 50);

// Delete all documents of a schema type
await db.DeleteManyAsync(je => je.GetSchema() == "Person");

Maintenance

Operation Description
ResetDatabaseAsync() Clear this database's data (other databases unaffected)
DeleteDatabaseAsync() Delete this database and its index permanently
RebuildSearchIndex() Rebuild the Lucene index from Table Storage

Clone this wiki locally