Skip to content

High level API async CRUD

Daniel Frantík edited this page Aug 29, 2026 · 4 revisions

Async CRUD with the O/R mapper

The awaitable half of the high-level API: LoadAllAsync, SaveAsync, DeleteAsync and friends, for code that must not block a thread while the router thinks. Use it wherever you would use the synchronous originals — the rules are identical.

Every method here is the counterpart of a synchronous one described in reading data and CRUD, and behaves identically apart from being awaited.

  • connection.LoadAllAsync<TEntity>
  • connection.LoadListAsync<TEntity>
  • connection.LoadSingleAsync<TEntity> / LoadSingleOrDefaultAsync<TEntity>
  • connection.LoadByIdAsync<TEntity> / LoadByNameAsync<TEntity>
  • connection.SaveAsync<TEntity>
  • connection.DeleteAsync<TEntity>
  • command.LoadListAsync<TEntity> / LoadSingleAsync<TEntity> / LoadSingleOrDefaultAsync<TEntity>
using tik4net;
using tik4net.Objects;
using tik4net.Objects.Ip;

var addresses = await connection.LoadAllAsync<IpAddress>(cancellationToken);

var address = await connection.LoadByIdAsync<IpAddress>("*1", cancellationToken);
address.Comment = "WAN";
await connection.SaveAsync(address, cancellationToken: cancellationToken);

await connection.DeleteAsync(address, cancellationToken);

SaveAsync takes the same optional usedFieldsFilter and saveMode as Save, so the cancellation token is normally passed by name.

They are the same rules, not similar ones

Everything that is not "wait for the router" is shared code, not a parallel implementation: how many rows a load may return and which exception says otherwise, the change-tracking snapshot that lets a later save send only what changed, what counts as a create, which fields are unset rather than set, and that the unsets are sent before the set. So:

  • SaveAsync on an entity loaded with a Load*Async method and then left alone sends nothing — the same skip Save performs.
  • SaveAsync writes the new .id back into the entity after a create, exactly as Save does.
  • LoadByIdAsync throws TikNoSuchItemException where LoadById does.

Filters

LoadListAsync / LoadSingleAsync / LoadSingleOrDefaultAsync take the cancellation token before the filter parameters, because a params array has to come last:

var wanAddresses = await connection.LoadListAsync<IpAddress>(
    cancellationToken,
    connection.CreateParameter("interface", "ether1"));

// no token, filter by name
var byInterface = await connection.LoadListAsync<IpAddress>(
    filterParameters: connection.CreateParameter("interface", "ether1"));

Which transports

All of them — every shipped transport declares AsyncCommands. A transport that does not (a custom ITikConnection, say) throws TikConnectionCapabilityNotSupportedException rather than block a thread and call the result asynchronous. What a CancellationToken can actually stop once the command is on the wire differs by transport — see what the CancellationToken actually does.

Not here

  • LoadWithDuration needs the Streaming capability (binary API only) and has no async form.
  • LoadWithCallback / LoadListenWithCallback are not part of this family: they are the callback monitors described in reading data, gated on Listen, and they hand you a running ITikCommand rather than a Task. The 3.x names LoadAsync / LoadListenAsync are [Obsolete(error: true)] in 4.0 and no longer compile: they sat next to this family in IntelliSense while not being awaitable, so a warning would have let the mistake through.
  • SaveListDifferences, DeleteAll, Move, MoveToEnd stay synchronous. Each is a sequence of the primitives above, and an async form of them raises a question about what should happen when the third of five writes fails — a design decision, not a mechanical translation.

See also

Clone this wiki locally