Database
AppAmbit provides a database accessible directly through the SDK and the AppAmbit Dashboard. Use it to store and query structured data within your app, with operations running on a background thread and results delivered on the main thread.
Executing Raw SQL
Use AppAmbitDb.execute() for full SQL control — complex queries, multi-row updates, or anything the query builder doesn't cover.
Basic Execution
var result = await AppAmbitDb.Execute(
"SELECT * FROM products WHERE active = 1"
);
AppAmbitDb.execute("SELECT * FROM products WHERE active = 1") { result, error in
if let error = error {
print("Error: \(error)")
}
}
[AppAmbitDb execute:@"SELECT * FROM products WHERE active = 1"
completion:^(DbResult *result, NSError *error) {
if (error) NSLog(@"Error: %@", error);
}];
AppAmbitDb.execute("SELECT * FROM products WHERE active = 1")
.then(result -> {
// success — called on the main thread
})
.onError(error -> {
// error — called on the main thread
});
AppAmbitDb.execute("SELECT * FROM products WHERE active = 1")
.then { result ->
// success — called on the main thread
}
.onError { error ->
// error — called on the main thread
}
final result = await AppAmbitDb.execute(
"SELECT * FROM products WHERE active = 1"
);
const result = await Database.execute(
"SELECT * FROM products WHERE active = 1"
);
var result = await AppAmbitDb.Execute(
"SELECT * FROM products WHERE active = 1"
);
var result = await AppAmbitDb.Execute(
"SELECT * FROM products WHERE active = 1"
);
With Parameters
Use ? placeholders to pass parameters safely — this prevents SQL injection and handles type encoding automatically.
var result = await AppAmbitDb.Execute(
"INSERT INTO products (name, price) VALUES (?, ?)",
"Widget", 9.99
);
AppAmbitDb.execute(
"INSERT INTO products (name, price) VALUES (?, ?)",
params: ["Widget", 9.99]
) { result, error in
// handle result
}
[AppAmbitDb execute:@"INSERT INTO products (name, price) VALUES (?, ?)"
params:@[@"Widget", @9.99]
completion:^(DbResult *result, NSError *error) {
// handle result
}];
AppAmbitDb.execute("INSERT INTO products (name, price) VALUES (?, ?)", "Widget", 9.99)
.then(result -> {
// success
});
AppAmbitDb.execute("INSERT INTO products (name, price) VALUES (?, ?)", "Widget", 9.99)
.then { result ->
// success
}
final result = await AppAmbitDb.execute(
"INSERT INTO products (name, price) VALUES (?, ?)",
["Widget", 9.99]
);
const result = await Database.execute(
"INSERT INTO products (name, price) VALUES (?, ?)",
["Widget", 9.99]
);
var result = await AppAmbitDb.Execute(
"INSERT INTO products (name, price) VALUES (?, ?)",
"Widget", 9.99
);
var result = await AppAmbitDb.Execute(
"INSERT INTO products (name, price) VALUES (?, ?)",
"Widget", 9.99
);
Multi-row Updates
execute() is useful for updates that affect more rows than the query builder's single-row-focused update() is meant for:
var result = await AppAmbitDb.Execute(
"UPDATE products SET active = 0 WHERE category = ?",
"discontinued"
);
AppAmbitDb.execute(
"UPDATE products SET active = 0 WHERE category = ?",
params: ["discontinued"]
) { result, error in
// handle result
}
[AppAmbitDb execute:@"UPDATE products SET active = 0 WHERE category = ?"
params:@[@"discontinued"]
completion:^(DbResult *result, NSError *error) {
// handle result
}];
AppAmbitDb.execute("UPDATE products SET active = 0 WHERE category = ?", "discontinued")
.then(result -> {
// result.getRowsWritten()
});
AppAmbitDb.execute("UPDATE products SET active = 0 WHERE category = ?", "discontinued")
.then { result ->
// result.rowsWritten
}
final result = await AppAmbitDb.execute(
"UPDATE products SET active = 0 WHERE category = ?",
["discontinued"]
);
const result = await Database.execute(
"UPDATE products SET active = 0 WHERE category = ?",
["discontinued"]
);
var result = await AppAmbitDb.Execute(
"UPDATE products SET active = 0 WHERE category = ?",
"discontinued"
);
var result = await AppAmbitDb.Execute(
"UPDATE products SET active = 0 WHERE category = ?",
"discontinued"
);
Batch Operations
Execute multiple SQL statements in a single round trip. Use batchInTransaction when the statements must all succeed together — any failure rolls back all changes.
Batch
var results = await AppAmbitDb.Batch(
new DbStatement("INSERT INTO products (name, price) VALUES (?, ?)", "Widget", 9.99),
new DbStatement("INSERT INTO products (name, price) VALUES (?, ?)", "Gadget", 24.99)
);
let statements = [
DbStatement(sql: "INSERT INTO products (name, price) VALUES (?, ?)", params: ["Widget", 9.99]),
DbStatement(sql: "INSERT INTO products (name, price) VALUES (?, ?)", params: ["Gadget", 24.99])
]
AppAmbitDb.batch(statements) { results, error in
// handle results
}
NSArray *statements = @[
[[DbStatement alloc] initWithSql:@"INSERT INTO products (name, price) VALUES (?, ?)"
params:@[@"Widget", @9.99]],
[[DbStatement alloc] initWithSql:@"INSERT INTO products (name, price) VALUES (?, ?)"
params:@[@"Gadget", @24.99]]
];
[AppAmbitDb batch:statements completion:^(NSArray *results, NSError *error) {
// handle results
}];
AppAmbitDb.batch(
new DbStatement("INSERT INTO products (name, price) VALUES (?, ?)", Arrays.asList("Widget", 9.99)),
new DbStatement("INSERT INTO products (name, price) VALUES (?, ?)", Arrays.asList("Gadget", 24.99))
).then(results -> {
// success
});
AppAmbitDb.batch(
DbStatement("INSERT INTO products (name, price) VALUES (?, ?)", listOf("Widget", 9.99)),
DbStatement("INSERT INTO products (name, price) VALUES (?, ?)", listOf("Gadget", 24.99))
).then { results ->
// success
}
final results = await AppAmbitDb.batch([
DbStatement.of("INSERT INTO products (name, price) VALUES (?, ?)", ["Widget", 9.99]),
DbStatement.of("INSERT INTO products (name, price) VALUES (?, ?)", ["Gadget", 24.99]),
]);
const results = await Database.batch([
{ sql: "INSERT INTO products (name, price) VALUES (?, ?)", params: ["Widget", 9.99] },
{ sql: "INSERT INTO products (name, price) VALUES (?, ?)", params: ["Gadget", 24.99] }
]);
var results = await AppAmbitDb.Batch(
new DbStatement("INSERT INTO products (name, price) VALUES (?, ?)", "Widget", 9.99),
new DbStatement("INSERT INTO products (name, price) VALUES (?, ?)", "Gadget", 24.99)
);
var results = await AppAmbitDb.Batch(
new DbStatement("INSERT INTO products (name, price) VALUES (?, ?)", "Widget", 9.99),
new DbStatement("INSERT INTO products (name, price) VALUES (?, ?)", "Gadget", 24.99)
);
Batch in Transaction
All statements run inside a single transaction. If any fails, all changes are rolled back:
var results = await AppAmbitDb.BatchInTransaction(
new DbStatement("UPDATE accounts SET balance = balance - ? WHERE id = ?", 100.0, 1),
new DbStatement("UPDATE accounts SET balance = balance + ? WHERE id = ?", 100.0, 2)
);
let statements = [
DbStatement(sql: "UPDATE accounts SET balance = balance - ? WHERE id = ?", params: [100.0, 1]),
DbStatement(sql: "UPDATE accounts SET balance = balance + ? WHERE id = ?", params: [100.0, 2])
]
AppAmbitDb.batchInTransaction(statements) { results, error in
// handle results
}
NSArray *statements = @[
[[DbStatement alloc] initWithSql:@"UPDATE accounts SET balance = balance - ? WHERE id = ?"
params:@[@100.0, @1]],
[[DbStatement alloc] initWithSql:@"UPDATE accounts SET balance = balance + ? WHERE id = ?"
params:@[@100.0, @2]]
];
[AppAmbitDb batchInTransaction:statements completion:^(NSArray *results, NSError *error) {
// handle results
}];
AppAmbitDb.batchInTransaction(
new DbStatement("UPDATE accounts SET balance = balance - ? WHERE id = ?", Arrays.asList(100.0, 1)),
new DbStatement("UPDATE accounts SET balance = balance + ? WHERE id = ?", Arrays.asList(100.0, 2))
).then(results -> {
// success — all statements committed
});
AppAmbitDb.batchInTransaction(
DbStatement("UPDATE accounts SET balance = balance - ? WHERE id = ?", listOf(100.0, 1)),
DbStatement("UPDATE accounts SET balance = balance + ? WHERE id = ?", listOf(100.0, 2))
).then { results ->
// success — all statements committed
}
final results = await AppAmbitDb.batchInTransaction([
DbStatement.of("UPDATE accounts SET balance = balance - ? WHERE id = ?", [100.0, 1]),
DbStatement.of("UPDATE accounts SET balance = balance + ? WHERE id = ?", [100.0, 2]),
]);
const results = await Database.batchInTransaction([
{ sql: "UPDATE accounts SET balance = balance - ? WHERE id = ?", params: [100.0, 1] },
{ sql: "UPDATE accounts SET balance = balance + ? WHERE id = ?", params: [100.0, 2] }
]);
var results = await AppAmbitDb.BatchInTransaction(
new DbStatement("UPDATE accounts SET balance = balance - ? WHERE id = ?", 100.0, 1),
new DbStatement("UPDATE accounts SET balance = balance + ? WHERE id = ?", 100.0, 2)
);
var results = await AppAmbitDb.BatchInTransaction(
new DbStatement("UPDATE accounts SET balance = balance - ? WHERE id = ?", 100.0, 1),
new DbStatement("UPDATE accounts SET balance = balance + ? WHERE id = ?", 100.0, 2)
);
Query Builder
The query builder provides a fluent API for common operations without writing SQL. Start with AppAmbitDb.from(tableName) and chain methods to build your query.
Note
Each builder instance is single-use. Create a new one via from() for each operation.
Fetching Records
Get All
Returns all rows matching the current filters:
// Map-based (Dictionary<string, object?>)
var rows = await AppAmbitDb.From("products")
.Where("active", true)
.OrderBy("name")
.Get();
// Typed (requires a class with a parameterless constructor)
var products = await AppAmbitDb.From<Product>("products")
.Where("active", true)
.OrderBy("name")
.Get();
// Map-based
AppAmbitDb.from("products")
.where("active", value: true)
.orderBy("name")
.get { rows, error in
// rows: [[String: Any]]?
}
// Typed (Swift only — T must be Decodable)
AppAmbitDb.from("products", as: Product.self)
.where("active", value: true)
.orderBy("name")
.get { products, error in
// products: [Product]?
}
[[[AppAmbitDb from:@"products"] where:@"active" value:@YES] orderBy:@"name"]
getWithCompletion:^(NSArray<NSDictionary *> *rows, NSError *error) {
// handle rows
}];
// Map-based
AppAmbitDb.from("products")
.where("active", true)
.orderBy("name")
.get()
.then(rows -> {
// rows: List<Map<String, Object>>
});
// Typed
AppAmbitDb.from("products", Product.class)
.where("active", true)
.orderBy("name")
.get()
.then(products -> {
// products: List<Product>
});
// Map-based
AppAmbitDb.from("products")
.where("active", true)
.orderBy("name")
.get()
.then { rows ->
// rows: List<Map<String, Any>>
}
// Typed
AppAmbitDb.from("products", Product::class.java)
.where("active", true)
.orderBy("name")
.get()
.then { products ->
// products: List<Product>
}
// Map-based
final rows = await AppAmbitDb.from("products")
.where("active", true)
.orderBy("name")
.get();
// Typed (provide a fromRow mapper)
final products = await AppAmbitDb.fromMapped<Product>(
"products",
fromRow: Product.fromMap,
)
.where("active", true)
.orderBy("name")
.get();
const rows = await Database.from("products")
.where("active", true)
.orderBy("name")
.get();
// Map-based
var rows = await AppAmbitDb.From("products")
.Where("active", true)
.OrderBy("name")
.Get();
// Typed
var products = await AppAmbitDb.From<Product>("products")
.Where("active", true)
.OrderBy("name")
.Get();
var products = await AppAmbitDb.From<Product>("products")
.Where("active", true)
.OrderBy("name")
.Get();
Get First
Returns the first matching row, or null / nil if none found:
var product = await AppAmbitDb.From<Product>("products")
.Where("id", 42)
.First();
AppAmbitDb.from("products", as: Product.self)
.where("id", value: 42)
.first { product, error in
// product: Product?
}
[[AppAmbitDb from:@"products"] where:@"id" value:@42]
firstWithCompletion:^(NSDictionary *row, NSError *error) {
// handle row
}];
AppAmbitDb.from("products", Product.class)
.where("id", 42)
.first()
.then(product -> {
// product: Product (null if not found)
});
AppAmbitDb.from("products", Product::class.java)
.where("id", 42)
.first()
.then { product ->
// product: Product? (null if not found)
}
final product = await AppAmbitDb.fromMapped<Product>(
"products",
fromRow: Product.fromMap,
)
.where("id", 42)
.first();
// product is null if not found
const product = await Database.from("products")
.where("id", 42)
.first();
var product = await AppAmbitDb.From<Product>("products")
.Where("id", 42)
.First();
var product = await AppAmbitDb.From<Product>("products")
.Where("id", 42)
.First();
Count
Returns the number of rows matching the current filters:
var total = await AppAmbitDb.From("products")
.Where("active", true)
.Count();
AppAmbitDb.from("products")
.where("active", value: true)
.count { total, error in
// total: Int
}
[[AppAmbitDb from:@"products"] where:@"active" value:@YES]
countWithCompletion:^(NSInteger total, NSError *error) {
// handle total
}];
AppAmbitDb.from("products")
.where("active", true)
.count()
.then(total -> {
// total: Integer
});
AppAmbitDb.from("products")
.where("active", true)
.count()
.then { total ->
// total: Int
}
final total = await AppAmbitDb.from("products")
.where("active", true)
.count();
const total = await Database.from("products")
.where("active", true)
.count();
var total = await AppAmbitDb.From("products")
.Where("active", true)
.Count();
var total = await AppAmbitDb.From("products")
.Where("active", true)
.Count();
Selecting Columns
Restrict which columns are returned:
var rows = await AppAmbitDb.From("products")
.Select("id", "name", "price")
.Get();
AppAmbitDb.from("products")
.select(["id", "name", "price"])
.get { rows, error in }
[[AppAmbitDb from:@"products"] select:@[@"id", @"name", @"price"]]
getWithCompletion:^(NSArray *rows, NSError *error) {}];
AppAmbitDb.from("products")
.select("id", "name", "price")
.get()
.then(rows -> {});
AppAmbitDb.from("products")
.select("id", "name", "price")
.get()
.then { rows -> }
final rows = await AppAmbitDb.from("products")
.select(["id", "name", "price"])
.get();
const rows = await Database.from("products")
.select("id", "name", "price")
.get();
var rows = await AppAmbitDb.From("products")
.Select("id", "name", "price")
.Get();
var rows = await AppAmbitDb.From("products")
.Select("id", "name", "price")
.Get();
Filtering
Where
Filter rows by equality or with a comparison operator. Multiple where() calls are combined with AND:
// Equality
var rows = await AppAmbitDb.From("products")
.Where("category", "electronics")
.Get();
// With operator: =, !=, <>, >, >=, <, <=, LIKE, NOT LIKE, IS, IS NOT
var rows = await AppAmbitDb.From("products")
.Where("price", "<", 50.0)
.Where("active", true)
.Get();
// Equality
AppAmbitDb.from("products")
.where("category", value: "electronics")
.get { rows, error in }
// With operator
AppAmbitDb.from("products")
.where("price", op: "<", value: 50.0)
.where("active", value: true)
.get { rows, error in }
[[[AppAmbitDb from:@"products"] where:@"price" op:@"<" value:@50.0] where:@"active" value:@YES]
getWithCompletion:^(NSArray *rows, NSError *error) {}];
// Equality
AppAmbitDb.from("products")
.where("category", "electronics")
.get().then(rows -> {});
// With operator
AppAmbitDb.from("products")
.where("price", "<", 50.0)
.where("active", true)
.get().then(rows -> {});
AppAmbitDb.from("products")
.where("price", "<", 50.0)
.where("active", true)
.get()
.then { rows -> }
// Equality
final rows = await AppAmbitDb.from("products")
.where("category", "electronics")
.get();
// With operator: =, !=, <>, >, >=, <, <=, LIKE, NOT LIKE, IS, IS NOT
final rows = await AppAmbitDb.from("products")
.whereOp("price", "<", 50.0)
.where("active", true)
.get();
// Equality
const rows = await Database.from("products")
.where("category", "electronics")
.get();
// With operator
const rows = await Database.from("products")
.where("price", "<", 50.0)
.where("active", true)
.get();
var rows = await AppAmbitDb.From("products")
.Where("price", "<", 50.0)
.Where("active", true)
.Get();
var rows = await AppAmbitDb.From("products")
.Where("price", "<", 50.0)
.Where("active", true)
.Get();
Where In
Filter rows where a column's value is in a given list:
var rows = await AppAmbitDb.From("products")
.WhereIn("category", new object[] { "electronics", "books", "toys" })
.Get();
AppAmbitDb.from("products")
.whereIn("category", values: ["electronics", "books", "toys"])
.get { rows, error in }
[[AppAmbitDb from:@"products"] whereIn:@"category" values:@[@"electronics", @"books", @"toys"]]
getWithCompletion:^(NSArray *rows, NSError *error) {}];
AppAmbitDb.from("products")
.whereIn("category", Arrays.asList("electronics", "books", "toys"))
.get().then(rows -> {});
AppAmbitDb.from("products")
.whereIn("category", listOf("electronics", "books", "toys"))
.get()
.then { rows -> }
final rows = await AppAmbitDb.from("products")
.whereIn("category", ["electronics", "books", "toys"])
.get();
const rows = await Database.from("products")
.whereIn("category", ["electronics", "books", "toys"])
.get();
var rows = await AppAmbitDb.From("products")
.WhereIn("category", new object[] { "electronics", "books", "toys" })
.Get();
var rows = await AppAmbitDb.From("products")
.WhereIn("category", new object[] { "electronics", "books", "toys" })
.Get();
Or Conditions and Grouping
Note
orWhere() / OrWhere() are available on iOS and .NET (.NET MAUI, DOTNET, Avalonia). On Android, Flutter, and React Native, where() calls are always combined with AND — use whereIn() or raw SQL via execute() for OR logic.
// OR conditions
var rows = await AppAmbitDb.From("products")
.Where("category", "electronics")
.OrWhere("category", "books")
.Get();
// Grouping: category = ? AND (price < ? OR active = ?)
var rows2 = await AppAmbitDb.From("products")
.Where("category", "electronics")
.WhereGroup(g => g
.Where("price", "<", 50.0)
.OrWhere("active", false))
.Get();
AppAmbitDb.from("products")
.where("category", value: "electronics")
.orWhere("category", value: "books")
.get { rows, error in }
[[[AppAmbitDb from:@"products"] where:@"category" value:@"electronics"] orWhere:@"category" value:@"books"]
getWithCompletion:^(NSArray *rows, NSError *error) {}];
var rows = await AppAmbitDb.From("products")
.Where("category", "electronics")
.OrWhere("category", "books")
.Get();
var rows = await AppAmbitDb.From("products")
.Where("category", "electronics")
.OrWhere("category", "books")
.Get();
Sorting
Sort results by a column, ascending or descending:
// Ascending
var rows = await AppAmbitDb.From("products").OrderBy("name").Get();
// Descending
var rows = await AppAmbitDb.From("products").OrderByDesc("price").Get();
AppAmbitDb.from("products").orderBy("name").get { rows, error in }
AppAmbitDb.from("products").orderByDesc("price").get { rows, error in }
[[AppAmbitDb from:@"products"] orderBy:@"name"] getWithCompletion:^(NSArray *rows, NSError *error) {}];
[[AppAmbitDb from:@"products"] orderByDesc:@"price"] getWithCompletion:^(NSArray *rows, NSError *error) {}];
AppAmbitDb.from("products").orderBy("name").get().then(rows -> {});
AppAmbitDb.from("products").orderByDesc("price").get().then(rows -> {});
AppAmbitDb.from("products").orderBy("name").get().then { rows -> }
AppAmbitDb.from("products").orderByDesc("price").get().then { rows -> }
final rows = await AppAmbitDb.from("products").orderBy("name").get();
final rows = await AppAmbitDb.from("products").orderByDesc("price").get();
const rows = await Database.from("products").orderBy("name").get();
const rows = await Database.from("products").orderByDesc("price").get();
var rows = await AppAmbitDb.From("products").OrderBy("name").Get();
var rows = await AppAmbitDb.From("products").OrderByDesc("price").Get();
var rows = await AppAmbitDb.From("products").OrderBy("name").Get();
var rows = await AppAmbitDb.From("products").OrderByDesc("price").Get();
Pagination
Use limit() and offset() together for paginated results:
int page = 2;
int perPage = 20;
var rows = await AppAmbitDb.From("products")
.OrderBy("name")
.Limit(perPage)
.Offset((page - 1) * perPage)
.Get();
AppAmbitDb.from("products")
.orderBy("name")
.limit(20)
.offset(20)
.get { rows, error in }
[[[[AppAmbitDb from:@"products"] orderBy:@"name"] limit:20] offset:20]
getWithCompletion:^(NSArray *rows, NSError *error) {}];
AppAmbitDb.from("products")
.orderBy("name")
.limit(20)
.offset(20)
.get().then(rows -> {});
AppAmbitDb.from("products")
.orderBy("name")
.limit(20)
.offset(20)
.get()
.then { rows -> }
final rows = await AppAmbitDb.from("products")
.orderBy("name")
.limit(20)
.offset(20)
.get();
const rows = await Database.from("products")
.orderBy("name")
.limit(20)
.offset(20)
.get();
var rows = await AppAmbitDb.From("products")
.OrderBy("name")
.Limit(20)
.Offset(20)
.Get();
var rows = await AppAmbitDb.From("products")
.OrderBy("name")
.Limit(20)
.Offset(20)
.Get();
CRUD Operations
Insert
var result = await AppAmbitDb.From("products")
.Insert(new Dictionary<string, object?> {
{ "name", "Widget" },
{ "price", 9.99 },
{ "active", true }
});
AppAmbitDb.from("products")
.insert(["name": "Widget", "price": 9.99, "active": true]) { result, error in
// result?.rowsWritten
}
[[AppAmbitDb from:@"products"]
insert:@{@"name": @"Widget", @"price": @9.99, @"active": @YES}
completion:^(DbResult *result, NSError *error) {}];
Map<String, Object> data = new HashMap<>();
data.put("name", "Widget");
data.put("price", 9.99);
data.put("active", true);
AppAmbitDb.from("products")
.insert(data)
.then(result -> {
// result.getRowsWritten()
});
AppAmbitDb.from("products")
.insert(mapOf("name" to "Widget", "price" to 9.99, "active" to true))
.then { result ->
// result.rowsWritten
}
final result = await AppAmbitDb.from("products")
.insert({ "name": "Widget", "price": 9.99, "active": true });
// result.rowsWritten
const result = await Database.from("products")
.insert({ name: "Widget", price: 9.99, active: true });
// result.rowsWritten
var result = await AppAmbitDb.From("products")
.Insert(new Dictionary<string, object?> {
{ "name", "Widget" },
{ "price", 9.99 },
{ "active", true }
});
var result = await AppAmbitDb.From("products")
.Insert(new Dictionary<string, object?> {
{ "name", "Widget" },
{ "price", 9.99 },
{ "active", true }
});
Update
Info
update() requires at least one where() condition. Calling it without a filter throws an exception to prevent accidental full-table updates. Use AppAmbitDb.execute() directly for intentional full-table updates.
var result = await AppAmbitDb.From("products")
.Where("id", 42)
.Update(new Dictionary<string, object?> {
{ "price", 14.99 },
{ "active", true }
});
AppAmbitDb.from("products")
.where("id", value: 42)
.update(["price": 14.99, "active": true]) { result, error in }
[[AppAmbitDb from:@"products"] where:@"id" value:@42]
update:@{@"price": @14.99, @"active": @YES}
completion:^(DbResult *result, NSError *error) {}];
Map<String, Object> data = new HashMap<>();
data.put("price", 14.99);
data.put("active", true);
AppAmbitDb.from("products")
.where("id", 42)
.update(data)
.then(result -> {});
AppAmbitDb.from("products")
.where("id", 42)
.update(mapOf("price" to 14.99, "active" to true))
.then { result -> }
final result = await AppAmbitDb.from("products")
.where("id", 42)
.update({ "price": 14.99, "active": true });
const result = await Database.from("products")
.where("id", 42)
.update({ price: 14.99, active: true });
var result = await AppAmbitDb.From("products")
.Where("id", 42)
.Update(new Dictionary<string, object?> {
{ "price", 14.99 },
{ "active", true }
});
var result = await AppAmbitDb.From("products")
.Where("id", 42)
.Update(new Dictionary<string, object?> {
{ "price", 14.99 },
{ "active", true }
});
Delete
Info
delete() requires at least one where() condition. Calling it without a filter throws an exception to prevent accidental full-table deletes. Use AppAmbitDb.execute() directly for intentional full-table deletes.
var result = await AppAmbitDb.From("products")
.Where("id", 42)
.Delete();
AppAmbitDb.from("products")
.where("id", value: 42)
.delete { result, error in }
[[AppAmbitDb from:@"products"] where:@"id" value:@42]
deleteWithCompletion:^(DbResult *result, NSError *error) {}];
AppAmbitDb.from("products")
.where("id", 42)
.delete()
.then(result -> {});
AppAmbitDb.from("products")
.where("id", 42)
.delete()
.then { result -> }
final result = await AppAmbitDb.from("products")
.where("id", 42)
.delete();
const result = await Database.from("products")
.where("id", 42)
.delete();
var result = await AppAmbitDb.From("products")
.Where("id", 42)
.Delete();
var result = await AppAmbitDb.From("products")
.Where("id", 42)
.Delete();
Troubleshooting
Builder already consumed
Platforms: .NET MAUI, DOTNET, Avalonia, Flutter
If you see InvalidOperationException: DbQueryBuilder instance already executed (.NET) or StateError: DbQueryBuilder already executed (Flutter), a terminal method (Get()/get(), First()/first(), Count()/count(), Insert()/insert(), Update()/update(), Delete()/delete()) was called on the same builder instance more than once. Each builder is single-use.
// Wrong — second call throws
var builder = AppAmbitDb.From("products").Where("active", true);
var rows = await builder.Get();
var count = await builder.Count(); // InvalidOperationException
// Right — create a new builder for each operation
var rows = await AppAmbitDb.From("products").Where("active", true).Get();
var count = await AppAmbitDb.From("products").Where("active", true).Count();
Update or Delete without a filter throws
Update() and Delete() require at least one Where() call. Without a filter they would modify every row in the table, so the SDK throws to prevent accidental data loss.
If you intentionally want to update or delete all rows, use raw SQL:
await AppAmbitDb.Execute("DELETE FROM products");
await AppAmbitDb.Execute("UPDATE products SET active = ?", false);
AppAmbitDb.execute("DELETE FROM products") { _, _ in }
[AppAmbitDb execute:@"DELETE FROM products" completion:^(DbResult *r, NSError *e) {}];
AppAmbitDb.execute("DELETE FROM products").then(result -> {});
AppAmbitDb.execute("DELETE FROM products").then { }
await AppAmbitDb.execute("DELETE FROM products");
await Database.execute("DELETE FROM products");
await AppAmbitDb.Execute("DELETE FROM products");
await AppAmbitDb.Execute("DELETE FROM products");
Query returns no results
NULL comparisons: where("column", null) compiles to IS NULL. Do not use a string "null" — pass the actual null value.
Note
On Android, where("column", null) compiles to = ? with a null parameter, which never matches in SQLite. Use where("column", "IS", null) (the 3-argument overload) for IS NULL comparisons on Android.
Column name case: SQLite column names are case-insensitive in SQL, but result dictionaries key by the name as defined in your CREATE TABLE statement. Pass column names to select() and where() exactly as they appear in the schema.
Typed mapping returns empty or missing fields
When using From<T>() (.NET) or from("table", MyClass.class) (Android), property/field names must match database column names exactly. Unmatched columns are silently skipped.
If your column names use snake_case but your class uses PascalCase, either rename the properties to match or use map-based queries (From("table")) and map manually.
Note
On iOS, typed queries use Decodable. Add CodingKeys to your struct if property names differ from column names.
On Flutter, fromMapped<T>() takes an explicit fromRow function — there's no reflection-based mapping, so handle snake_case/camelCase differences directly in that function.