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
THow 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
pathstringThe name or document path.
valueAttributeValueThe 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
pathstringThe name or document path.
valueTNumberThe number, which may be negative.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Type Parameters
TNumberThe 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
attributeAttr<TValue, NullableHandleConverter<TValue, TConverter>>The attribute.
valueTValueThe number.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Type Parameters
TValueThe type of the attribute.
TConverterThe converter, of a number:
ADDtakes 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
attributeAttr<TValue, NullableReferenceHandleConverter<TValue, TConverter>>The attribute.
valueTValueThe set.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Type Parameters
TValueThe type of the attribute.
TConverterThe converter, of a set:
ADDtakes 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
attributeAttr<TValue, TConverter>The attribute.
valueTValueThe number or the set.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Type Parameters
TValueThe type of the attribute.
TConverterThe converter, of a number or a set:
ADDtakes 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
pathstringThe name or document path.
elementsAttributeValueThe 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
attributeAttr<TValue, TConverter>The attribute.
elementsTValueThe elements, as a set.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Type Parameters
TValueThe type of the attribute.
TConverterThe 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
conditionExpressionHandlerThe 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
conditionTConditionThe condition.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Type Parameters
TConditionThe type of the condition.
Exceptions
- ArgumentNullException
conditionis 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
attemptsintThe number of attempts.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Exceptions
- ArgumentOutOfRangeException
attemptsis 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
pathstringThe 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
attributeAttr<TValue, TConverter>The attribute.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Type Parameters
TValueThe type of the attribute.
TConverterThe 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
attributeAttrThe 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
detailReturnConsumedCapacityHow 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
attributeAttr<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
TValueThe type of the attribute.
TConverterThe 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
cancellationTokenCancellationTokenCancels 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
IfVersionnorIgnoreVersion, 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
actionExpressionHandlerThe 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
pathstringThe name or document path. The parent of a nested path must exist.
valueAttributeValueThe 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
pathstringThe name or document path.
valueAttributeValueThe 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
pathstringThe name or document path.
valueTNumberThe number.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Type Parameters
TNumberThe 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
attributeAttr<TValue, TConverter>The attribute.
valueTValueThe value.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Type Parameters
TValueThe type of the attribute.
TConverterThe converter.
Exceptions
- ArgumentNullException
valueis null.- ArgumentException
valueis 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
attributeAttr<TValue, TConverter>The attribute.
valueTValue?The value, or null to leave the attribute alone.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Type Parameters
TValueThe type of the attribute.
TConverterThe 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
attributeAttr<TValue, TConverter>The attribute.
valueTValueThe value, or null to leave the attribute alone.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Type Parameters
TValueThe type of the attribute.
TConverterThe 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
pathstringThe name or document path. The parent of a nested path must exist.
valueTNumberThe number.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Type Parameters
TNumberThe 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
attributeAttr<TValue, TConverter>The attribute.
valueTValue?The value, or null to remove the attribute.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Type Parameters
TValueThe type of the attribute.
TConverterThe 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
attributeAttr<TValue, TConverter>The attribute.
valueTValueThe value, or null to remove the attribute.
Returns
- UpdateCommand<T>
The command, which replaces this one.
Type Parameters
TValueThe type of the attribute.
TConverterThe 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
cancellationTokenCancellationTokenCancels 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
IfVersionnorIgnoreVersion, 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.