#Transaction API
The Transaction API provides ACID-compliant transactions for consistent data operations.
#Creating Transactions
#Read-Only Transaction
using (var txn = db.BeginTransaction(readOnly: true))
{
// Snapshot isolation - sees consistent view
var products = txn.Query("collection('products')//product");
var orders = txn.Query("collection('orders')//order");
// Process results
}
// Automatically completes on dispose#Read-Write Transaction
using (var txn = db.BeginTransaction())
{
var container = txn.GetContainer("products");
container.PutDocument("p1.xml", xml1);
container.PutDocument("p2.xml", xml2);
container.DeleteDocument("p3.xml");
txn.Commit(); // Commit all changes
}
// If Commit() not called, changes are rolled back#With Options
var options = new TransactionOptions
{
ReadOnly = false,
Timeout = TimeSpan.FromSeconds(30),
IsolationLevel = IsolationLevel.Snapshot
};
using (var txn = db.BeginTransaction(options))
{
// Operations...
txn.Commit();
}#TransactionOptions
|
Property |
Type |
Default |
Description |
|---|---|---|---|
|
|
|
|
Read-only transaction |
|
|
|
60s |
Maximum duration |
|
|
|
|
Isolation level |
#Transaction Operations
#ITransaction Interface
interface ITransaction : IDisposable
{
bool IsReadOnly { get; }
TransactionState State { get; }
IContainer GetContainer(string name);
IQueryResult Query(string xquery);
IQueryResult Query(string xquery, QueryParameters parameters);
T QuerySingle<T>(string xquery);
void Execute(string xqueryUpdate);
void Commit();
void Abort();
}#Get Container
using (var txn = db.BeginTransaction())
{
var products = txn.GetContainer("products");
var orders = txn.GetContainer("orders");
// Operations on containers are part of transaction
}#Query in Transaction
using (var txn = db.BeginTransaction(readOnly: true))
{
var result = txn.Query("""
for $p in collection('products')//product
where $p/price > $minPrice
return $p
""",
new QueryParameters { ["minPrice"] = 100 });
foreach (var product in result)
{
Console.WriteLine(product);
}
}#Update in Transaction
using (var txn = db.BeginTransaction())
{
txn.Execute("""
for $p in collection('products')//product
where $p/category = 'discontinued'
return delete node $p
""");
txn.Commit();
}#Commit and Rollback
#Explicit Commit
using (var txn = db.BeginTransaction())
{
// Make changes
txn.GetContainer("data").PutDocument("doc.xml", content);
// Explicitly commit
txn.Commit();
// Transaction state is now Committed
Console.WriteLine(txn.State); // Committed
}#Explicit Rollback
using (var txn = db.BeginTransaction())
{
txn.GetContainer("data").PutDocument("doc.xml", content);
// Something went wrong, rollback
txn.Abort();
// Transaction state is now Aborted
Console.WriteLine(txn.State); // Aborted
}#Automatic Rollback
using (var txn = db.BeginTransaction())
{
txn.GetContainer("data").PutDocument("doc.xml", content);
// No Commit() called
}
// Changes automatically rolled back on dispose#Transaction States
enum TransactionState
{
Active, // Transaction in progress
Committed, // Successfully committed
Aborted, // Rolled back
Disposed // Disposed (implies aborted if not committed)
}#Error Handling
try
{
using (var txn = db.BeginTransaction())
{
// Operations...
txn.Commit();
}
}
catch (TransactionConflictException ex)
{
// Another transaction modified the same data
Console.WriteLine($"Conflict: {ex.Message}");
}
catch (TransactionTimeoutException ex)
{
// Transaction exceeded timeout
Console.WriteLine($"Timeout: {ex.Message}");
}
catch (TransactionAbortedException ex)
{
// Transaction was aborted
Console.WriteLine($"Aborted: {ex.Message}");
}
catch (InvalidOperationException ex)
{
// e.g., Write operation in read-only transaction
Console.WriteLine($"Invalid operation: {ex.Message}");
}#Patterns
#Unit of Work
public void TransferFunds(string from, string to, decimal amount)
{
using var txn = db.BeginTransaction();
var accounts = txn.GetContainer("accounts");
// Check balance
var fromBalance = txn.QuerySingle<decimal>(
$"doc('accounts/{from}.xml')/account/balance");
if (fromBalance < amount)
throw new InsufficientFundsException();
// Update balances
txn.Execute($"""
replace value of node doc('accounts/{from}.xml')/account/balance
with {fromBalance - amount}
""");
var toBalance = txn.QuerySingle<decimal>(
$"doc('accounts/{to}.xml')/account/balance");
txn.Execute($"""
replace value of node doc('accounts/{to}.xml')/account/balance
with {toBalance + amount}
""");
txn.Commit();
}#Retry on Conflict
public T ExecuteWithRetry<T>(Func<ITransaction, T> operation, int maxRetries = 3)
{
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
try
{
using var txn = db.BeginTransaction();
var result = operation(txn);
txn.Commit();
return result;
}
catch (TransactionConflictException) when (attempt < maxRetries)
{
Thread.Sleep(100 * attempt); // Exponential backoff
}
}
throw new TransactionFailedException("Max retries exceeded");
}
// Usage
var result = ExecuteWithRetry(txn =>
{
// Operations that might conflict
return txn.QuerySingle<int>("count(//product)");
});#Optimistic Locking
public void UpdateWithVersion(string docName, string newContent)
{
using var txn = db.BeginTransaction();
// Get current version
var currentVersion = txn.QuerySingle<int>(
$"doc('data/{docName}')/document/@version");
// Update with version check
txn.Execute($"""
let $doc := doc('data/{docName}')/document
return
if ($doc/@version = {currentVersion}) then (
replace node $doc with {newContent},
replace value of node $doc/@version with {currentVersion + 1}
) else
error(xs:QName('local:conflict'), 'Version conflict')
""");
txn.Commit();
}#Nested Operations
using (var txn = db.BeginTransaction())
{
ProcessOrders(txn);
UpdateInventory(txn);
SendNotifications(txn);
txn.Commit(); // All or nothing
}
void ProcessOrders(ITransaction txn)
{
var orders = txn.GetContainer("orders");
// Process orders within transaction context
}#Best Practices
-
Keep transactions short - Minimize time between begin and commit
-
Use read-only when possible - Better concurrency
-
Don't hold transactions - Across async/await or user input
-
Handle conflicts - Implement retry logic
-
Always dispose - Use
usingstatements -
Batch related operations - Single transaction for related changes
#Next Steps
|
Concepts |
Execution |
Configuration |
|---|---|---|
|
TransactionsTransaction concepts |
Query APIQuery execution |
ConfigurationTransaction settings |