From 8922aff53240af3cae300974b2d063413f5285df Mon Sep 17 00:00:00 2001
From: Malhar Khimsaria <96malhar@gmail.com>
Date: Sun, 11 Aug 2024 22:24:18 -0700
Subject: [PATCH] Add the ability to mock TransactWrite and
MultiTableTransactWrite operations
---
.../DynamoDBv2/Custom/DataModel/Context.cs | 38 +-
.../Custom/DataModel/IDynamoDBContext.cs | 8 +-
.../Custom/DataModel/TransactWrite.cs | 406 +++++++++---------
.../Custom/DataModel/_async/Context.Async.cs | 9 +-
.../_async/IDynamoDBContext.Async.cs | 2 +-
.../DataModel/_async/TransactWrite.Async.cs | 40 +-
.../Custom/DataModel/_bcl/Context.Sync.cs | 7 +-
.../DataModel/_bcl/IDynamoDBContext.Sync.cs | 2 +-
.../DataModel/_bcl/TransactWrite.Sync.cs | 40 +-
.../MockabilityTests/TransactWriteTests.cs | 121 ++++++
10 files changed, 380 insertions(+), 293 deletions(-)
create mode 100644 sdk/test/Services/DynamoDBv2/UnitTests/Custom/MockabilityTests/TransactWriteTests.cs
diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/Context.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/Context.cs
index 3012c9089c93..54d5e24300c9 100644
--- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/Context.cs
+++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/Context.cs
@@ -342,52 +342,30 @@ public MultiTableTransactGet CreateMultiTableTransactGet(params TransactGet[] tr
#region TransactWrite
- ///
- /// Creates a strongly-typed TransactWrite object, allowing
- /// a transactional write operation against DynamoDB.
- ///
- /// Type of objects to write.
- /// Empty strongly-typed TransactWrite object.
- public TransactWrite CreateTransactWrite()
+ ///
+ public ITransactWrite CreateTransactWrite()
{
return CreateTransactWrite((TransactWriteConfig)null);
}
- ///
- /// Creates a strongly-typed TransactWrite object, allowing
- /// a transactional write operation against DynamoDB.
- ///
- /// Type of objects to write.
- /// Config object which can be used to override that table used.
- /// Empty strongly-typed TransactWrite object.
+ ///
[Obsolete("Use the CreateTransactWrite overload that takes TransactWriteConfig instead, since DynamoDBOperationConfig contains properties that are not applicable to CreateTransactWrite.")]
- public TransactWrite CreateTransactWrite(DynamoDBOperationConfig operationConfig)
+ public ITransactWrite CreateTransactWrite(DynamoDBOperationConfig operationConfig)
{
DynamoDBFlatConfig config = new DynamoDBFlatConfig(operationConfig, this.Config);
return new TransactWrite(this, config);
}
- ///
- /// Creates a strongly-typed TransactWrite object, allowing
- /// a transactional write operation against DynamoDB.
- ///
- /// Type of objects to write.
- /// Config object that can be used to override properties on the table's context for this request.
- /// Empty strongly-typed TransactWrite object.
- public TransactWrite CreateTransactWrite(TransactWriteConfig transactWriteConfig)
+ ///
+ public ITransactWrite CreateTransactWrite(TransactWriteConfig transactWriteConfig)
{
DynamoDBFlatConfig config = new DynamoDBFlatConfig(transactWriteConfig?.ToDynamoDBOperationConfig(), this.Config);
return new TransactWrite(this, config);
}
- ///
- /// Creates a MultiTableTransactWrite object, composed of multiple
- /// individual TransactWrite objects.
- ///
- /// Individual TransactWrite objects.
- /// Composite MultiTableTransactWrite object.
- public MultiTableTransactWrite CreateMultiTableTransactWrite(params TransactWrite[] transactionParts)
+ ///
+ public IMultiTableTransactWrite CreateMultiTableTransactWrite(params ITransactWrite[] transactionParts)
{
return new MultiTableTransactWrite(transactionParts);
}
diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/IDynamoDBContext.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/IDynamoDBContext.cs
index d7a18828685c..557af941bc93 100644
--- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/IDynamoDBContext.cs
+++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/IDynamoDBContext.cs
@@ -316,7 +316,7 @@ public partial interface IDynamoDBContext : IDisposable
///
/// Type of objects to write.
/// Empty strongly-typed TransactWrite object.
- TransactWrite CreateTransactWrite();
+ ITransactWrite CreateTransactWrite();
///
/// Creates a strongly-typed TransactWrite object, allowing
@@ -326,7 +326,7 @@ public partial interface IDynamoDBContext : IDisposable
/// Config object which can be used to override that table used.
/// Empty strongly-typed TransactWrite object.
[Obsolete("Use the CreateTransactWrite overload that takes TransactWriteConfig instead, since DynamoDBOperationConfig contains properties that are not applicable to CreateTransactWrite.")]
- TransactWrite CreateTransactWrite(DynamoDBOperationConfig operationConfig = null);
+ ITransactWrite CreateTransactWrite(DynamoDBOperationConfig operationConfig = null);
///
/// Creates a strongly-typed TransactWrite object, allowing
@@ -335,7 +335,7 @@ public partial interface IDynamoDBContext : IDisposable
/// Type of objects to write.
/// Config object that can be used to override properties on the table's context for this request.
/// Empty strongly-typed TransactWrite object.
- TransactWrite CreateTransactWrite(TransactWriteConfig transactWriteConfig);
+ ITransactWrite CreateTransactWrite(TransactWriteConfig transactWriteConfig);
///
/// Creates a MultiTableTransactWrite object, composed of multiple
@@ -343,7 +343,7 @@ public partial interface IDynamoDBContext : IDisposable
///
/// Individual TransactWrite objects.
/// Composite MultiTableTransactWrite object.
- MultiTableTransactWrite CreateMultiTableTransactWrite(params TransactWrite[] transactionParts);
+ IMultiTableTransactWrite CreateMultiTableTransactWrite(params ITransactWrite[] transactionParts);
#endregion
}
diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/TransactWrite.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/TransactWrite.cs
index 5aa70cea7464..42ff4668e246 100644
--- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/TransactWrite.cs
+++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/TransactWrite.cs
@@ -24,53 +24,148 @@
namespace Amazon.DynamoDBv2.DataModel
{
+
///
- /// Represents a non-generic object for writing/deleting/version-checking multiple items
+ /// Represents a non-generic interface for writing/deleting/version-checking multiple items
/// in a single DynamoDB table in a transaction.
///
- public abstract partial class TransactWrite
+ public partial interface ITransactWrite
{
- #region Internal/protected properties
+ }
- internal DynamoDBContext Context { get; set; }
- internal DynamoDBFlatConfig Config { get; set; }
- internal DocumentTransactWrite DocumentTransaction { get; set; }
+ ///
+ /// Represents a generic interface for writing/deleting/version-checking multiple items
+ /// in a single DynamoDB table in a transaction.
+ ///
+ public interface ITransactWrite : ITransactWrite
+ {
+ ///
+ /// Creates a MultiTableTransactWrite object that is a combination
+ /// of the current TransactWrite and the specified TransactWrites.
+ ///
+ /// Other TransactWrite objects.
+ ///
+ /// MultiTableTransactWrite consisting of the multiple TransactWrite objects:
+ /// the current TransactWrite object and the passed-in TransactWrite objects.
+ ///
+ MultiTableTransactWrite Combine(params TransactWrite[] otherTransactionParts);
- #endregion
+ ///
+ /// Add a number of items to be saved in the current transaction operation.
+ ///
+ /// Items to save.
+ void AddSaveItems(IEnumerable values);
+ ///
+ /// Add a single item to be saved in the current transaction operation.
+ ///
+ /// Item to save.
+ void AddSaveItem(T item);
- #region Constructor
+ ///
+ /// Add a single item to be saved in the current transaction operation.
+ /// Item is identified by its hash primary key and will be updated using the update expression provided.
+ ///
+ /// Hash key of the item to delete.
+ /// Update expression to use.
+ /// Condition to check before the operation.
+ void AddSaveItem(object hashKey, Expression updateExpression, Expression conditionExpression = null);
- internal TransactWrite(DynamoDBContext context, DynamoDBFlatConfig config)
- {
- Context = context;
- Config = config;
- }
+ ///
+ /// Add a single item to be saved in the current transaction operation.
+ /// Item is identified by its hash-and-range primary key and will be updated using the update expression provided.
+ ///
+ /// Hash key of the item to delete.
+ /// Range key of the item to delete.
+ /// Update expression to use.
+ /// Condition to check before the operation.
+ void AddSaveItem(object hashKey, object rangeKey, Expression updateExpression, Expression conditionExpression = null);
- #endregion
+ ///
+ /// Add a number of items to be deleted in the current transaction operation.
+ ///
+ /// Items to be deleted.
+ void AddDeleteItems(IEnumerable values);
+ ///
+ /// Add a single item to be deleted in the current transaction operation.
+ ///
+ /// Item to be deleted.
+ void AddDeleteItem(T item);
- #region Protected methods
+ ///
+ /// Add a single item to be deleted in the current transaction operation.
+ /// Item is identified by its hash primary key.
+ ///
+ /// Hash key of the item to delete.
+ void AddDeleteKey(object hashKey);
///
- /// Executes a server call to write/delete/version-check the items requested in a transaction.
+ /// Add a single item to be deleted in the current transaction operation.
+ /// Item is identified by its hash primary key.
///
- protected internal abstract void ExecuteHelper();
+ /// Hash key of the item to delete.
+ /// Condition to check before the operation.
+ void AddDeleteKey(object hashKey, Expression conditionExpression);
+
+ ///
+ /// Add a single item to be deleted in the current transaction operation.
+ /// Item is identified by its hash-and-range primary key.
+ ///
+ /// Hash key of the item to delete.
+ /// Range key of the item to delete.
+ void AddDeleteKey(object hashKey, object rangeKey);
-#if AWS_ASYNC_API
///
- /// Executes an asynchronous server call to write/delete/version-check the items requested in a transaction.
+ /// Add a single item to be deleted in the current transaction operation.
+ /// Item is identified by its hash-and-range primary key.
///
- protected internal abstract Task ExecuteHelperAsync(CancellationToken cancellationToken);
-#endif
- #endregion
+ /// Hash key of the item to delete.
+ /// Range key of the item to delete.
+ /// Condition to check before the operation.
+ void AddDeleteKey(object hashKey, object rangeKey, Expression conditionExpression);
+ ///
+ /// Add a single item to be version checked in the current transaction operation.
+ /// The item must have a single property marked with the DynamoDBVersionAttribute.
+ ///
+ /// Item to be version checked.
+ void AddVersionCheckItem(T item);
- #region Internal methods
+ ///
+ /// Add a number of items to be version checked in the current transaction operation.
+ /// All items must have a single property marked with the DynamoDBVersionAttribute.
+ ///
+ /// Items to be version checked.
+ void AddVersionCheckItems(IEnumerable items);
- internal abstract void PopulateObjects();
+ ///
+ /// Add a single item to be version checked in the current transaction operation.
+ /// Item is identified by its hash primary key.
+ ///
+ /// Hash key of the item to be version checked.
+ /// Version of the item.
+ void AddVersionCheckKey(object hashKey, object version);
+
+ ///
+ /// Add a single item to be version checked in the current transaction operation.
+ /// Item is identified by its hash-and-range primary key.
+ ///
+ /// Hash key of the item to be version checked.
+ /// Range key of the item to be version checked.
+ /// Version of the item.
+ void AddVersionCheckKey(object hashKey, object rangeKey, object version);
+ }
+
+ ///
+ /// Represents a non-generic object for writing/deleting/version-checking multiple items
+ /// in a single DynamoDB table in a transaction.
+ ///
+ public abstract partial class TransactWrite : ITransactWrite
+ {
+ internal DocumentTransactWrite DocumentTransaction { get; set; }
- #endregion
+ internal abstract void PopulateObjects();
}
///
@@ -80,33 +175,29 @@ internal TransactWrite(DynamoDBContext context, DynamoDBFlatConfig config)
#if NET8_0_OR_GREATER
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode(Amazon.DynamoDBv2.Custom.Internal.InternalConstants.RequiresUnreferencedCodeMessage)]
#endif
- public class TransactWrite : TransactWrite
+ public partial class TransactWrite : TransactWrite, ITransactWrite
{
- #region Public Combine methods
+ private readonly DynamoDBContext _context;
+ private readonly DynamoDBFlatConfig _config;
+ private readonly ItemStorageConfig _storageConfig;
+ private readonly List _objectItems = new();
- ///
- /// Creates a MultiTableTransactWrite object that is a combination
- /// of the current TransactWrite and the specified TransactWrites.
- ///
- /// Other TransactWrite objects.
- ///
- /// MultiTableTransactWrite consisting of the multiple TransactWrite objects:
- /// the current TransactWrite object and the passed-in TransactWrite objects.
- ///
+ internal TransactWrite(DynamoDBContext context, DynamoDBFlatConfig config)
+ {
+ _context = context;
+ _config = config;
+ _storageConfig = context.StorageConfigCache.GetConfig(config);
+ Table table = _context.GetTargetTable(_storageConfig, _config);
+ DocumentTransaction = table.CreateTransactWrite();
+ }
+
+ ///
public MultiTableTransactWrite Combine(params TransactWrite[] otherTransactionParts)
{
return new MultiTableTransactWrite(this, otherTransactionParts);
}
- #endregion
-
-
- #region Public Save methods
-
- ///
- /// Add a number of items to be saved in the current transaction operation.
- ///
- /// Items to save.
+ ///
public void AddSaveItems(IEnumerable values)
{
if (values == null) return;
@@ -117,15 +208,12 @@ public void AddSaveItems(IEnumerable values)
}
}
- ///
- /// Add a single item to be saved in the current transaction operation.
- ///
- /// Item to save.
+ ///
public void AddSaveItem(T item)
{
if (item == null) return;
- ItemStorage storage = Context.ObjectToItemStorageHelper(item, StorageConfig, Config, keysOnly: false, Config.IgnoreNullValues ?? false);
+ ItemStorage storage = _context.ObjectToItemStorageHelper(item, _storageConfig, _config, keysOnly: false, _config.IgnoreNullValues ?? false);
if (storage == null) return;
Expression conditionExpression = CreateConditionExpressionForVersion(storage);
SetNewVersion(storage);
@@ -140,29 +228,16 @@ public void AddSaveItem(T item)
OriginalObject = item,
ItemStorage = storage
};
- objectItems.Add(objectItem);
+ _objectItems.Add(objectItem);
}
- ///
- /// Add a single item to be saved in the current transaction operation.
- /// Item is identified by its hash primary key and will be updated using the update expression provided.
- ///
- /// Hash key of the item to delete.
- /// Update expression to use.
- /// Condition to check before the operation.
+ ///
public void AddSaveItem(object hashKey, Expression updateExpression, Expression conditionExpression = null)
{
AddSaveItem(hashKey, rangeKey: null, updateExpression, conditionExpression);
}
- ///
- /// Add a single item to be saved in the current transaction operation.
- /// Item is identified by its hash-and-range primary key and will be updated using the update expression provided.
- ///
- /// Hash key of the item to delete.
- /// Range key of the item to delete.
- /// Update expression to use.
- /// Condition to check before the operation.
+ ///
public void AddSaveItem(object hashKey, object rangeKey, Expression updateExpression, Expression conditionExpression = null)
{
var operationConfig = conditionExpression != null
@@ -173,18 +248,10 @@ public void AddSaveItem(object hashKey, object rangeKey, Expression updateExpres
}
: null;
- DocumentTransaction.AddDocumentToUpdateHelper(Context.MakeKey(hashKey, rangeKey, StorageConfig, Config), updateExpression, operationConfig);
+ DocumentTransaction.AddDocumentToUpdateHelper(_context.MakeKey(hashKey, rangeKey, _storageConfig, _config), updateExpression, operationConfig);
}
- #endregion
-
-
- #region Public Delete methods
-
- ///
- /// Add a number of items to be deleted in the current transaction operation.
- ///
- /// Items to be deleted.
+ ///
public void AddDeleteItems(IEnumerable values)
{
if (values == null) return;
@@ -195,15 +262,12 @@ public void AddDeleteItems(IEnumerable values)
}
}
- ///
- /// Add a single item to be deleted in the current transaction operation.
- ///
- /// Item to be deleted.
+ ///
public void AddDeleteItem(T item)
{
if (item == null) return;
- ItemStorage storage = Context.ObjectToItemStorageHelper(item, StorageConfig, Config, keysOnly: true, Config.IgnoreNullValues ?? false);
+ ItemStorage storage = _context.ObjectToItemStorageHelper(item, _storageConfig, _config, keysOnly: true, _config.IgnoreNullValues ?? false);
if (storage == null) return;
Expression conditionExpression = CreateConditionExpressionForVersion(storage);
@@ -214,45 +278,25 @@ public void AddDeleteItem(T item)
});
}
- ///
- /// Add a single item to be deleted in the current transaction operation.
- /// Item is identified by its hash primary key.
- ///
- /// Hash key of the item to delete.
+ ///
public void AddDeleteKey(object hashKey)
{
AddDeleteKey(hashKey, conditionExpression: null);
}
- ///
- /// Add a single item to be deleted in the current transaction operation.
- /// Item is identified by its hash primary key.
- ///
- /// Hash key of the item to delete.
- /// Condition to check before the operation.
+ ///
public void AddDeleteKey(object hashKey, Expression conditionExpression)
{
AddDeleteKey(hashKey, rangeKey: null, conditionExpression);
}
- ///
- /// Add a single item to be deleted in the current transaction operation.
- /// Item is identified by its hash-and-range primary key.
- ///
- /// Hash key of the item to delete.
- /// Range key of the item to delete.
+ ///
public void AddDeleteKey(object hashKey, object rangeKey)
{
AddDeleteKey(hashKey, rangeKey, conditionExpression: null);
}
- ///
- /// Add a single item to be deleted in the current transaction operation.
- /// Item is identified by its hash-and-range primary key.
- ///
- /// Hash key of the item to delete.
- /// Range key of the item to delete.
- /// Condition to check before the operation.
+ ///
public void AddDeleteKey(object hashKey, object rangeKey, Expression conditionExpression)
{
var operationConfig = conditionExpression != null
@@ -263,26 +307,17 @@ public void AddDeleteKey(object hashKey, object rangeKey, Expression conditionEx
}
: null;
- DocumentTransaction.AddKeyToDeleteHelper(Context.MakeKey(hashKey, rangeKey, StorageConfig, Config), operationConfig);
+ DocumentTransaction.AddKeyToDeleteHelper(_context.MakeKey(hashKey, rangeKey, _storageConfig, _config), operationConfig);
}
- #endregion
-
-
- #region Public VersionCheck methods
-
- ///
- /// Add a single item to be version checked in the current transaction operation.
- /// The item must have a single property marked with the DynamoDBVersionAttribute.
- ///
- /// Item to be version checked.
+ ///
public void AddVersionCheckItem(T item)
{
CheckUseVersioning();
if (item == null) return;
- ItemStorage storage = Context.ObjectToItemStorageHelper(item, StorageConfig, Config, keysOnly: true, Config.IgnoreNullValues ?? false);
+ ItemStorage storage = _context.ObjectToItemStorageHelper(item, _storageConfig, _config, keysOnly: true, _config.IgnoreNullValues ?? false);
if (storage == null) return;
Expression conditionExpression = CreateConditionExpressionForVersion(storage);
@@ -293,11 +328,7 @@ public void AddVersionCheckItem(T item)
});
}
- ///
- /// Add a number of items to be version checked in the current transaction operation.
- /// All items must have a single property marked with the DynamoDBVersionAttribute.
- ///
- /// Items to be version checked.
+ ///
public void AddVersionCheckItems(IEnumerable items)
{
foreach (var item in items)
@@ -306,40 +337,29 @@ public void AddVersionCheckItems(IEnumerable items)
}
}
- ///
- /// Add a single item to be version checked in the current transaction operation.
- /// Item is identified by its hash primary key.
- ///
- /// Hash key of the item to be version checked.
- /// Version of the item.
+ ///
public void AddVersionCheckKey(object hashKey, object version)
{
AddVersionCheckKey(hashKey, rangeKey: null, version);
}
- ///
- /// Add a single item to be version checked in the current transaction operation.
- /// Item is identified by its hash-and-range primary key.
- ///
- /// Hash key of the item to be version checked.
- /// Range key of the item to be version checked.
- /// Version of the item.
+ ///
public void AddVersionCheckKey(object hashKey, object rangeKey, object version)
{
CheckUseVersioning();
- Key key = Context.MakeKey(hashKey, rangeKey, StorageConfig, Config);
- DynamoDBEntry versionEntry = Context.ToDynamoDBEntry(StorageConfig.VersionPropertyStorage, version, Config);
+ Key key = _context.MakeKey(hashKey, rangeKey, _storageConfig, _config);
+ DynamoDBEntry versionEntry = _context.ToDynamoDBEntry(_storageConfig.VersionPropertyStorage, version, _config);
Primitive versionPrimitive = versionEntry?.AsPrimitive();
if (versionEntry != null && versionPrimitive == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture,
"Version property {0} must be Primitive.",
- StorageConfig.VersionPropertyName));
+ _storageConfig.VersionPropertyName));
}
- ItemStorage storage = new ItemStorage(StorageConfig)
+ ItemStorage storage = new ItemStorage(_storageConfig)
{
CurrentVersion = versionPrimitive
};
@@ -353,42 +373,14 @@ public void AddVersionCheckKey(object hashKey, object rangeKey, object version)
});
}
- #endregion
-
-
- #region Constructor
-
- internal TransactWrite(DynamoDBContext context, DynamoDBFlatConfig config)
- : base(context, config)
- {
- StorageConfig = context.StorageConfigCache.GetConfig(config);
- Table table = Context.GetTargetTable(StorageConfig, Config);
- DocumentTransaction = table.CreateTransactWrite();
- }
-
- #endregion
-
-
- #region Internal/protected/private members
-
- private readonly List objectItems = new List();
-
- internal ItemStorageConfig StorageConfig { get; set; }
-
- ///
- /// Executes a server call to write/delete/version-check the items requested in a transaction.
- ///
- protected internal override void ExecuteHelper()
+ private void ExecuteHelper()
{
DocumentTransaction.ExecuteHelper();
PopulateObjects();
}
#if AWS_ASYNC_API
- ///
- /// Executes an asynchronous server call to write/delete/version-check the items requested in a transaction.
- ///
- protected internal override async Task ExecuteHelperAsync(CancellationToken cancellationToken)
+ private async Task ExecuteHelperAsync(CancellationToken cancellationToken)
{
await DocumentTransaction.ExecuteHelperAsync(cancellationToken).ConfigureAwait(false);
PopulateObjects();
@@ -397,27 +389,27 @@ protected internal override async Task ExecuteHelperAsync(CancellationToken canc
internal override void PopulateObjects()
{
- foreach (var objectItem in objectItems)
+ foreach (var objectItem in _objectItems)
{
- objectItem.PopulateObject(Context, Config);
+ objectItem.PopulateObject(_context, _config);
}
}
private bool ShouldUseVersioning()
{
- var skipVersionCheck = Config.SkipVersionCheck ?? false;
- return !skipVersionCheck && StorageConfig.HasVersion;
+ var skipVersionCheck = _config.SkipVersionCheck ?? false;
+ return !skipVersionCheck && _storageConfig.HasVersion;
}
private void CheckUseVersioning()
{
- if (Config.SkipVersionCheck == true)
+ if (_config.SkipVersionCheck == true)
{
throw new InvalidOperationException(
"Using DynamoDBContextConfig.SkipVersionCheck property with true value is not supported for this operation.");
}
- if (!StorageConfig.HasVersion)
+ if (!_storageConfig.HasVersion)
{
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture,
"Object {0} does not have a versioning field, which is not supported for this operation.",
@@ -439,86 +431,86 @@ private void SetNewVersion(ItemStorage storage)
if (!ShouldUseVersioning()) return;
DynamoDBContext.SetNewVersion(storage);
}
+ }
- #endregion
+ ///
+ /// Interface for writing/deleting/version-checking multiple items in multiple DynamoDB tables,
+ /// using multiple strongly-typed TransactWrite objects.
+ ///
+ public partial interface IMultiTableTransactWrite
+ {
+ ///
+ /// Add a TransactWrite object to the multi-table transaction request.
+ ///
+ /// TransactWrite to add.
+ void AddTransactionPart(ITransactWrite transactionPart);
}
///
/// Class for writing/deleting/version-checking multiple items in multiple DynamoDB tables,
/// using multiple strongly-typed TransactWrite objects.
///
- public partial class MultiTableTransactWrite
+ public partial class MultiTableTransactWrite : IMultiTableTransactWrite
{
- #region Private members
-
- private readonly List allTransactionParts;
-
- #endregion
-
-
- #region Constructor
+ private readonly List allTransactionParts;
///
/// Constructs a MultiTableTransactWrite object from a number of
/// TransactWrite objects
///
/// Collection of TransactWrite objects
- public MultiTableTransactWrite(params TransactWrite[] transactionParts)
+ public MultiTableTransactWrite(params ITransactWrite[] transactionParts)
{
- allTransactionParts = new List(transactionParts);
+ allTransactionParts = new List(transactionParts);
}
- internal MultiTableTransactWrite(TransactWrite first, params TransactWrite[] rest)
+ internal MultiTableTransactWrite(ITransactWrite first, params ITransactWrite[] rest)
{
- allTransactionParts = new List();
+ allTransactionParts = new List();
allTransactionParts.Add(first);
allTransactionParts.AddRange(rest);
}
- #endregion
-
-
- #region Public methods
-
- ///
- /// Add a TransactWrite object to the multi-table transaction request.
- ///
- /// TransactWrite to add.
- public void AddTransactionPart(TransactWrite transactionPart)
+ ///
+ public void AddTransactionPart(ITransactWrite transactionPart)
{
allTransactionParts.Add(transactionPart);
}
- internal void ExecuteHelper()
+ private void ExecuteHelper()
{
MultiTableDocumentTransactWrite transaction = new MultiTableDocumentTransactWrite();
+ var errMsg = $"All transactionParts must be of type {nameof(TransactWrite)}";
foreach (var transactionPart in allTransactionParts)
{
- transaction.AddTransactionPart(transactionPart.DocumentTransaction);
+ var abstractTransactWrite = transactionPart as TransactWrite ?? throw new InvalidOperationException(errMsg);
+ transaction.AddTransactionPart(abstractTransactWrite.DocumentTransaction);
}
transaction.ExecuteHelper();
foreach (var transactionPart in allTransactionParts)
{
- transactionPart.PopulateObjects();
+ var abstractTransactWrite = transactionPart as TransactWrite ?? throw new InvalidOperationException(errMsg);
+ abstractTransactWrite.PopulateObjects();
}
}
#if AWS_ASYNC_API
- internal async Task ExecuteHelperAsync(CancellationToken cancellationToken)
+ private async Task ExecuteHelperAsync(CancellationToken cancellationToken)
{
MultiTableDocumentTransactWrite transaction = new MultiTableDocumentTransactWrite();
+ var errMsg = $"All transactionParts must be of type {nameof(TransactWrite)}";
foreach (var transactionPart in allTransactionParts)
{
- transaction.AddTransactionPart(transactionPart.DocumentTransaction);
+ var abstractTransactWrite = transactionPart as TransactWrite ?? throw new InvalidOperationException(errMsg);
+ transaction.AddTransactionPart(abstractTransactWrite.DocumentTransaction);
}
await transaction.ExecuteHelperAsync(cancellationToken).ConfigureAwait(false);
foreach (var transactionPart in allTransactionParts)
{
- transactionPart.PopulateObjects();
+ var abstractTransactWrite = transactionPart as TransactWrite ?? throw new InvalidOperationException(errMsg);
+ abstractTransactWrite.PopulateObjects();
}
}
#endif
-
- #endregion
}
}
diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/_async/Context.Async.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/_async/Context.Async.cs
index a408abb50e5a..84a255cc9eda 100644
--- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/_async/Context.Async.cs
+++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/_async/Context.Async.cs
@@ -239,13 +239,8 @@ public Task ExecuteBatchGetAsync(params IBatchGet[] batches)
#region TransactWrite async
- ///
- /// Issues a transactional write request with multiple TransactWrite objects.
- ///
- /// Configured TransactWrite objects.
- /// Token which can be used to cancel the task.
- /// A Task that can be used to poll or wait for results, or both.
- public Task ExecuteTransactWriteAsync(TransactWrite[] transactionParts, CancellationToken cancellationToken = default(CancellationToken))
+ ///
+ public Task ExecuteTransactWriteAsync(ITransactWrite[] transactionParts, CancellationToken cancellationToken = default(CancellationToken))
{
MultiTableTransactWrite transaction = new MultiTableTransactWrite(transactionParts);
return transaction.ExecuteAsync(cancellationToken);
diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/_async/IDynamoDBContext.Async.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/_async/IDynamoDBContext.Async.cs
index 1417c11689c8..22ad5936c4ea 100644
--- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/_async/IDynamoDBContext.Async.cs
+++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/_async/IDynamoDBContext.Async.cs
@@ -467,7 +467,7 @@ partial interface IDynamoDBContext
/// Configured TransactWrite objects.
/// Token which can be used to cancel the task.
/// A Task that can be used to poll or wait for results, or both.
- Task ExecuteTransactWriteAsync(TransactWrite[] transactionParts, CancellationToken cancellationToken = default(CancellationToken));
+ Task ExecuteTransactWriteAsync(ITransactWrite[] transactionParts, CancellationToken cancellationToken = default(CancellationToken));
#endregion
diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/_async/TransactWrite.Async.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/_async/TransactWrite.Async.cs
index f905dd149298..4910c851591d 100644
--- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/_async/TransactWrite.Async.cs
+++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/_async/TransactWrite.Async.cs
@@ -18,46 +18,48 @@
namespace Amazon.DynamoDBv2.DataModel
{
- ///
- /// Represents a non-generic object for writing/deleting/version-checking multiple items
- /// in a single DynamoDB table in a transaction.
- ///
- public abstract partial class TransactWrite
+ public partial interface ITransactWrite
{
- #region Public methods
-
///
/// Executes a server call to write/delete/version-check the items requested in a transaction.
///
/// Token which can be used to cancel the task.
/// A Task that can be used to poll or wait for results, or both.
- public Task ExecuteAsync(CancellationToken cancellationToken = default(CancellationToken))
+ Task ExecuteAsync(CancellationToken cancellationToken = default(CancellationToken));
+ }
+
+ public abstract partial class TransactWrite : ITransactWrite
+ {
+ ///
+ public abstract Task ExecuteAsync(CancellationToken cancellationToken = default(CancellationToken));
+ }
+
+ public partial class TransactWrite : TransactWrite, ITransactWrite
+ {
+ ///
+ public override Task ExecuteAsync(CancellationToken cancellationToken = default(CancellationToken))
{
return ExecuteHelperAsync(cancellationToken);
}
-
- #endregion
}
- ///
- /// Class for writing/deleting/version-checking multiple items in multiple DynamoDB tables,
- /// using multiple strongly-typed TransactWrite objects.
- ///
- public partial class MultiTableTransactWrite
+ public partial interface IMultiTableTransactWrite
{
- #region Public methods
-
///
/// Executes a multi-table transaction request against all configured TransactWrite objects.
///
/// Token which can be used to cancel the task.
///
/// A Task that can be used to poll or wait for results, or both.
+ public Task ExecuteAsync(CancellationToken cancellationToken = default(CancellationToken));
+ }
+
+ public partial class MultiTableTransactWrite : IMultiTableTransactWrite
+ {
+ ///
public Task ExecuteAsync(CancellationToken cancellationToken = default(CancellationToken))
{
return ExecuteHelperAsync(cancellationToken);
}
-
- #endregion
}
}
diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/_bcl/Context.Sync.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/_bcl/Context.Sync.cs
index ea57d246b026..d60be12972e2 100644
--- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/_bcl/Context.Sync.cs
+++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/_bcl/Context.Sync.cs
@@ -206,11 +206,8 @@ public void ExecuteBatchWrite(params IBatchWrite[] batches)
#region Transact Write
- ///
- /// Issues a transactional write request with multiple TransactWrite objects.
- ///
- /// Configured TransactWrite objects.
- public void ExecuteTransactWrite(params TransactWrite[] transactionParts)
+ ///
+ public void ExecuteTransactWrite(params ITransactWrite[] transactionParts)
{
MultiTableTransactWrite transaction = new MultiTableTransactWrite(transactionParts);
transaction.Execute();
diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/_bcl/IDynamoDBContext.Sync.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/_bcl/IDynamoDBContext.Sync.cs
index db4c41c25729..207c4a241b37 100644
--- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/_bcl/IDynamoDBContext.Sync.cs
+++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/_bcl/IDynamoDBContext.Sync.cs
@@ -396,7 +396,7 @@ partial interface IDynamoDBContext
/// Issues a transactional write request with multiple TransactWrite objects.
///
/// Configured TransactWrite objects.
- void ExecuteTransactWrite(params TransactWrite[] transactionParts);
+ void ExecuteTransactWrite(params ITransactWrite[] transactionParts);
#endregion
diff --git a/sdk/src/Services/DynamoDBv2/Custom/DataModel/_bcl/TransactWrite.Sync.cs b/sdk/src/Services/DynamoDBv2/Custom/DataModel/_bcl/TransactWrite.Sync.cs
index bb6242eaff20..4b166e850d38 100644
--- a/sdk/src/Services/DynamoDBv2/Custom/DataModel/_bcl/TransactWrite.Sync.cs
+++ b/sdk/src/Services/DynamoDBv2/Custom/DataModel/_bcl/TransactWrite.Sync.cs
@@ -15,41 +15,43 @@
namespace Amazon.DynamoDBv2.DataModel
{
- ///
- /// Represents a non-generic object for writing/deleting/version-checking multiple items
- /// in a single DynamoDB table in a transaction.
- ///
- public abstract partial class TransactWrite
+ public partial interface ITransactWrite
{
- #region Public methods
-
///
/// Executes a server call to write/delete/version-check the items requested in a transaction.
///
- public void Execute()
+ void Execute();
+ }
+
+ public abstract partial class TransactWrite : ITransactWrite
+ {
+ ///
+ public abstract void Execute();
+ }
+
+ public partial class TransactWrite : TransactWrite, ITransactWrite
+ {
+ ///
+ public override void Execute()
{
ExecuteHelper();
}
-
- #endregion
}
- ///
- /// Class for writing/deleting/version-checking multiple items in multiple DynamoDB tables,
- /// using multiple strongly-typed TransactWrite objects.
- ///
- public partial class MultiTableTransactWrite
+ public partial interface IMultiTableTransactWrite
{
- #region Public methods
-
///
/// Executes a multi-table transaction request against all configured TransactWrite objects.
///
+ void Execute();
+ }
+
+ public partial class MultiTableTransactWrite : IMultiTableTransactWrite
+ {
+ ///
public void Execute()
{
ExecuteHelper();
}
-
- #endregion
}
}
diff --git a/sdk/test/Services/DynamoDBv2/UnitTests/Custom/MockabilityTests/TransactWriteTests.cs b/sdk/test/Services/DynamoDBv2/UnitTests/Custom/MockabilityTests/TransactWriteTests.cs
new file mode 100644
index 000000000000..24eef4f9602f
--- /dev/null
+++ b/sdk/test/Services/DynamoDBv2/UnitTests/Custom/MockabilityTests/TransactWriteTests.cs
@@ -0,0 +1,121 @@
+using Amazon.DynamoDBv2.DataModel;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using System.Collections.Generic;
+
+namespace AWSSDK.UnitTests.DynamoDBv2.NetFramework.Custom.MockabilityTests
+{
+ [TestClass]
+ public class TransactWriteTests
+ {
+ [TestMethod]
+ public void TestMockability_TransactWrite()
+ {
+ var itemsToSave = new List();
+ var inMemoryTable = new List();
+
+ var mockContext = new Mock();
+ mockContext
+ .Setup(x => x.CreateTransactWrite())
+ .Returns(CreateTransactWriteMock(itemsToSave, inMemoryTable));
+
+ var ddbContext = mockContext.Object;
+ var transactWrite = ddbContext.CreateTransactWrite();
+
+ Assert.AreEqual(0, inMemoryTable.Count);
+ Assert.AreEqual(0, itemsToSave.Count);
+
+ transactWrite.AddSaveItem("item1");
+ transactWrite.AddSaveItem("item2");
+ Assert.AreEqual(2, itemsToSave.Count);
+ Assert.AreEqual(0, inMemoryTable.Count);
+
+ transactWrite.Execute();
+ Assert.AreEqual(0, itemsToSave.Count);
+ Assert.AreEqual(2, inMemoryTable.Count);
+ Assert.IsTrue(inMemoryTable.Contains("item1"));
+ Assert.IsTrue(inMemoryTable.Contains("item2"));
+ }
+
+ [TestMethod]
+ public void TestMockability_MultiTableTransactWrite()
+ {
+ var itemsToSave_table1 = new List();
+ var inMemory_table1 = new List();
+ var transactWrite_table1 = CreateTransactWriteMock(itemsToSave_table1, inMemory_table1);
+ transactWrite_table1.AddSaveItem("item1");
+
+ var itemsToSave_table2 = new List();
+ var inMemory_table2 = new List();
+ var transactWrite_table2 = CreateTransactWriteMock(itemsToSave_table2, inMemory_table2);
+ transactWrite_table2.AddSaveItem("item2");
+
+ var mockContext = new Mock();
+ mockContext
+ .Setup(x => x.CreateMultiTableTransactWrite())
+ .Returns(CreateMultiTableTransactWriteMock());
+
+ var ddbContext = mockContext.Object;
+ var multiTransactWrite = ddbContext.CreateMultiTableTransactWrite();
+ multiTransactWrite.AddTransactionPart(transactWrite_table1);
+ multiTransactWrite.AddTransactionPart(transactWrite_table2);
+
+ Assert.AreEqual(0, inMemory_table1.Count);
+ Assert.AreEqual(0, inMemory_table2.Count);
+
+ multiTransactWrite.Execute();
+ Assert.AreEqual(1, inMemory_table1.Count);
+ Assert.AreEqual(1, inMemory_table2.Count);
+ Assert.IsTrue(inMemory_table1.Contains("item1"));
+ Assert.IsTrue(inMemory_table2.Contains("item2"));
+ }
+
+ public ITransactWrite CreateTransactWriteMock(List itemsToSave, List inMemoryTable)
+ {
+ var transactWrite = new Mock>();
+
+ transactWrite
+ .Setup(x => x.AddSaveItem(It.IsAny()))
+ .Callback((T item) => itemsToSave.Add(item));
+
+ transactWrite.
+ Setup(x => x.Execute())
+ .Callback(() =>
+ {
+ foreach (var item in itemsToSave)
+ {
+ inMemoryTable.Add(item);
+ }
+
+ itemsToSave.Clear();
+ });
+
+ return transactWrite.Object;
+ }
+
+ public IMultiTableTransactWrite CreateMultiTableTransactWriteMock()
+ {
+ var multiTransactWrite = new Mock();
+ var transactionParts = new List();
+
+ multiTransactWrite
+ .Setup(x => x.AddTransactionPart(It.IsAny()))
+ .Callback((ITransactWrite transactionPart) =>
+ {
+ transactionParts.Add(transactionPart);
+ });
+
+ multiTransactWrite
+ .Setup(x => x.Execute())
+ .Callback(() =>
+ {
+ foreach (var batch in transactionParts)
+ {
+ batch.Execute();
+ }
+ });
+
+ return multiTransactWrite.Object;
+ }
+ }
+}