OrmLite's Upsert API inserts a row when its Primary Key does not exist, or updates the row with the same Primary Key
when it does. On supported databases this is performed with a single native SQL statement, avoiding the separate
existence query used by db.Save().
var customer = new Customer
{
Id = 1,
Name = "Initial Name",
Email = "initial@example.org",
};
db.Upsert(customer); // Inserts Id=1
customer.Name = "Updated Name";
db.Upsert(customer); // Updates Id=1
Upsert is useful for synchronization, importing data, consuming events and retryable jobs where the desired result is:
Create this row, or bring the existing row with this Id up to date
It removes the need to first query whether the row exists and avoids a race between an application-side existence check and its subsequent insert or update.
Example Data Model​
The examples on this page use the following Data Model:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
[IgnoreOnUpdate]
public DateTime CreatedDate { get; set; } = DateTime.UtcNow;
}
OrmLite uses the Data Model's single Primary Key as the conflict key. By convention this is the Id property, although
an explicitly attributed [PrimaryKey] property and aliased table or column names are also supported.
Update selected fields​
The preferred way to restrict which fields are changed on an existing row is the typed updateOnly expression:
db.Upsert(customer,
updateOnly: x => new { x.Name, x.Email });
This has different behavior for each possible outcome:
- When the row is new, all insertable fields are inserted.
- When the Primary Key already exists, only
NameandEmailare updated.
This makes it safe to submit a complete Data Model whilst preserving fields owned by another part of the application:
var customer = new Customer
{
Id = 1,
Name = "Updated Name",
Email = "updated@example.org",
CreatedDate = DateTime.UtcNow, // Ignored when updating
};
db.Upsert(customer,
updateOnly: x => new { x.Name, x.Email });
The Primary Key and RowVersion fields cannot be included in updateOnly. Fields marked with [IgnoreOnUpdate] are also
excluded from updates made by Upsert.
Select fields at runtime​
String field names provide flexibility when the fields are selected dynamically:
var fields = includeEmail
? new[] { nameof(Customer.Name), nameof(Customer.Email) }
: new[] { nameof(Customer.Name) };
db.Upsert(customer, updateOnly: fields);
Prefer the typed expression when the field set is known at compile time, as it is refactor-safe and validated by C#.
Upsert multiple rows​
UpsertAll inserts new rows and updates existing rows in a transaction:
var customers = new[]
{
new Customer { Id = 1, Name = "Updated", Email = "one@example.org" },
new Customer { Id = 2, Name = "Inserted", Email = "two@example.org" },
};
db.UpsertAll(customers);
The same typed updateOnly API can restrict updates for every existing row. It does not restrict the fields inserted for
new rows:
db.UpsertAll(customers,
updateOnly: x => new { x.Name, x.Email });
String field-name overloads are also available for UpsertAll.
Async APIs​
Every Upsert API has an asynchronous equivalent and accepts an optional CancellationToken:
await db.UpsertAsync(customer,
updateOnly: x => new { x.Name, x.Email },
token: cancellationToken);
await db.UpsertAllAsync(customers,
updateOnly: x => new { x.Name, x.Email },
token: cancellationToken);
Auto-increment Primary Keys​
When an [AutoIncrement] Primary Key has its default value, Upsert treats the Data Model as a new row, inserts it and
populates its generated Id:
public class Customer
{
[AutoIncrement]
public long Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
var customer = new Customer
{
Name = "New Customer",
Email = "new@example.org",
};
db.Upsert(customer);
// Populated with the generated database ID
var id = customer.Id;
Once the generated ID is populated, subsequent calls use it as the Upsert conflict key:
customer.Name = "Updated Customer";
db.Upsert(customer);
An explicitly populated auto-increment Primary Key is preserved and can be used to insert or update that specific ID.
Native database support​
OrmLite generates the native Upsert syntax for its primary supported databases:
| Database | Generated operation |
|---|---|
| SQLite | INSERT ... ON CONFLICT (PrimaryKey) DO UPDATE |
| PostgreSQL | INSERT ... ON CONFLICT (PrimaryKey) DO UPDATE |
| SQL Server | MERGE ... WITH (HOLDLOCK) matching the Primary Key |
| MySQL / MariaDB | INSERT ... ON DUPLICATE KEY UPDATE |
Providers without native Upsert support fall back to Save()-style behavior: OrmLite checks whether the Primary Key
exists, then issues an INSERT or UPDATE. The fallback has the same updateOnly behavior, but requires a separate
existence query and cannot provide the same atomic single-statement behavior as a native Upsert.
INFO
MySQL and MariaDB's ON DUPLICATE KEY UPDATE can also be activated by a secondary UNIQUE constraint, not just the
Primary Key. Applications which require strict Primary-Key-only matching can disable native MySQL Upserts:
MySqlDialect.Instance.UseNativeUpsert = false;
OrmLite will then use its Primary-Key existence check and insert/update fallback.