Updating Records (UPDATE Operations)
Summary
This section covers all methods for updating existing records in the database, including single record updates, bulk operations, understanding update behavior, error handling, and update options.
Single Record Update
Use Database.Edit() to update an existing record in the database.
Basic Single Record Update:
// Retrieve record
var account = Database.Retrieve<Account>(accountId);
// Modify fields
account.Name = "Updated Name";
account.Industry = "Technology";
account.AnnualRevenue = 5000000;
// Save changes
Database.Edit(account);
Update Specific Fields:
// Retrieve account
var account = Database.Retrieve<Account>(accountId);
// Update only specific fields
account.Phone = "(555) 123-4567";
account.Website = "https://example.com";
Database.Edit(account);
Update with Validation:
public void UpdateAccountRevenue(string accountId, decimal newRevenue)
{
// Validate input
if (newRevenue < 0)
{
SystemInfo.Debug("Revenue cannot be negative");
return null;
}
// Retrieve and update
var account = Database.Retrieve<Account>(accountId);
if (account == null)
{
SystemInfo.Debug("Account not found");
return null;
}
account.AnnualRevenue = newRevenue;
Database.Edit(account);
}
Conditional Update:
// Update only if condition is met
var account = Database.Retrieve<Account>(accountId);
if (account.Status__c == "Active")
{
account.LastReviewDate__c = DateTime.UtcNow;
account.ReviewedBy__c = User.Current.Id;
Database.Edit(account);
}
else
{
SystemInfo.Debug("Account is not active - no update performed");
}
Update Related Record Reference:
// Change contact's account
var contact = Database.Retrieve<Contact>(contactId);
contact.AccountId = newAccountId;
var result = Database.Edit(contact);
Bulk Update
Always use bulk update methods when modifying multiple records. Never perform database operations in loops.
Basic Bulk Update:
// Query records to update
var accounts = Database.Query<Account>()
.Where(f => f.Type == "Partner" && f.Industry == null)
.ToList();
// Modify records
foreach (var account in accounts)
{
account.Industry = "Technology";
}
// Single bulk update
Database.Edit(accounts);
Bulk Update with Conditions:
// Query accounts needing update
var accounts = Database.Query<Account>()
.Where(f => f.AnnualRevenue > 1000000 && f.Rating__c == null)
.ToList();
// Apply updates
foreach (var account in accounts)
{
account.Rating__c = "High Value";
account.LastReviewDate__c = DateTime.UtcNow;
}
// Bulk update
Database.Edit(accounts);
Bulk Update Pattern:
// ✅ GOOD: Query once, modify in memory, update once
var contacts = Database.Query<Contact>()
.Where(f => f.AccountId == accountId)
.ToList();
foreach (var contact in contacts)
{
contact.MailingCity = "New York";
contact.MailingState = "NY";
}
Database.Edit(contacts);
// ❌ BAD: Query and update in loop
var contactIds = GetContactIds();
foreach (var contactId in contactIds)
{
var contact = Database.Retrieve<Contact>(contactId); // Multiple queries
contact.MailingCity = "New York";
Database.Edit(contact); // Multiple updates
}
Bulk Update with Result Tracking:
var accounts = Database.Query<Account>()
.Where(f => f.Type == "Partner")
.ToList();
// Track which records are being updated
var updateMap = new Dictionary<string, Account>();
foreach (var account in accounts)
{
account.LastContactDate__c = DateTime.UtcNow;
updateMap[account.Id] = account;
}
Database.Edit(accounts);
Large Bulk Update (Batching):
// Update large dataset in batches
public void BulkUpdateAccounts(List<string> accountIds, string newIndustry)
{
int batchSize = 200;
int totalBatches = (int)Math.Ceiling((double)accountIds.Count / batchSize);
for (int i = 0; i < totalBatches; i++)
{
var batchIds = accountIds.Skip(i * batchSize).Take(batchSize).ToList();
// Query batch
var accounts = Database.Query<Account>()
.Where(f => batchIds.Contains(f.Id))
.ToList();
// Update batch
foreach (var account in accounts)
{
account.Industry = newIndustry;
}
// Save batch
Database.Edit(accounts);
}
}
Understanding Update Behavior
Field-Level Updates:
// Only modified fields are updated
var account = Database.Retrieve<Account>(accountId);
// These fields will be updated
account.Name = "New Name";
account.Phone = "(555) 123-4567";
// These fields remain unchanged (not modified)
// account.Industry (unchanged)
// account.AnnualRevenue (unchanged)
Database.Edit(account);
Null vs Empty String:
var account = Database.Retrieve<Account>(accountId);
// Clear field value (set to null)
account.Phone = null;
// Set to empty string
account.Description = "";
Database.Edit(account);
System Fields:
// System fields are automatically managed
var account = Database.Retrieve<Account>(accountId);
// These are set automatically and cannot be manually changed:
// - ModifiedOn (set to current timestamp)
// - ModifiedById (set to current user)
// - SystemModstamp (system timestamp)
account.Name = "Updated Name";
Database.Edit(account);
// After update, system fields are updated
SystemInfo.Debug($"Modified on: {account.ModifiedOn}");
SystemInfo.Debug($"Modified by: {account.ModifiedById}");
Record Must Exist:
// Update requires existing record with valid ID
var account = new Account
{
Id = "INVALID_ID",
Name = "Test"
};
Database.Edit(account);
Concurrency Handling:
// Retrieve record
var account = Database.Retrieve<Account>(accountId);
// Another user may have modified the record
// Update will succeed with last-write-wins behavior
account.Name = "My Update";
Database.Edit(account);
// To check if record was modified since retrieval:
var latestAccount = Database.Retrieve<Account>(accountId);
if (latestAccount.ModifiedOn > account.ModifiedOn)
{
SystemInfo.Debug("Warning: Record was modified by another user");
}
Error Handling
Proper error handling ensures data integrity and provides meaningful feedback.
Exception Mode (default):
try
{
var account = Database.Retrieve<Account>(accountId);
account.Name = ""; // Invalid - required field
var result = Database.Edit(account); // Throws exception
}
catch (Exception ex)
{
SystemInfo.Debug($"Update failed: {ex.Message}");
}
Result Mode (recommended):
var account = Database.Retrieve<Account>(accountId);
account.Name = ""; // Invalid - required field
var result = Database.Edit(account, new DatabaseOptions { IsApiMode = true });
if (result.HasError)
{
foreach (var error in result.Errors)
{
SystemInfo.Debug($"Field '{error.PropertyName}': {error.Message}");
}
}
Common Update Errors:
// Required field cleared
var account = Database.Retrieve<Account>(accountId);
account.Name = null; // Error: Name is required
Database.Edit(account);
// Invalid field value
var contact = Database.Retrieve<Contact>(contactId);
contact.Email = "invalid-email"; // Error: Invalid email format
Database.Edit(contact);
// Invalid reference
var contact = Database.Retrieve<Contact>(contactId);
contact.AccountId = "INVALID_ID"; // Error: Account does not exist
Database.Edit(contact);
// Duplicate unique value
var account = Database.Retrieve<Account>(accountId);
account.ExternalId__c = "EXISTING-VALUE"; // Error: Duplicate external ID
Database.Edit(account);
Bulk Update Error Handling:
var accounts = Database.Query<Account>()
.Where(f => f.Type == "Partner")
.ToList();
foreach (var account in accounts)
{
account.Industry = "Technology";
}
Database.Edit(accounts);
Controller Update Error Handling:
[HttpPost]
public ActionResponse UpdateAccount(Account model)
{
// Retrieve existing record
var account = Database.Retrieve<Account>(model.Id);
if (account == null)
{
ModelState.AddModelError("", "Account not found");
return View(model);
}
// Apply changes
account.Name = model.Name;
account.Industry = model.Industry;
account.Phone = model.Phone;
account.Website = model.Website;
// Update
Database.Edit(account);
AspxPage.AddMessage("Account updated successfully!");
return RedirectToAction("Details", new { id = account.Id });
}
Update Options (DatabaseOptions)
DatabaseOptions controls update operation behavior.
IsApiMode:
// Return errors in result (recommended)
var result = Database.Edit(account, new DatabaseOptions { IsApiMode = true });
if (result.HasError)
{
// Handle errors in result
}
// Throw exceptions on error (default)
try
{
var result = Database.Edit(account);
}
catch (DatabaseException ex)
{
// Handle exception
}
SystemMode:
// Update with system privileges (bypasses user permissions)
var result = Database.Edit(account, new DatabaseOptions
{
SystemMode = true,
IsApiMode = true
});
// Use SystemMode only when necessary:
// - Automated processes
// - System integrations
// - Administrative operations
⚠ Warning: Always document why SystemMode = true is required.
AllOrNone (Transactional Mode):
// All records must succeed or all fail
var accounts = Database.Query<Account>()
.Where(f => f.Type == "Partner")
.ToList();
foreach (var account in accounts)
{
account.Industry = "Technology";
}
var results = Database.Edit(accounts, new DatabaseOptions
{
AllOrNone = true,
IsApiMode = true
});
// If any record fails, all updates are rolled back
if (results.Any(r => r.HasError))
{
SystemInfo.Debug("Transaction failed - no records were updated");
}
else
{
SystemInfo.Debug("All records updated successfully");
}
Combined Options:
// Bulk update with full control
var results = Database.Edit(accounts, new DatabaseOptions
{
IsApiMode = true, // Return errors in results
AllOrNone = true, // All succeed or all fail
SystemMode = false // Respect user permissions
});
Update Operation Examples
Incremental Update:
// Increment field values
var opportunities = Database.Query<Opportunity>()
.Where(f => f.Stage == "Closed Won" && f.ContractSigned__c == true)
.ToList();
foreach (var opp in opportunities)
{
opp.ContractCount__c = (opp.ContractCount__c ?? 0) + 1;
opp.LastContractDate__c = DateTime.UtcNow;
}
Database.Edit(opportunities);
Cascade Update:
// Update related records when parent changes
public void UpdateAccountContacts(string accountId, string newMailingCity, string newMailingState)
{
// Update account
var account = Database.Retrieve<Account>(accountId);
account.BillingCity = newMailingCity;
account.BillingState = newMailingState;
var accountResult = Database.Edit(account);
// Update related contacts
var contacts = Database.Query<Contact>()
.Where(f => f.AccountId == accountId)
.ToList();
foreach (var contact in contacts)
{
contact.MailingCity = newMailingCity;
contact.MailingState = newMailingState;
}
Database.Edit(contacts);
}
Conditional Mass Update:
public void UpdateHighValueAccounts(decimal revenueThreshold, string newRating)
{
var accounts = Database.Query<Account>()
.Where(f => f.AnnualRevenue >= revenueThreshold)
.ToList();
var accountsToUpdate = new List<Account>();
foreach (var account in accounts)
{
// Only update if rating is different
if (account.Rating__c != newRating)
{
account.Rating__c = newRating;
account.LastRatingUpdate__c = DateTime.UtcNow;
accountsToUpdate.Add(account);
}
}
if (accountsToUpdate.Count > 0)
Database.Edit(accountsToUpdate);
else
SystemInfo.Debug("No accounts needed updating");
}
Status Change with Validation:
public void CloseOpportunity(string opportunityId, bool isWon, decimal? finalAmount)
{
var opp = Database.Retrieve<Opportunity>(opportunityId);
if (opp == null)
{
SystemInfo.Debug("Opportunity not found");
return;
}
if (opp.IsClosed)
{
SystemInfo.Debug("Opportunity is already closed");
return;
}
// Update status
opp.Stage = isWon ? "Closed Won" : "Closed Lost";
opp.CloseDate = DateTime.UtcNow;
if (finalAmount.HasValue)
opp.Amount = finalAmount.Value;
Database.Edit(opp);
}
Best Practices for Updating Records
✅ Don't use IsApiMode = true for error handling. When an error occurs, the database call will throw an exception. Handle the exception appropriately in your code.
Database.Edit(account);
✅ Use bulk operations for multiple records
// ✅ GOOD
Database.Edit(accounts);
// ❌ BAD
foreach (var account in accounts)
{
Database.Edit(account);
}
✅ Retrieve before update
// ✅ GOOD
var account = Database.Retrieve<Account>(accountId);
account.Name = "New Name";
Database.Edit(account);
// ❌ BAD - Object may not have all fields loaded
var account = new Account { Id = accountId, Name = "New Name" };
var result = Database.Edit(account, new DatabaseOptions { IsApiMode = true });
✅ Check for null before updating
var account = Database.Retrieve<Account>(accountId);
if (account == null)
{
SystemInfo.Debug("Account not found");
return;
}
account.Name = "Updated Name";
Database.Edit(account);
✅ Only update fields that need to change
// ✅ GOOD - Only modify fields that changed
var account = Database.Retrieve<Account>(accountId);
if (account.Industry != newIndustry)
{
account.Industry = newIndustry;
Database.Edit(account);
}
// ❌ WASTEFUL - Updates even when no change
var account = Database.Retrieve<Account>(accountId);
account.Industry = account.Industry; // No change
Database.Edit(account);
❌ Never query and update in loops
// ❌ VERY BAD
foreach (var id in accountIds)
{
var account = Database.Retrieve<Account>(id);
account.Industry = "Technology";
Database.Edit(account);
}
// ✅ GOOD
var accounts = Database.Query<Account>()
.Where(f => accountIds.Contains(f.Id))
.ToList();
foreach (var account in accounts)
{
account.Industry = "Technology";
}
Database.Edit(accounts);