I need to implement a transaction in C# with AWS DynamoDb as database
I checked the official website but don't see any example with C#
Below are my different Db operations.
public class DbHandler
{
private readonly IConfiguration _configuration;
private readonly RegionEndpoint _region;
private readonly AmazonDynamoDBClient _dynamoClient;
public DbHandler(IConfiguration configuration)
{
_configuration = configuration;
var awsSettings = configuration.GetSection("AWS:DynamoDb");
_region = RegionEndpoint.GetBySystemName(awsSettings["Region"]);
_dynamoClient = SetDynamoClient(awsSettings);
}
public async Task<EventTO> Add(EventTO eventObj)
{
try
{
//Db Operation#1
await _dynamoClient.PutItemAsync(
tableName: _configuration.GetSection("AWS:DynamoDb")["Table1"],
item: SetEventObject(eventObj));
//Db Operation#2
await _dynamoClient.PutItemAsync(tableName: _configuration.GetSection("AWS:DynamoDb")["Table2"], someotherObj);
return eventObj;
}
catch (Exception ex)
{
throw;
}
}
}
I am using the Low Level API
private Dictionary<string, AttributeValue> SetEventObject(EventTO eventObj)
{
//DynamoDb - Using Low Level API
var attributes = new Dictionary<string, AttributeValue>
{
//EventId
{
nameof(eventObj.EventId),
new AttributeValue
{
S =eventObj.EventId
}
},
//Event Title
{
nameof(eventObj.Title),
new AttributeValue
{
S = eventObj.Title.Trim()
}
}
};
return attributes;
}
I want to know how to implement Transaction using the Low Level API in C# for AWS DynamoDb?
Thanks!
For those looking for something more detailed, the following translates the Java snippets the question links to at https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-example.html into their C# equivalents.
Keep in mind that the original Java example is a contrived one. In a production environment, you would want to do things like separate business logic from writing to a console and separate the various parts of building a transaction into smaller methods to adhere to SRP.
For instructions on how to setup your AmazonDynamoDBClient, see https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.DotNet.html
Since C# doesn't support final local variables, you can convert those from the Java example to fields to achieve the same effect:
private const string CUSTOMER_TABLE_NAME = "Customers";
private const string CUSTOMER_PARTITION_KEY = "CustomerId";
private const string PRODUCT_TABLE_NAME = "ProductCatalog";
private const string PRODUCT_PARTITION_KEY = "ProductId";
private const string ORDER_PARTITION_KEY = "OrderId";
private const string ORDER_TABLE_NAME = "Orders";
productKey and orderIdusing Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
var customerId = "09e8e9c8-ec48";
var customerItemKey = new Dictionary<string, AttributeValue>
{
{ CUSTOMER_PARTITION_KEY, new AttributeValue(customerId) }
};
var checkCustomerValid = new ConditionCheck()
{
TableName = CUSTOMER_TABLE_NAME,
Key = customerItemKey,
ConditionExpression = "attribute_exists(" + CUSTOMER_PARTITION_KEY + ")"
};
var productItemKey = new Dictionary<string, AttributeValue>
{
{ PRODUCT_PARTITION_KEY, new AttributeValue(productKey) }
};
var expressionAttributeValues = new Dictionary<string, AttributeValue>
{
{ ":new_status", new AttributeValue("SOLD") },
{ ":expected_status", new AttributeValue("IN_STOCK") }
};
var markItemSold = new Update()
{
TableName = PRODUCT_TABLE_NAME,
Key = productItemKey,
UpdateExpression = "SET ProductStatus = :new_status",
ExpressionAttributeValues = expressionAttributeValues,
ConditionExpression = "ProductStatus = :expected_status",
ReturnValuesOnConditionCheckFailure = ReturnValuesOnConditionCheckFailure.ALL_OLD
};
var orderItem = new Dictionary<string, AttributeValue>
{
{ ORDER_PARTITION_KEY, new AttributeValue(orderId) },
{ PRODUCT_PARTITION_KEY, new AttributeValue(productKey) },
{ CUSTOMER_PARTITION_KEY, new AttributeValue(customerId) },
{ "OrderStatus", new AttributeValue("CONFIRMED") },
{ "OrderTotal", new AttributeValue("100") }
};
var createOrder = new Put()
{
TableName = ORDER_TABLE_NAME,
Item = orderItem,
ReturnValuesOnConditionCheckFailure = ReturnValuesOnConditionCheckFailure.ALL_OLD,
ConditionExpression = "attribute_not_exists(" + ORDER_PARTITION_KEY + ")"
};
var actions = new List<TransactWriteItem>()
{
new TransactWriteItem() {ConditionCheck = checkCustomerValid },
new TransactWriteItem() { Update = markItemSold },
new TransactWriteItem() { Put = createOrder }
};
var placeOrderTransaction = new TransactWriteItemsRequest()
{
TransactItems = actions,
ReturnConsumedCapacity = ReturnConsumedCapacity.TOTAL
};
// Run the transaction and process the result.
try
{
_ = await client.TransactWriteItemsAsync(placeOrderTransaction);
Console.WriteLine("Transaction Successful");
}
catch (ResourceNotFoundException rnf)
{
Console.Error.WriteLine("One of the table involved in the transaction is not found" + rnf.Message);
}
catch (InternalServerErrorException ise)
{
Console.Error.WriteLine("Internal Server Error" + ise.Message);
}
catch (TransactionCanceledException tce)
{
Console.Error.WriteLine("Transaction Canceled " + tce.Message);
}
var productItemKey = new Dictionary<string, AttributeValue>
{
{ PRODUCT_PARTITION_KEY, new AttributeValue(productKey) }
};
var orderKey = new Dictionary<string, AttributeValue>
{
{ ORDER_PARTITION_KEY, new AttributeValue(orderId) }
};
var readProductSold = new Get()
{
TableName = PRODUCT_TABLE_NAME,
Key = productItemKey
};
var readCreatedOrder = new Get()
{
TableName = ORDER_TABLE_NAME,
Key = orderKey
};
var getActions = new List<TransactGetItem>()
{
new TransactGetItem(){Get = readProductSold },
new TransactGetItem(){Get = readCreatedOrder}
};
var readCompletedOrder = new TransactGetItemsRequest()
{
TransactItems = getActions,
ReturnConsumedCapacity = ReturnConsumedCapacity.TOTAL
};
// Run the transaction and process the result.
try
{
var result = await client.TransactGetItemsAsync(readCompletedOrder);
Console.WriteLine(result.Responses);
}
catch (ResourceNotFoundException rnf)
{
Console.Error.WriteLine("One of the table involved in the transaction is not found" + rnf.Message);
}
catch (InternalServerErrorException ise)
{
Console.Error.WriteLine("Internal Server Error" + ise.Message);
}
catch (TransactionCanceledException tce)
{
Console.Error.WriteLine("Transaction Canceled" + tce.Message);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With