Upsert

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 Name and Email are 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.

Choosing Upsert, Save, Insert or Update​