Table of Contents

Struct UpdateCommand<T>

Namespace
DynaSharp
Assembly
DynaSharp.dll

An UpdateItem under construction. It changes single attributes, and it creates the item if none has the key, unless IfExists() says otherwise. Chain the changes and options in any order and end with SendAsync(CancellationToken) in one expression.

public readonly ref struct UpdateCommand<T> : IConditional<UpdateCommand<T>>, IUpdatable<UpdateCommand<T>>, IRetryable<UpdateCommand<T>> where T : IDynamoDbDocument<T>

Type Parameters

T

How the returned item is read.

Implements
Inherited Members

Examples

var moved = await customers.Update(Customer.Keys.Of("alice"))
    .Set(Customer.Attrs.ShipTo.At(Address.Attrs.City), "Bern")
    .Add(Customer.Attrs.Interests, ["keyboards"])
    .IfExists()
    .ReturnNew()
    .SendAsync(ct);

Remarks

Every attribute name becomes a placeholder, so reserved words such as status need no care. One attribute can appear in one action of an update only; DynamoDB rejects the request otherwise.

An update of an entity with a discriminator always ends its SET with _type = if_not_exists(_type, value), because the update may be what creates the item, and a typed read filters on it. A new item gets the type, and an item that exists keeps the one it has, so an update through the wrong table at a key held by another type does not change that type. It costs a name and a value like any other action, and UPDATED_NEW returns it. A key of the table is left out, because no update can write one, and so is an attribute the update has an action of its own on, such as .SetIfNotExists("_type", value).

An index of the entity whose keys are all literals holds every item, and no property can bring an item in, so an update writes those keys the same way, before the discriminator. An index with a key made of properties is written only by an update that sets those properties, with a key of literals beside them in the same SET, so that an update that does not touch it adds nothing to it.

See GetCommand<T> for what a command is and what it cannot do.

Methods

Add(string, AttributeValue)

Adds to a number, or adds elements to a set (ADD). An attribute that does not exist starts from zero or the empty set.

public UpdateCommand<T> Add(string path, AttributeValue value)

Parameters

path string

The name or document path.

value AttributeValue

The number or the set.

Returns

UpdateCommand<T>

The command, which replaces this one.

Add<TNumber>(string, TNumber)

Adds to a number (ADD). An attribute that does not exist starts from zero. A retry after a lost response repeats the addition. MaxAttempts(int) of 1 makes it at most once, not exact: a lost response then reports a failure after the addition was applied. Only a condition, such as a version, makes it exact.

public UpdateCommand<T> Add<TNumber>(string path, TNumber value) where TNumber : INumberBase<TNumber>

Parameters

path string

The name or document path.

value TNumber

The number, which may be negative.

Returns

UpdateCommand<T>

The command, which replaces this one.

Type Parameters

TNumber

The type of the number.

Add<TValue, TConverter>(Attr<TValue, NullableHandleConverter<TValue, TConverter>>, TValue)

Adds to a nullable number (ADD), see Add<TValue, TConverter>(Attr<TValue, TConverter>, TValue).

public UpdateCommand<T> Add<TValue, TConverter>(Attr<TValue, NullableHandleConverter<TValue, TConverter>> attribute, TValue value) where TValue : struct where TConverter : IDynamoDbConverter<TValue>, IAddableConverter

Parameters

attribute Attr<TValue, NullableHandleConverter<TValue, TConverter>>

The attribute.

value TValue

The number.

Returns

UpdateCommand<T>

The command, which replaces this one.

Type Parameters

TValue

The type of the attribute.

TConverter

The converter, of a number: ADD takes nothing else, so an attribute of another type is a compile error.

Add<TValue, TConverter>(Attr<TValue, NullableReferenceHandleConverter<TValue, TConverter>>, TValue)

Adds elements to a nullable set (ADD), see Add<TValue, TConverter>(Attr<TValue, TConverter>, TValue).

public UpdateCommand<T> Add<TValue, TConverter>(Attr<TValue, NullableReferenceHandleConverter<TValue, TConverter>> attribute, TValue value) where TValue : class where TConverter : IDynamoDbConverter<TValue>, IAddableConverter

Parameters

attribute Attr<TValue, NullableReferenceHandleConverter<TValue, TConverter>>

The attribute.

value TValue

The set.

Returns

UpdateCommand<T>

The command, which replaces this one.

Type Parameters

TValue

The type of the attribute.

TConverter

The converter, of a set: ADD takes nothing else, so an attribute of another type is a compile error.

Add<TValue, TConverter>(Attr<TValue, TConverter>, TValue)

Adds to a number, or adds elements to a set (ADD). An attribute that does not exist starts from zero or the empty set, and a negative number subtracts.

public UpdateCommand<T> Add<TValue, TConverter>(Attr<TValue, TConverter> attribute, TValue value) where TConverter : IDynamoDbConverter<TValue>, IAddableConverter

Parameters

attribute Attr<TValue, TConverter>

The attribute.

value TValue

The number or the set.

Returns

UpdateCommand<T>

The command, which replaces this one.

Type Parameters

TValue

The type of the attribute.

TConverter

The converter, of a number or a set: ADD takes nothing else, so an attribute of another type is a compile error.

Remarks

Taking stock. Add(Product.Attrs.Stock, -quantity) with If(Product.Attrs.Stock.Ge(quantity)) takes stock without going below zero.

A retry adds twice. A retry of the client after a lost response repeats the addition to a number, except in a transaction, whose attempts carry one ClientRequestToken, so DynamoDB applies it once. MaxAttempts(1) makes it at most once, not exact: a lost response then reports a failure after the addition was applied. Only a condition, such as a version, makes it exact.

Delete(string, AttributeValue)

Removes elements from a set (DELETE). A set that ends up empty disappears, as DynamoDB has no empty sets.

public UpdateCommand<T> Delete(string path, AttributeValue elements)

Parameters

path string

The name or document path.

elements AttributeValue

The elements, as a set.

Returns

UpdateCommand<T>

The command, which replaces this one.

Delete<TValue, TConverter>(Attr<TValue, TConverter>, TValue)

Removes elements from a set (DELETE). A set that ends up empty disappears, as DynamoDB has no empty sets.

public UpdateCommand<T> Delete<TValue, TConverter>(Attr<TValue, TConverter> attribute, TValue elements) where TConverter : IDynamoDbConverter<TValue>

Parameters

attribute Attr<TValue, TConverter>

The attribute.

elements TValue

The elements, as a set.

Returns

UpdateCommand<T>

The command, which replaces this one.

Type Parameters

TValue

The type of the attribute.

TConverter

The converter.

If(ExpressionHandler)

Adds a condition in the grammar of DynamoDB, for what the conditions of handles do not cover: .If($"attribute_exists({Attr("pk")}) AND size({Attr("tags")}) < {10}"). An interpolated Attr stands for an attribute name and any other interpolated value for a value. The update happens only if the condition holds, and a second condition means both must hold.

public UpdateCommand<T> If(ExpressionHandler condition)

Parameters

condition ExpressionHandler

The condition.

Returns

UpdateCommand<T>

The command, which replaces this one.

IfExists()

Updates only if an item has this key (attribute_exists on the partition key), which is how an update stops being an upsert. IfVersion(0) says that no item has the key, so with it SendAsync throws InvalidOperationException, in either order, unless Allow makes it ask for an item that reads as 0.

public UpdateCommand<T> IfExists()

Returns

UpdateCommand<T>

The command, which replaces this one.

IfNotExists()

Updates only if no item has this key (attribute_not_exists on the partition key), so that the update can only create. With IfVersion(n) above 0 SendAsync throws InvalidOperationException, because no item can lack its key and hold a version; version 0 is this same condition, which then renders once, also with Allow, where this alone says that the key is free.

public UpdateCommand<T> IfNotExists()

Returns

UpdateCommand<T>

The command, which replaces this one.

If<TCondition>(TCondition)

Adds a condition. The update happens only if it holds, and a second condition means both must hold.

public UpdateCommand<T> If<TCondition>(TCondition condition) where TCondition : ICondition

Parameters

condition TCondition

The condition.

Returns

UpdateCommand<T>

The command, which replaces this one.

Type Parameters

TCondition

The type of the condition.

Exceptions

ArgumentNullException

condition is null.

MaxAttempts(int)

Sets the number of attempts of this call including the first, in place of MaxAttempts. A retry after a lost response repeats a write that may have been applied, so 1 makes an ADD count at most once, and a lost response then reports a failure after the write was applied.

public UpdateCommand<T> MaxAttempts(int attempts)

Parameters

attempts int

The number of attempts.

Returns

UpdateCommand<T>

The command, which replaces this one.

Exceptions

ArgumentOutOfRangeException

attempts is below 1.

Remove(string)

Removes an attribute (REMOVE), or an element of a list, which moves the elements after it. Removing what does not exist is no error.

public UpdateCommand<T> Remove(string path)

Parameters

path string

The name or document path.

Returns

UpdateCommand<T>

The command, which replaces this one.

Remove<TValue, TConverter>(Attr<TValue, TConverter>)

Removes an attribute (REMOVE), by its handle. Removing what does not exist is no error. A property that feeds a key of a sparse index takes the key attributes of that index with it, so that the item leaves the index; see Set<TValue, TConverter>(Attr<TValue, TConverter>, TValue?).

public UpdateCommand<T> Remove<TValue, TConverter>(Attr<TValue, TConverter> attribute) where TConverter : IDynamoDbConverter<TValue>

Parameters

attribute Attr<TValue, TConverter>

The attribute.

Returns

UpdateCommand<T>

The command, which replaces this one.

Type Parameters

TValue

The type of the attribute.

TConverter

The converter.

Return(Attr)

Ends the chain with one attribute as the result, see Return<TValue, TConverter>(Attr<TValue, TConverter>), as an AttributeValue.

public ReturnCommand<AttributeValue, AttributeValueConverter> Return(Attr attribute)

Parameters

attribute Attr

The attribute, a name or a path through maps.

Returns

ReturnCommand<AttributeValue, AttributeValueConverter>

The command that is left.

Exceptions

ArgumentException

The path of the attribute has a list index.

ReturnCapacity(ReturnConsumedCapacity)

Asks for the consumed capacity, which goes to ConsumedCapacity and the capacity metric.

public UpdateCommand<T> ReturnCapacity(ReturnConsumedCapacity detail = ReturnConsumedCapacity.Total)

Parameters

detail ReturnConsumedCapacity

How much detail to return.

Returns

UpdateCommand<T>

The command, which replaces this one.

ReturnMetrics()

Asks for the size estimate of the item collection, which goes to ItemCollectionMetrics. DynamoDB has it for tables with a local secondary index.

public UpdateCommand<T> ReturnMetrics()

Returns

UpdateCommand<T>

The command, which replaces this one.

ReturnNew()

Returns the whole item as it is after the update in Item (ALL_NEW).

public UpdateCommand<T> ReturnNew()

Returns

UpdateCommand<T>

The command, which replaces this one.

ReturnOld()

Returns the item as it was before the update in Item (ALL_OLD). There is none if the update created it.

public UpdateCommand<T> ReturnOld()

Returns

UpdateCommand<T>

The command, which replaces this one.

ReturnOldOnFailure()

Puts the existing item on Item when a condition fails, or on Item with TrySendAsync(CancellationToken), which saves a read to find out why. It costs no extra capacity.

public UpdateCommand<T> ReturnOldOnFailure()

Returns

UpdateCommand<T>

The command, which replaces this one.

Return<TValue, TConverter>(Attr<TValue, TConverter>)

Ends the chain with one attribute as the result, for example a counter after Add (UPDATED_NEW). The result is the value, not a WriteResult<T>, and only clauses that come before this are possible. The update must change the attribute, or DynamoDB does not return it, and then sending throws DynamoDbMappingException, with the update applied.

public ReturnCommand<TValue, TConverter> Return<TValue, TConverter>(Attr<TValue, TConverter> attribute) where TConverter : IDynamoDbConverter<TValue>

Parameters

attribute Attr<TValue, TConverter>

The attribute, a name or a path through maps. A list element cannot be returned: DynamoDB returns a shortened list, in which its position is not known.

Returns

ReturnCommand<TValue, TConverter>

The command that is left.

Type Parameters

TValue

The type of the attribute.

TConverter

The converter.

Exceptions

ArgumentException

The path of the attribute has a list index.

SendAsync(CancellationToken)

Sends the command. A retry repeats the update, so one that is not idempotent, such as Add, is guarded by a condition or by MaxAttempts(int).

public ValueTask<WriteResult<T>> SendAsync(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken

Cancels the call.

Returns

ValueTask<WriteResult<T>>

The result. Await it once.

Exceptions

InvalidOperationException

The command was already sent, extended or rendered, an entity with a VersionAttribute has neither IfVersion nor IgnoreVersion, version 0 stands beside IfExists() without Allow, or a change by handle cannot leave a key consistent, see Set<TValue, TConverter>(Attr<TValue, TConverter>, TValue?).

ObjectDisposedException

The client is disposed.

ConditionalCheckFailedException

A condition did not hold, so nothing was written.

ValidationException

DynamoDB rejects the request; the exception shows the expressions.

ResourceNotFoundException

The table does not exist.

DynamoDbMappingException

The returned item cannot be read as T. The write has happened.

DynamoDbException

DynamoDB returns any other error; a throttling or server error only after the last attempt.

Set(ExpressionHandler)

Adds a SET action in the grammar of DynamoDB, for what the other forms do not cover, such as arithmetic and list_append: .Set($"{Attr("total")} = {Attr("total")} + {5}"). An interpolated Attr stands for an attribute name and any other interpolated value for a value.

public UpdateCommand<T> Set(ExpressionHandler action)

Parameters

action ExpressionHandler

The action.

Returns

UpdateCommand<T>

The command, which replaces this one.

Set(string, AttributeValue)

Sets an attribute, creating it if it does not exist (SET).

public UpdateCommand<T> Set(string path, AttributeValue value)

Parameters

path string

The name or document path. The parent of a nested path must exist.

value AttributeValue

The value.

Returns

UpdateCommand<T>

The command, which replaces this one.

SetIfNotExists(string, AttributeValue)

Sets an attribute unless it exists (SET a = if_not_exists(a, value)).

public UpdateCommand<T> SetIfNotExists(string path, AttributeValue value)

Parameters

path string

The name or document path.

value AttributeValue

The value.

Returns

UpdateCommand<T>

The command, which replaces this one.

SetIfNotExists<TNumber>(string, TNumber)

Sets a number attribute unless it exists (SET a = if_not_exists(a, value)).

public UpdateCommand<T> SetIfNotExists<TNumber>(string path, TNumber value) where TNumber : INumberBase<TNumber>

Parameters

path string

The name or document path.

value TNumber

The number.

Returns

UpdateCommand<T>

The command, which replaces this one.

Type Parameters

TNumber

The type of the number.

SetIfNotExists<TValue, TConverter>(Attr<TValue, TConverter>, TValue)

Sets an attribute unless it exists (SET a = if_not_exists(a, value)).

public UpdateCommand<T> SetIfNotExists<TValue, TConverter>(Attr<TValue, TConverter> attribute, TValue value) where TConverter : IDynamoDbConverter<TValue>

Parameters

attribute Attr<TValue, TConverter>

The attribute.

value TValue

The value.

Returns

UpdateCommand<T>

The command, which replaces this one.

Type Parameters

TValue

The type of the attribute.

TConverter

The converter.

Exceptions

ArgumentNullException

value is null.

ArgumentException

value is empty, on a property with OmitEmptyAttribute, which counts that as null, or anything else that IsOmitted(T) of the converter counts as null.

SetIfNotNull<TValue, TConverter>(Attr<TValue, TConverter>, TValue?)

Sets an attribute of a value type if the nullable value has a value, and adds nothing if it has none. With a value it costs what Set<TValue, TConverter>(Attr<TValue, TConverter>, TValue?) costs. See SetIfNotNull<TValue, TConverter>(Attr<TValue, TConverter>, TValue?).

public UpdateCommand<T> SetIfNotNull<TValue, TConverter>(Attr<TValue, TConverter> attribute, TValue? value) where TValue : struct where TConverter : IDynamoDbConverter<TValue>

Parameters

attribute Attr<TValue, TConverter>

The attribute.

value TValue?

The value, or null to leave the attribute alone.

Returns

UpdateCommand<T>

The command, which replaces this one.

Type Parameters

TValue

The type of the attribute.

TConverter

The converter.

SetIfNotNull<TValue, TConverter>(Attr<TValue, TConverter>, TValue?)

Sets an attribute if there is a value, and leaves it as it is if the value is null, which is what an update of optional fields wants: .SetIfNotNull(Customer.Attrs.Email, request.Email).

public UpdateCommand<T> SetIfNotNull<TValue, TConverter>(Attr<TValue, TConverter> attribute, TValue? value) where TConverter : IDynamoDbConverter<TValue>

Parameters

attribute Attr<TValue, TConverter>

The attribute.

value TValue

The value, or null to leave the attribute alone.

Returns

UpdateCommand<T>

The command, which replaces this one.

Type Parameters

TValue

The type of the attribute.

TConverter

The converter.

Remarks

With a value it is Set<TValue, TConverter>(Attr<TValue, TConverter>, TValue?), with all it says about keys. With null the attribute stays as it is, where Set would remove it, and a property that feeds a key counts as untouched. On a property with OmitEmptyAttribute an empty string or collection is no value either. An update that ends up with no action is still an UpdateItem: it checks its conditions, creates the item if none has the key, and writes what the entity owes it, such as its discriminator.

Set<TNumber>(string, TNumber)

Sets a number attribute, creating it if it does not exist (SET).

public UpdateCommand<T> Set<TNumber>(string path, TNumber value) where TNumber : INumberBase<TNumber>

Parameters

path string

The name or document path. The parent of a nested path must exist.

value TNumber

The number.

Returns

UpdateCommand<T>

The command, which replaces this one.

Type Parameters

TNumber

The type of the number.

Set<TValue, TConverter>(Attr<TValue, TConverter>, TValue?)

Sets an attribute of a value type from a nullable value, which is what a nullable property holds, or removes it when there is no value: a Nullable<T> with a value is Set<TValue, TConverter>(Attr<TValue, TConverter>, TValue?) at the same cost, and without one it is Remove<TValue, TConverter>(Attr<TValue, TConverter>). The rules of a property that feeds a key are the ones of those two.

public UpdateCommand<T> Set<TValue, TConverter>(Attr<TValue, TConverter> attribute, TValue? value) where TValue : struct where TConverter : IDynamoDbConverter<TValue>

Parameters

attribute Attr<TValue, TConverter>

The attribute.

value TValue?

The value, or null to remove the attribute.

Returns

UpdateCommand<T>

The command, which replaces this one.

Type Parameters

TValue

The type of the attribute.

TConverter

The converter.

Set<TValue, TConverter>(Attr<TValue, TConverter>, TValue?)

Sets an attribute, creating it if it does not exist (SET), and keeps every key made from it consistent, or throws when the command ends. A null value is a Remove<TValue, TConverter>(Attr<TValue, TConverter>), for every attribute, because a generated entity writes no attribute for a null property either. On a property with OmitEmptyAttribute an empty string or collection is one too.

public UpdateCommand<T> Set<TValue, TConverter>(Attr<TValue, TConverter> attribute, TValue? value) where TConverter : IDynamoDbConverter<TValue>

Parameters

attribute Attr<TValue, TConverter>

The attribute.

value TValue

The value, or null to remove the attribute.

Returns

UpdateCommand<T>

The command, which replaces this one.

Type Parameters

TValue

The type of the attribute.

TConverter

The converter.

Remarks

Key templates. The handle of a property that feeds a key template keeps that key consistent, which a raw path does not. When the command ends, all inputs of the template must be known: then the derived attribute is written in the same SET, and otherwise sending throws InvalidOperationException naming the template and the Set calls to add, because the index would go stale. An update expression can copy an attribute and cannot format one, so an input of a key of several values or with literals that does not change is set as well, to the value the item holds. An input of a key of the table is known from the key the update was given, and it cannot be set, because DynamoDB cannot update a key, so an index key that repeats it needs nothing more: that is the place for an input that never changes. A key given as strings is parsed for that, so a value that its text does not hold, such as a timestamp a key cuts off at the day, comes from a generated key or not at all: then use a Put of the changed entity, or a raw path. A write by handle to a property that a computed input is derived from, see ComputedFromAttribute, also to a path inside it, throws in the same way, because no update can run a getter, and so does a write by handle to such a computed input itself.

Sparse indexes. An index takes an item only with all its keys, so setting one key of a sparse index needs the other one in the same update, or sending throws InvalidOperationException naming it: an update does not read the item, so it cannot tell whether the item holds the other key, and an item without it would stay out of the index without an error. A key of literals, of any index, and a key made only of inputs of the table key are no such keys: they are written in the same SET, and removed with the other key. A key that is the stored text of an attribute, one bare placeholder such as {Owner} over a string, also with :raw, a Guid, a DateOnly, a timestamp in ticks or an enum by name, each with its built-in converter, is copied in the same SET, gsi1pk = Owner, so the update needs no value for it. DynamoDB evaluates the copy on the item: an item without the attribute, also one that the update would create, fails the whole update with ValidationException, "The provided expression refers to an attribute that does not exist in the item", and is left as it was.

Add and SetIfNotExists. By the handle of such a property they throw too: the value they leave is not known here, so the key cannot follow. In a transaction the check runs when the action ends, at the next verb.

ToRequestJson()

Renders the body this command would send, as DynamoDB JSON, without sending it. It ends the command, and is meant for tests and debugging. The text contains the key and the values.

public string ToRequestJson()

Returns

string

The request body.

Exceptions

InvalidOperationException

The command was already sent, extended or rendered.

TrySendAsync(CancellationToken)

Sends the command as SendAsync(CancellationToken) does, and takes a failed condition as an answer instead of throwing ConditionalCheckFailedException: ConditionMet is then false, nothing was written, and with ReturnOldOnFailure() Item is the item the condition saw. It is for a write whose failed condition is routine, such as an event that is old news, and no exception is thrown for it.

public ValueTask<WriteResult<T>> TrySendAsync(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken

Cancels the call.

Returns

ValueTask<WriteResult<T>>

The result. Await it once.

Remarks

The request is the one SendAsync(CancellationToken) sends, and every other error throws as there. ReturnOldOnFailure() stays a choice: without it a failed condition gives no item. ReturnOld() or ReturnNew() gives nothing on a failed condition, because DynamoDB returns nothing then.

A retry of the client after a lost response meets what its first attempt wrote, and a condition that rules out the write's own result then fails. That is the same answer a second delivery of the same change gets, and on the wire the two cannot be told apart, so ConditionMet does not say which. Compare the item of ReturnOldOnFailure() with what the update sent: the same version or time means that the change is stored, by this call or an earlier one.

Inside RetryOnConflictAsync a failed condition of this method is no conflict, so the delegate does not run again. The span of a failed condition is no error, and carries dynasharp.condition_met = false.

Exceptions

InvalidOperationException

The command was already sent, extended or rendered, an entity with a VersionAttribute has neither IfVersion nor IgnoreVersion, version 0 stands beside IfExists() without Allow, or a change by handle cannot leave a key consistent, see Set<TValue, TConverter>(Attr<TValue, TConverter>, TValue?).

ObjectDisposedException

The client is disposed.

ValidationException

DynamoDB rejects the request; the exception shows the expressions.

ResourceNotFoundException

The table does not exist.

DynamoDbMappingException

The returned item, or the item a failed condition saw, cannot be read as T. The write has happened only in the first case.

DynamoDbException

DynamoDB returns any other error; a throttling or server error only after the last attempt.