Database Options Reference
Summary
This section provides a comprehensive reference for the DatabaseOptions class, which controls the behavior of database operations.
DatabaseOptions Class
DatabaseOptions controls how database operations (Create, Edit, Upsert, Delete) are executed.
Class Overview:
public class DatabaseOptions
{
public bool IsApiMode { get; set; }
public bool SystemMode { get; set; }
public bool AllOrNone { get; set; }
public bool TriggerUserEmail { get; set; }
public bool PermanentDelete { get; set; }
public string ExternalIdField { get; set; }
}
DatabaseOptions Properties
IsApiMode
Controls error handling behavior for database operations.
| Property | Type | Default | Description |
|---|
IsApiMode | bool | false | When true, returns errors in result objects; when false, throws exceptions |
Usage:
// IsApiMode = true (recommended)
var result = Database.Create(account, new DatabaseOptions { IsApiMode = true });
if (result.HasError)
{
// Handle errors from result
foreach (var error in result.Errors)
{
SystemInfo.Debug($"{error.PropertyName}: {error.Message}");
}
}
// IsApiMode = false (default - throws exceptions)
try
{
var result = Database.Create(account);
}
catch (Exception ex)
{
// Handle exception
SystemInfo.Debug($"Error: {ex.Message}");
}
When to Use:
- ✅ Always use
IsApiMode = true for better error handling - ✅ Recommended for all database operations
- ✅ Required for bulk operations where some records may fail
- ❌ Avoid default behavior (exceptions) unless specifically needed
SystemMode
Executes database operations with system administrator privileges, bypassing user permissions and sharing rules.
| Property | Type | Default | Description |
|---|
SystemMode | bool | false | When true, bypasses all user permissions and sharing rules |
Usage:
// Execute with system privileges
var result = Database.Create(account, new DatabaseOptions
{
SystemMode = true,
IsApiMode = true
});
// Execute with user permissions (default)
var result = Database.Create(account, new DatabaseOptions { IsApiMode = true });
Behavior:
When to Use:
- ✅ Automated system processes
- ✅ System integrations
- ✅ Administrative operations requiring full access
- ✅ Background jobs and scheduled tasks
- ❌ Never use for user-facing operations
- ❌ Never use without explicit business justification
⚠ Warning: Always document why SystemMode = true is required. Misuse can create security vulnerabilities.
Examples:
// Automated cleanup process
public void AutomatedCleanupProcess()
{
var oldRecords = Database.Query<Account>()
.Where(f => f.LastActivityDate__c < DateTime.UtcNow.AddYears(-5))
.ToListAsAdmin();
// Delete with system privileges
Database.Delete(oldRecords, new DatabaseOptions
{
SystemMode = true,
IsApiMode = true
});
}
// System integration
public void SyncFromExternalSystem(List<ExternalAccount> externalAccounts)
{
var accounts = ConvertToAccounts(externalAccounts);
// Upsert with system privileges
var results = Database.Upsert(accounts, new DatabaseOptions
{
SystemMode = true,
ExternalIdField = "ExternalId__c",
IsApiMode = true
});
}
AllOrNone
Controls transactional behavior for bulk operations.
| Property | Type | Default | Description |
|---|
AllOrNone | bool | false | When true, all records must succeed or all fail together |
Usage:
// Transactional mode - all succeed or all fail
var results = Database.Create(accounts, new DatabaseOptions
{
AllOrNone = true,
IsApiMode = true
});
if (results.Any(r => r.HasError))
{
SystemInfo.Debug("Transaction failed - no records were created");
}
else
{
SystemInfo.Debug("All records created successfully");
}
// Default mode - partial success allowed
var results = Database.Create(accounts, new DatabaseOptions { IsApiMode = true });
var successCount = results.Count(r => !r.HasError);
var errorCount = results.Count(r => r.HasError);
SystemInfo.Debug($"Created {successCount}, failed {errorCount}");
Behavior:
When to Use:
- ✅ Related records that must be created together
- ✅ Financial transactions requiring consistency
- ✅ Data integrity requirements
- ✅ When partial success is unacceptable
- ❌ Large batch operations (may fail entire batch)
- ❌ When partial success is acceptable
Examples:
// Order with line items - must all succeed
public SaveResult CreateOrderWithItems(Order order, List<OrderItem> items)
{
// Create order
var orderResult = Database.Create(order, new DatabaseOptions
{
AllOrNone = true,
IsApiMode = true
});
if (orderResult.HasError)
{
return orderResult;
}
// Link items to order
foreach (var item in items)
{
item.OrderId = orderResult.Id;
}
// Create items - all must succeed
var itemResults = Database.Create(items, new DatabaseOptions
{
AllOrNone = true,
IsApiMode = true
});
if (itemResults.Any(r => r.HasError))
{
// Rollback order if items fail
Database.Delete(orderResult.Id, new DatabaseOptions { IsApiMode = true });
SystemInfo.Debug("Order creation failed - all changes rolled back");
return itemResults.First(r => r.HasError);
}
return orderResult;
}
TriggerUserEmail
Controls whether automated emails are sent to users during user record operations.
| Property | Type | Default | Description |
|---|
TriggerUserEmail | bool | true | When true, sends automated emails; when false, suppresses emails |
Usage:
// Create user without sending welcome email
var user = new User
{
Email = "newuser@example.com",
FirstName = "Jane",
LastName = "Doe"
};
var result = Database.Create(user, new DatabaseOptions
{
TriggerUserEmail = false,
IsApiMode = true
});
// Create user with welcome email (default)
var result = Database.Create(user, new DatabaseOptions { IsApiMode = true });
Affected Operations:
- User creation (welcome/activation emails)
- User updates (notification emails)
- Password resets
When to Use:
- ✅ Bulk user imports
- ✅ User provisioning from external systems
- ✅ Testing environments
- ✅ User record updates that don't require notification
- ❌ Single user creation when email is needed
- ❌ Password reset operations requiring notification
Examples:
// Bulk user import without emails
public void ImportUsers(List<ExternalUser> externalUsers)
{
var users = new List<User>();
foreach (var ext in externalUsers)
{
users.Add(new User
{
Email = ext.Email,
FirstName = ext.FirstName,
LastName = ext.LastName
});
}
// Create users without sending emails
var results = Database.Create(users, new DatabaseOptions
{
TriggerUserEmail = false,
IsApiMode = true
});
SystemInfo.Debug($"Imported {results.Count(r => !r.HasError)} users");
}
PermanentDelete
Controls whether records are permanently deleted or moved to recycle bin.
| Property | Type | Default | Description |
|---|
PermanentDelete | bool | false | When true, permanently deletes records; when false, soft deletes to recycle bin |
Usage:
// Soft delete (default) - moves to recycle bin
var result = Database.Delete(accountId, new DatabaseOptions { IsApiMode = true });
// Permanent delete - removes completely
var result = Database.Delete(accountId, new DatabaseOptions
{
PermanentDelete = true,
IsApiMode = true
});
Behavior:
When to Use:
- ✅ Removing test data from production
- ✅ Cleaning up duplicate records
- ✅ Data retention policy compliance
- ✅ Purging old archived data
- ❌ Standard record deletion
- ❌ Records that may need restoration
- ❌ Unless specifically required
⚠ Warning: Permanent delete cannot be undone. Always use with extreme caution.
Examples:
// Remove test data permanently
public void CleanupTestData()
{
var testRecords = Database.Query<Account>()
.Where(f => f.Type == "Test" && f.CreatedOn < DateTime.UtcNow.AddDays(-90))
.ToList();
if (testRecords.Count > 0)
{
SystemInfo.Debug($"WARNING: Permanently deleting {testRecords.Count} test records");
var results = Database.Delete(testRecords, new DatabaseOptions
{
PermanentDelete = true,
IsApiMode = true
});
}
}
ExternalIdField
Specifies the field to use for matching records in upsert operations.
| Property | Type | Default | Description |
|---|
ExternalIdField | string | null | Field name to use for matching existing records during upsert |
Usage:
// Upsert by external ID field
var account = new Account
{
ExternalId__c = "EXT-12345",
Name = "Partner Company",
Type = "Partner"
};
var result = Database.Upsert(account, new DatabaseOptions
{
ExternalIdField = "ExternalId__c",
IsApiMode = true
});
// Upsert by email
var contact = new Contact
{
Email = "john@example.com",
FirstName = "John",
LastName = "Smith"
};
var result = Database.Upsert(contact, new DatabaseOptions
{
ExternalIdField = "Email",
IsApiMode = true
});
Behavior:
- If record with matching external ID value exists, it updates
- If no match found, it creates new record
- External ID field must be unique
- External ID field must be indexed for performance
Requirements:
- Field must exist on the entity
- Field should be marked as unique
- Field should be indexed
- Field value must not be null
When to Use:
- ✅ Data synchronization from external systems
- ✅ Import operations with external identifiers
- ✅ API integrations
- ✅ When record existence is unknown
- ❌ When using record ID for matching
- ❌ When operation type (create/update) is known
Common External ID Fields:
ExternalId__c - Generic external system IDEmail - Email address (Contacts, Users)ProductCode - Product SKUOrderNumber__c - Order numberInvoiceNumber__c - Invoice number
Examples:
// Sync products from external catalog
public void SyncProducts(List<ExternalProduct> externalProducts)
{
var products = new List<Product>();
foreach (var ext in externalProducts)
{
products.Add(new Product
{
ProductCode = ext.SKU, // External ID
Name = ext.Name,
Price__c = ext.Price
});
}
var results = Database.Upsert(products, new DatabaseOptions
{
ExternalIdField = "ProductCode",
IsApiMode = true
});
var inserted = results.Count(r => !r.HasError && r.IsInsert);
var updated = results.Count(r => !r.HasError && !r.IsInsert);
SystemInfo.Debug($"Synced products: {inserted} new, {updated} updated");
}
Combined Options Examples
Complete Create with All Options:
var results = Database.Create(accounts, new DatabaseOptions
{
IsApiMode = true, // Return errors in results
SystemMode = false, // Use user permissions
AllOrNone = true, // All succeed or all fail
TriggerUserEmail = true // Send automated emails
});
Complete Upsert with All Options:
var results = Database.Upsert(contacts, new DatabaseOptions
{
ExternalIdField = "Email", // Match on email
IsApiMode = true, // Return errors in results
SystemMode = false, // Use user permissions
AllOrNone = false // Allow partial success
});
Complete Delete with All Options:
var results = Database.Delete(accounts, new DatabaseOptions
{
IsApiMode = true, // Return errors in results
SystemMode = false, // Use user permissions
AllOrNone = true, // All succeed or all fail
PermanentDelete = false // Soft delete to recycle bin
});
Best Practices for Database Options
✅ Always use IsApiMode = true
var result = Database.Create(account, new DatabaseOptions { IsApiMode = true });
✅ Document SystemMode usage
// JUSTIFICATION: Automated nightly cleanup process requires system access
var results = Database.Delete(oldRecords, new DatabaseOptions
{
SystemMode = true,
IsApiMode = true
});
✅ Use AllOrNone for related records
// Create order and items together
var results = Database.Create(orderItems, new DatabaseOptions
{
AllOrNone = true,
IsApiMode = true
});
✅ Soft delete by default
// Allow recovery
var result = Database.Delete(accountId, new DatabaseOptions { IsApiMode = true });
❌ Avoid SystemMode without justification
// ❌ BAD - No justification
var result = Database.Create(account, new DatabaseOptions { SystemMode = true });
❌ Avoid PermanentDelete unless required
// ❌ BAD - Unnecessary permanent delete
var result = Database.Delete(accountId, new DatabaseOptions
{
PermanentDelete = true,
IsApiMode = true
});