diff --git a/generator/.DevConfigs/8e546afe-27ad-4b11-8400-e0f3c33f0a4a.json b/generator/.DevConfigs/8e546afe-27ad-4b11-8400-e0f3c33f0a4a.json
new file mode 100644
index 000000000000..06ad294598d4
--- /dev/null
+++ b/generator/.DevConfigs/8e546afe-27ad-4b11-8400-e0f3c33f0a4a.json
@@ -0,0 +1,11 @@
+{
+ "services": [
+ {
+ "serviceName": "DynamoDBv2",
+ "type": "patch",
+ "changeLogMessages": [
+ "Fixed issue with TransactWrite in the DataModel where it wasn't correctly handling cases where only keys were being saved."
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/TransactWrite.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/TransactWrite.cs
index e5d69a3f4f9b..0340288caf8a 100644
--- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/TransactWrite.cs
+++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/TransactWrite.cs
@@ -16,6 +16,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
+using System.Linq;
#if AWS_ASYNC_API
using System.Threading;
using System.Threading.Tasks;
@@ -134,11 +135,8 @@ public void AddSaveItem(T item)
Expression conditionExpression = CreateConditionExpressionForVersion(storage);
SetNewVersion(storage);
- DocumentTransaction.AddDocumentToUpdate(storage.Document, new TransactWriteItemOperationConfig
- {
- ConditionalExpression = conditionExpression,
- ReturnValuesOnConditionCheckFailure = DocumentModel.ReturnValuesOnConditionCheckFailure.None
- });
+ AddDocumentTransaction(storage, conditionExpression);
+
var objectItem = new DynamoDBContext.ObjectWithItemStorage
{
OriginalObject = item,
@@ -437,6 +435,45 @@ private Expression CreateConditionExpressionForVersion(ItemStorage storage)
DocumentTransaction.TargetTable.IsEmptyStringValueEnabled);
return DynamoDBContext.CreateConditionExpressionForVersion(storage, conversionConfig);
}
+
+
+ private void AddDocumentTransaction(ItemStorage storage, Expression conditionExpression)
+ {
+ var hashKeyPropertyNames = storage.Config.HashKeyPropertyNames;
+ var rangeKeyPropertyNames = storage.Config.RangeKeyPropertyNames;
+
+ var attributeNames = storage.Document.Keys.ToList();
+
+ foreach (var keyPropertyName in hashKeyPropertyNames)
+ {
+ attributeNames.Remove(keyPropertyName);
+ }
+
+ foreach (var rangeKeyPropertyName in rangeKeyPropertyNames)
+ {
+ attributeNames.Remove(rangeKeyPropertyName);
+ }
+
+ // If there are no attributes left, we need to use PutItem
+ // as UpdateItem requires at least one data attribute
+ if (attributeNames.Any())
+ {
+ DocumentTransaction.AddDocumentToUpdate(storage.Document, new TransactWriteItemOperationConfig
+ {
+ ConditionalExpression = conditionExpression,
+ ReturnValuesOnConditionCheckFailure = DocumentModel.ReturnValuesOnConditionCheckFailure.None
+ });
+ }
+ else
+ {
+
+ DocumentTransaction.AddDocumentToPut(storage.Document, new TransactWriteItemOperationConfig
+ {
+ ConditionalExpression = conditionExpression,
+ ReturnValuesOnConditionCheckFailure = DocumentModel.ReturnValuesOnConditionCheckFailure.None
+ });
+ }
+ }
private void SetNewVersion(ItemStorage storage)
{
diff --git a/sdk/test/Services/DynamoDBv2/IntegrationTests/DataModelTests.cs b/sdk/test/Services/DynamoDBv2/IntegrationTests/DataModelTests.cs
index 5763cf7631ae..ec52570f756f 100644
--- a/sdk/test/Services/DynamoDBv2/IntegrationTests/DataModelTests.cs
+++ b/sdk/test/Services/DynamoDBv2/IntegrationTests/DataModelTests.cs
@@ -183,6 +183,76 @@ public void TestContext_DisableFetchingTableMetadata_KeyWithPropertyConverter()
Assert.AreEqual(employee.Name, storedEmployee.Name);
}
+
+ ///
+ /// Tests that disabling fetching table metadata works with a key that has a property converter.
+ ///
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void TestTransactWrite_AddSaveItem_DocumentTransaction()
+ {
+ TableCache.Clear();
+ CleanupTables();
+ TableCache.Clear();
+
+ CreateContext(DynamoDBEntryConversion.V2, true, true);
+
+ {
+
+ var hashRangeOnly = new AnnotatedRangeTable
+ {
+ Name = "Bob",
+ Age = 10
+ };
+
+ var transactWrite = Context.CreateTransactWrite();
+ transactWrite.AddSaveItem(hashRangeOnly);
+ transactWrite.Execute();
+
+ var storedHashOnly = Context.Load(hashRangeOnly.Name, hashRangeOnly.Age);
+ Assert.IsNotNull(storedHashOnly);
+ Assert.AreEqual(hashRangeOnly.Name, storedHashOnly.Name);
+ }
+
+ {
+ var hashRangeOnly = new IgnoreAnnotatedRangeTable
+ {
+ Name = "Bob",
+ Age = 10,
+ IgnoreAttribute = 100
+ };
+
+ var transactWrite = Context.CreateTransactWrite();
+ transactWrite.AddSaveItem(hashRangeOnly);
+ transactWrite.Execute();
+
+ var storedHashOnly = Context.Load(hashRangeOnly.Name, hashRangeOnly.Age);
+ Assert.IsNotNull(storedHashOnly);
+ Assert.AreEqual(hashRangeOnly.Name, storedHashOnly.Name);
+ Assert.AreEqual(hashRangeOnly.Age, storedHashOnly.Age);
+ }
+
+ {
+ var hashRangeOnly = new AnnotatedRangeTable2
+ {
+ Name = "Bob",
+ Age = 10,
+ NotAnnotatedAttribute = 100
+ };
+
+ var transactWrite = Context.CreateTransactWrite();
+ transactWrite.AddSaveItem(hashRangeOnly);
+ transactWrite.Execute();
+
+ var storedHashOnly = Context.Load(hashRangeOnly.Name, hashRangeOnly.Age);
+ Assert.IsNotNull(storedHashOnly);
+ Assert.AreEqual(hashRangeOnly.Name, storedHashOnly.Name);
+ Assert.AreEqual(hashRangeOnly.Age, storedHashOnly.Age);
+ Assert.AreEqual(hashRangeOnly.NotAnnotatedAttribute, storedHashOnly.NotAnnotatedAttribute);
+ }
+
+ }
+
///
/// Tests that the DynamoDB operations can retrieve attributes in UTC and local timezone.
///
@@ -2039,6 +2109,34 @@ public class PropertyConverterEmployee
public Status Name { get; set; }
}
+ [DynamoDBTable("HashRangeTable")]
+ public class AnnotatedRangeTable
+ {
+ // Hash key
+ [DynamoDBHashKey]
+ public string Name { get; set; }
+
+ // Range key
+ [DynamoDBRangeKey]
+ internal int Age { get; set; }
+ }
+
+ [DynamoDBTable("HashRangeTable")]
+ public class IgnoreAnnotatedRangeTable : AnnotatedRangeTable
+ {
+ [DynamoDBIgnore]
+ internal int IgnoreAttribute { get; set; }
+ }
+
+
+ [DynamoDBTable("HashRangeTable")]
+ public class AnnotatedRangeTable2 : AnnotatedRangeTable
+ {
+ internal int NotAnnotatedAttribute { get; set; }
+ }
+
+
+
public class DateTimeUtcConverter : IPropertyConverter
{
public DynamoDBEntry ToEntry(object value) => (DateTime)value;