Appearance
Operations Framework: Transient Operations
A transient operation is one that exists only in memory during command execution. It's not written to the operation log, so its invalidation never reaches other hosts.
Transient operations are created by TransientOperationScope and have:
IsTransient = trueHasStoredOperation = false- UUID format:
{uuid}-local
When Are Operations Transient?
Operations become transient when:
- The command doesn't use
CreateOperationDbContext()(no database interaction) - The command explicitly disables storage with
Operation.StoreMode = OperationStoreMode.None
cs
// Transient: No database context requested
[CommandHandler]
[DeferredInvalidationMode(DeferredInvalidationMode.Local)]
public virtual async Task IncrementCounter(
IncrementCommand command, CancellationToken cancellationToken = default)
{
// No CreateOperationDbContext = transient operation
_counters.AddOrUpdate(command.Key, 1, (_, v) => v + 1);
Invalidation.Defer(() => _ = GetCounter(command.Key, default));
}Persistent Operations
Operations become persistent when using CreateOperationDbContext:
cs
// Persistent: Uses database context
[CommandHandler]
[DeferredInvalidationMode(DeferredInvalidationMode.Local)]
public virtual async Task UpdateUser(
UpdateUserCommand command, CancellationToken cancellationToken = default)
{
// CreateOperationDbContext = persistent operation
await using var dbContext = await DbHub.CreateOperationDbContext(cancellationToken);
var user = await dbContext.Users.FindAsync(command.UserId);
user!.Name = command.Name;
await dbContext.SaveChangesAsync(cancellationToken);
Invalidation.Defer(() => _ = GetUser(command.UserId, default));
}Comparison
| Aspect | Transient | Persistent |
|---|---|---|
| Scope Provider | TransientOperationScopeProvider | DbOperationScopeProvider<T> |
IsTransient | true | false |
| Stored in DB | No | Yes |
| Invalidation reaches other hosts | No | Yes |
| Can have events | No | Yes |
| Can carry recorded invalidation calls | No | Yes |
| UUID suffix | -local | (none) |
Controlling Storage
You can explicitly control whether an operation is stored:
cs
[CommandHandler]
public virtual async Task SomeCommand(
SomeCommand command, CancellationToken cancellationToken = default)
{
var context = CommandContext.GetCurrent();
await using var dbContext = await DbHub.CreateOperationDbContext(cancellationToken);
// ... do work ...
// Don't store the operation (thus only local invalidation, etc.)
context.Operation.StoreMode = OperationStoreMode.None;
}Transient Operation Limitations
- No events: Calling
AddEvent()throwsTransientScopeOperationCannotHaveEvents - No cross-host invalidation: Only the local host sees the invalidation
- Invalidation is local:
ReplicatedandDistributedboth throw on a transient scope, because there's no stored row to carry the recorded calls
Use transient operations for:
- Commands that modify only in-memory state
- Commands that don't need cluster-wide invalidation
- Commands where you handle invalidation through other means
Operation Completion
Whether transient or persistent, all operations go through completion:
The completion flow is the same for both transient and persistent operations, but only a persistent operation's recorded invalidation calls reach other hosts.
