feat(client-dotnet): add WriteArrayElementsAsync default-fill helper and document semantics

Adds a public WriteArrayElementsAsync helper on MxGatewaySession that builds
an MxValue{SparseArrayValue} and delegates to the existing WriteAsync. Extracts
the MxValue construction into an internal static BuildSparseArray builder for
unit-testability. Two new tests cover builder output shape and the full write
command path. README documents the reset (not preserve) semantics alongside
the existing whole-array guidance.
This commit is contained in:
Joseph Doherty
2026-06-18 02:59:43 -04:00
parent 627c17fae1
commit 95b5b09a67
3 changed files with 144 additions and 0 deletions
@@ -687,6 +687,63 @@ public sealed class MxGatewaySession : IAsyncDisposable
reply.EnsureProtocolSuccess().EnsureMxAccessSuccess();
}
/// <summary>
/// Writes specific array indices to an item using default-fill semantics.
/// </summary>
/// <remarks>
/// The gateway expands the sparse descriptor into a full <c>totalLength</c>-element array
/// before forwarding to the worker. Indices not listed in <paramref name="elements"/> are
/// written as the element type's default value — this is a RESET, not a preserve. The
/// current values at those positions are discarded. <paramref name="totalLength"/> is
/// required and must match the declared length of the array attribute.
/// </remarks>
/// <param name="serverHandle">The ServerHandle from register.</param>
/// <param name="itemHandle">The ItemHandle from add-item.</param>
/// <param name="elementDataType">The MXAccess data type of each element.</param>
/// <param name="totalLength">The total declared length of the target array attribute.</param>
/// <param name="elements">Map of zero-based array index to scalar <see cref="MxValue"/>.</param>
/// <param name="userId">User ID context for the write.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public Task WriteArrayElementsAsync(
int serverHandle,
int itemHandle,
MxDataType elementDataType,
uint totalLength,
IReadOnlyDictionary<uint, MxValue> elements,
int userId = 0,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(elements);
MxValue value = BuildSparseArray(elementDataType, totalLength, elements);
return WriteAsync(serverHandle, itemHandle, value, userId, cancellationToken);
}
/// <summary>
/// Builds an <see cref="MxValue"/> whose <see cref="MxValue.SparseArrayValue"/> describes a
/// default-fill partial array write.
/// </summary>
/// <param name="elementDataType">The MXAccess data type of each element.</param>
/// <param name="totalLength">The total declared length of the target array attribute.</param>
/// <param name="elements">Map of zero-based array index to scalar <see cref="MxValue"/>.</param>
/// <returns>An <see cref="MxValue"/> with <see cref="MxValue.KindOneofCase.SparseArrayValue"/> set.</returns>
internal static MxValue BuildSparseArray(
MxDataType elementDataType,
uint totalLength,
IReadOnlyDictionary<uint, MxValue> elements)
{
MxSparseArray sparse = new()
{
ElementDataType = elementDataType,
TotalLength = totalLength,
};
foreach (KeyValuePair<uint, MxValue> kv in elements)
{
sparse.Elements.Add(new MxSparseElement { Index = kv.Key, Value = kv.Value });
}
return new MxValue { SparseArrayValue = sparse };
}
/// <summary>
/// Writes a value to an item on the MXAccess server without error checking.
/// </summary>