diff --git a/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/ExpressionBuilder.cs b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/ExpressionBuilder.cs
new file mode 100644
index 000000000000..c6023d437269
--- /dev/null
+++ b/sdk/src/Services/DynamoDBv2/Custom/DocumentModel/ExpressionBuilder.cs
@@ -0,0 +1,1595 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+/*
+ * This file contains classes and utilities for building expressions used in DynamoDB operations.
+ * It provides a flexible and extensible framework for constructing conditional, filter, and query expressions
+ * using attribute names, values, and logical operators.
+ */
+
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using Amazon.Runtime;
+
+namespace Amazon.DynamoDBv2.DocumentModel
+{
+ ///
+ /// Interface for building expressions used in DynamoDB operations.
+ ///
+ public interface IExpressionBuilder
+ {
+ ///
+ /// Builds the expression based on the conditions and operands provided.
+ ///
+ /// An object representing the constructed expression.
+ Expression Build();
+ }
+
+ ///
+ /// Abstract base class for building expressions used in DynamoDB operations.
+ /// Provides a structure for managing conditions and operands that form the basis of an expression.
+ ///
+ public abstract class ExpressionBuilder: IExpressionBuilder
+ {
+ ///
+ /// Builds the expression based on provided.
+ ///
+ /// An object representing the constructed expression.
+ public Expression Build()
+ {
+ var expressionTree = BuildExpressionTree(out string expressionType);
+
+ var aliasList = new KeyAttributeAliasList();
+
+ var expression = new Expression()
+ {
+ ExpressionStatement = expressionTree.BuildExpressionString(aliasList, expressionType)
+ };
+
+ if (aliasList.NamesList != null && aliasList.NamesList.Count != 0)
+ {
+ var namesDictionary = new Dictionary();
+ for (int i = 0; i < aliasList.NamesList.Count; i++)
+ {
+ namesDictionary[$"#{expressionType}{i}"] = aliasList.NamesList[i];
+ }
+
+ expression.ExpressionAttributeNames = namesDictionary;
+ }
+
+ if (aliasList.ValuesList != null && aliasList.ValuesList.Count != 0)
+ {
+ var values = new Dictionary();
+ for (int i = 0; i < aliasList.ValuesList.Count; i++)
+ {
+ values[$":{expressionType}{i}"] = aliasList.ValuesList[i];
+ }
+
+ expression.ExpressionAttributeValues = values;
+ }
+
+ return expression;
+ }
+
+ ///
+ /// Builds the expression tree for the current expression builder.
+ ///
+ ///
+ ///
+ internal abstract ExpressionNode BuildExpressionTree(out string s);
+ }
+
+ ///
+ /// The class is used to construct update expressions for DynamoDB operations.
+ /// An update expression consists of one or more clauses. Each clause begins with a SET, REMOVE, ADD, or DELETE keyword.
+ /// You can include any of these clauses in an update expression, in any order.
+ ///
+ public class UpdateExpressionBuilder : ExpressionBuilder
+ {
+
+ private const string OperationModeSet = "SET";
+ private const string OperationModeRemove = "REMOVE";
+ private const string OperationModeAdd = "ADD";
+ private const string OperationModeDelete = "DELETE";
+
+ ///
+ /// The list of operations that will be used in the update expression.
+ ///
+ private Dictionary> OperationBuilders { get; set; }
+
+ ///
+ /// Initializes a new instance of the class with default settings.
+ ///
+ protected UpdateExpressionBuilder()
+ {
+ OperationBuilders = new Dictionary>();
+ }
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// A new instance for building update expressions.
+ public static UpdateExpressionBuilder New()
+ {
+ return new UpdateExpressionBuilder();
+ }
+
+ ///
+ /// Adds a "SET" operation to the update expression, which sets the value of an attribute.
+ ///
+ /// The representing the attribute name.
+ /// The representing the value to set.
+ /// The current instance for chaining additional operations.
+ public UpdateExpressionBuilder Set(NameBuilder nameBuilder, OperandBuilder setExpressionBuilder)
+ {
+ if (!OperationBuilders.TryGetValue(OperationModeSet, out var ops))
+ {
+ ops = new List();
+ OperationBuilders[OperationModeSet] = ops;
+ }
+
+ ops.Add(new OperationBuilder(OperationMode.Set)
+ {
+ Name = nameBuilder,
+ Value = setExpressionBuilder
+ });
+
+ return this;
+ }
+
+ ///
+ /// Adds a "REMOVE" operation to the update expression, which removes one or more attributes from an item.
+ ///
+ /// The representing the attribute name to remove.
+ /// The current instance for chaining additional operations.
+ public UpdateExpressionBuilder Remove(NameBuilder nameBuilder)
+ {
+ if (!OperationBuilders.TryGetValue(OperationModeRemove, out var ops))
+ {
+ ops = new List();
+ OperationBuilders[OperationModeRemove] = ops;
+ }
+
+ ops.Add(new OperationBuilder(OperationMode.Remove)
+ {
+ Name = nameBuilder
+ });
+
+ return this;
+ }
+
+ ///
+ /// Adds a "DELETE" operation to the update expression, which removes one or more elements from a set attribute.
+ ///
+ /// The representing the attribute name.
+ /// The representing the value to delete from the set.
+ /// The current instance for chaining additional operations.
+ public UpdateExpressionBuilder Delete(NameBuilder nameBuilder, ValueBuilder valueBuilder)
+ {
+ if (!OperationBuilders.TryGetValue(OperationModeDelete, out var ops))
+ {
+ ops = new List();
+ OperationBuilders[OperationModeDelete] = ops;
+ }
+
+ ops.Add(new OperationBuilder(OperationMode.Delete)
+ {
+ Name = nameBuilder,
+ Value = valueBuilder
+ });
+
+ return this;
+ }
+
+ ///
+ /// Adds an "ADD" operation to the update expression, which adds a new attribute or modifies an existing one.
+ /// If the attribute is a number, the value is added or subtracted. If the attribute is a set, the value is appended.
+ ///
+ /// The representing the attribute name.
+ /// The representing the value to add.
+ /// The current instance for chaining additional operations.
+ public UpdateExpressionBuilder Add(NameBuilder nameBuilder, ValueBuilder valueBuilder)
+ {
+ if (!OperationBuilders.TryGetValue(OperationModeAdd, out var ops))
+ {
+ ops = new List();
+ OperationBuilders[OperationModeAdd] = ops;
+ }
+
+ ops.Add(new OperationBuilder(OperationMode.Add)
+ {
+ Name = nameBuilder,
+ Value = valueBuilder
+ });
+
+ return this;
+ }
+
+ ///
+ /// Builds the expression tree for the update expression based on the operations provided.
+ ///
+ ///
+ /// An representing the constructed update expression tree.
+ /// Thrown if no operations are defined in the update expression.
+ internal override ExpressionNode BuildExpressionTree(out string s)
+ {
+ s = "U";
+ var resultNode = new ExpressionNode
+ {
+ Children = new Queue()
+ };
+
+ if (OperationBuilders.TryGetValue(OperationModeSet, out var setOps))
+ {
+ var childNode = BuildChildNodes(setOps);
+ resultNode.Children.Enqueue(childNode);
+ resultNode.FormatedExpression += $"{OperationModeSet} $c\n";
+ }
+
+ if (OperationBuilders.TryGetValue(OperationModeRemove, out var removeOps))
+ {
+ var childNode = BuildChildNodes(removeOps);
+ resultNode.Children.Enqueue(childNode);
+ resultNode.FormatedExpression += $"{OperationModeRemove} $c\n";
+ }
+
+ if (OperationBuilders.TryGetValue(OperationModeAdd, out var addOps))
+ {
+ var childNode = BuildChildNodes(addOps);
+ resultNode.Children.Enqueue(childNode);
+ resultNode.FormatedExpression += $"{OperationModeAdd} $c\n";
+ }
+
+ if (OperationBuilders.TryGetValue(OperationModeDelete, out var deleteOps))
+ {
+ var childNode = BuildChildNodes(deleteOps);
+ resultNode.Children.Enqueue(childNode);
+ resultNode.FormatedExpression += $"{OperationModeDelete} $c\n";
+ }
+
+ if (resultNode.Children.Count == 0)
+ {
+ throw new InvalidOperationException("Cannot build update expression tree: operation list is empty.");
+ }
+
+ return resultNode;
+ }
+
+ private ExpressionNode BuildChildNodes(List builders)
+ {
+ if (builders == null || builders.Count == 0)
+ {
+ throw new InvalidOperationException("Cannot build update expression tree: operation list is empty.");
+ }
+
+ var node = new ExpressionNode
+ {
+ Children = new Queue()
+ };
+
+ foreach (var builder in builders)
+ {
+ var expr = builder.Build();
+
+ node.Children.Enqueue(expr);
+ node.FormatedExpression += "$c, ";
+ }
+
+ // Remove trailing comma and space, if any
+ if (node.FormatedExpression.EndsWith(", "))
+ {
+ node.FormatedExpression = node.FormatedExpression.Substring(0, node.FormatedExpression.Length - 2);
+ }
+
+ return node;
+ }
+
+ }
+
+ ///
+ /// Class for building conditions used in DynamoDB expressions.
+ /// Supports comparison, logical, and function-based conditions.
+ ///
+ public class ConditionExpressionBuilder : ExpressionBuilder
+ {
+ private readonly ConditionMode _conditionMode;
+
+ ///
+ /// The list of conditions that will be used in the expression.
+ ///
+ internal List Conditions { get; set; }
+
+ ///
+ /// The list of operands that will be used in the expression.
+ ///
+ internal List Operands { get; set; }
+
+ ///
+ /// Initializes a new instance of the class with default settings.
+ ///
+ protected ConditionExpressionBuilder()
+ {
+ _conditionMode = ConditionMode.Unset;
+ Operands = new List();
+ Conditions = new List();
+ }
+
+ private ConditionExpressionBuilder(List operandBuilders, ConditionMode mode) : this()
+ {
+
+ _conditionMode = mode;
+ Operands = operandBuilders;
+ }
+
+ private ConditionExpressionBuilder(List conditionBuilders, ConditionMode mode) : this()
+ {
+ _conditionMode = mode;
+ Conditions = conditionBuilders;
+ }
+
+ ///
+ /// Creates a new instance of .
+ ///
+ /// A new instance.
+ public static ConditionExpressionBuilder New()
+ {
+ return new ConditionExpressionBuilder();
+ }
+
+ ///
+ /// Adds a condition to the current condition builder.
+ ///
+ /// The condition to add.
+ /// The current instance for chaining.
+ public ConditionExpressionBuilder WithCondition(ConditionExpressionBuilder condition)
+ {
+ Conditions.Add(condition);
+ return this;
+ }
+
+ ///
+ /// Adds an attribute name to the condition.
+ ///
+ /// The attribute name or path.
+ /// A for further configuration.
+ public NameBuilder WithName(string path)
+ {
+ return new NameBuilder(path);
+ }
+
+ ///
+ /// Combines multiple conditions using the logical "AND" operator.
+ ///
+ /// The first instance to combine.
+ /// The second instance to combine.
+ /// Additional instances to include in the combination.
+ /// A new instance representing the combined "AND" condition.
+ public static ConditionExpressionBuilder And(ConditionExpressionBuilder left, ConditionExpressionBuilder right,
+ params ConditionExpressionBuilder[] others)
+ {
+ var allConditions = new List { left, right };
+ if (others is { Length: > 0 })
+ {
+ allConditions.AddRange(others);
+ }
+
+ return new ConditionExpressionBuilder(allConditions, ConditionMode.And);
+ }
+
+ ///
+ /// Combines the current condition with additional conditions using the logical "AND" operator.
+ ///
+ /// The instance to combine with the current condition.
+ /// Additional instances to include in the combination.
+ /// A new instance representing the combined "AND" condition.
+ public ConditionExpressionBuilder And(ConditionExpressionBuilder right, params ConditionExpressionBuilder[] others)
+ {
+ var allConditions = new List { this, right };
+ if (others is { Length: > 0 })
+ {
+ allConditions.AddRange(others);
+ }
+
+ return new ConditionExpressionBuilder(allConditions, ConditionMode.And);
+ }
+
+ ///
+ /// Combines multiple conditions using the logical "OR" operator.
+ ///
+ /// The first instance to combine.
+ /// The second instance to combine.
+ /// Additional instances to include in the combination.
+ /// A new instance representing the combined "OR" condition.
+ public static ConditionExpressionBuilder Or(ConditionExpressionBuilder left, ConditionExpressionBuilder right,
+ params ConditionExpressionBuilder[] others)
+ {
+ var allConditions = new List { left, right };
+ if (others is { Length: > 0 })
+ {
+ allConditions.AddRange(others);
+ }
+
+ return new ConditionExpressionBuilder(allConditions, ConditionMode.Or);
+ }
+
+ ///
+ /// Combines the current condition with additional conditions using the logical "OR" operator.
+ ///
+ /// The instance to combine with the current condition.
+ /// Additional instances to include in the combination.
+ /// A new instance representing the combined "OR" condition.
+ public ConditionExpressionBuilder Or(ConditionExpressionBuilder right, params ConditionExpressionBuilder[] others)
+ {
+ var allConditions = new List { this, right };
+ if (others is { Length: > 0 })
+ {
+ allConditions.AddRange(others);
+ }
+
+ return new ConditionExpressionBuilder(allConditions, ConditionMode.Or);
+ }
+
+ ///
+ /// Negates a condition using the logical "NOT" operator.
+ ///
+ /// The instance to negate.
+ /// A new instance representing the negated condition.
+ public static ConditionExpressionBuilder Not(ConditionExpressionBuilder condition)
+ {
+ var allConditions = new List { condition };
+ return new ConditionExpressionBuilder(allConditions, ConditionMode.Not);
+ }
+
+ ///
+ /// Negates the current condition using the logical "NOT" operator.
+ ///
+ /// A new instance representing the negated condition.
+ public ConditionExpressionBuilder Not()
+ {
+ var allConditions = new List { this };
+ return new ConditionExpressionBuilder(allConditions, ConditionMode.Not);
+ }
+
+ internal static ConditionExpressionBuilder Equal(OperandBuilder left, OperandBuilder right)
+ {
+ var condition = new ConditionExpressionBuilder(new List { left, right }, ConditionMode.Equal);
+ return condition;
+ }
+
+ internal static ConditionExpressionBuilder NotEqual(OperandBuilder left, OperandBuilder right)
+ {
+ var condition = new ConditionExpressionBuilder(new List { left, right }, ConditionMode.NotEqual);
+ return condition;
+ }
+
+ internal static ConditionExpressionBuilder LesThan(OperandBuilder left, OperandBuilder right)
+ {
+ var condition = new ConditionExpressionBuilder(new List { left, right }, ConditionMode.LessThan);
+ return condition;
+ }
+
+ internal static ConditionExpressionBuilder LessThanOrEqual(OperandBuilder left, OperandBuilder right)
+ {
+ var condition = new ConditionExpressionBuilder(new List { left, right }, ConditionMode.LessThanOrEqual);
+ return condition;
+ }
+
+ internal static ConditionExpressionBuilder GreaterThan(OperandBuilder left, OperandBuilder right)
+ {
+ var condition = new ConditionExpressionBuilder(new List { left, right }, ConditionMode.GreaterThan);
+ return condition;
+ }
+
+ internal static ConditionExpressionBuilder GreaterThanOrEqual(OperandBuilder left, OperandBuilder right)
+ {
+ var condition = new ConditionExpressionBuilder(new List { left, right }, ConditionMode.GreaterThanOrEqual);
+ return condition;
+ }
+
+ internal static ConditionExpressionBuilder Between(OperandBuilder left, OperandBuilder lowerOperand, OperandBuilder upperOperand)
+ {
+ var condition = new ConditionExpressionBuilder(new List { left, lowerOperand, upperOperand }, ConditionMode.Between);
+ return condition;
+ }
+
+ internal static ConditionExpressionBuilder In(OperandBuilder left, OperandBuilder rightOperand, params OperandBuilder[] otherOperands)
+ {
+ var operands = new List { left, rightOperand };
+ if (otherOperands is { Length: > 0 })
+ {
+ operands.AddRange(otherOperands);
+ }
+ var condition = new ConditionExpressionBuilder(operands, ConditionMode.In);
+ return condition;
+ }
+
+ internal static ConditionExpressionBuilder AttributeExists(OperandBuilder nameOperand)
+ {
+ var condition = new ConditionExpressionBuilder(new List { nameOperand }, ConditionMode.AttributeExists);
+ return condition;
+ }
+
+ internal static ConditionExpressionBuilder AttributeNotExists(OperandBuilder nameOperand)
+ {
+ var condition = new ConditionExpressionBuilder(new List { nameOperand }, ConditionMode.AttributeNotExists);
+ return condition;
+ }
+ internal static ConditionExpressionBuilder AttributeType(OperandBuilder left, OperandBuilder right)
+ {
+ var condition = new ConditionExpressionBuilder(new List { left, right }, ConditionMode.AttributeType);
+ return condition;
+ }
+
+ internal static ConditionExpressionBuilder Contains(OperandBuilder left, OperandBuilder right)
+ {
+ var condition = new ConditionExpressionBuilder(new List { left, right }, ConditionMode.Contains);
+ return condition;
+ }
+
+ internal static ConditionExpressionBuilder BeginsWith(OperandBuilder left, OperandBuilder right)
+ {
+ var condition = new ConditionExpressionBuilder(new List { left, right }, ConditionMode.BeginsWith);
+ return condition;
+ }
+
+ ///
+ ///
+ internal override ExpressionNode BuildExpressionTree(out string s)
+ {
+ s = "C";
+ var childNodes = BuildChildNodes();
+
+ var node = new ExpressionNode
+ {
+ Children = childNodes,
+ };
+
+ return _conditionMode switch
+ {
+ ConditionMode.Equal or
+ ConditionMode.NotEqual or
+ ConditionMode.LessThan or
+ ConditionMode.LessThanOrEqual or
+ ConditionMode.GreaterThan or
+ ConditionMode.GreaterThanOrEqual =>
+ CompareBuildCondition(_conditionMode, node),
+
+ ConditionMode.And or ConditionMode.Or =>
+ CompoundBuildCondition(this, node),
+
+ ConditionMode.Not =>
+ NotBuildCondition(node),
+
+ ConditionMode.Between =>
+ BetweenBuildCondition(node),
+
+ ConditionMode.In =>
+ InBuildCondition(this, node),
+
+ ConditionMode.AttributeExists =>
+ AttributeExistsBuildCondition(node),
+
+ ConditionMode.AttributeNotExists =>
+ AttributeNotExistsBuildCondition(node),
+
+ ConditionMode.AttributeType =>
+ AttributeTypeBuildCondition(node),
+
+ ConditionMode.BeginsWith =>
+ BeginsWithBuildCondition(node),
+
+ ConditionMode.Contains =>
+ ContainsBuildCondition(node),
+
+ ConditionMode.Unset =>
+ throw new InvalidOperationException("ConditionBuilder"),
+
+ _ =>
+ throw new InvalidOperationException($"Build condition error: unsupported mode: {_conditionMode}")
+ };
+ }
+
+ private Queue BuildChildNodes()
+ {
+ var childNodes = new Queue();
+
+ foreach (var condition in Conditions)
+ {
+ ExpressionNode node;
+ try
+ {
+ node = condition.BuildExpressionTree(out _);
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException("Failed to build condition tree", ex);
+ }
+
+ childNodes.Enqueue(node);
+ }
+
+ foreach (var operand in Operands)
+ {
+ ExpressionNode node;
+ try
+ {
+ node = operand.Build();
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException("Failed to build operand", ex);
+ }
+
+ childNodes.Enqueue(node);
+ }
+
+ return childNodes;
+ }
+
+ #region statement_build
+
+ private ExpressionNode NotBuildCondition(ExpressionNode node)
+ {
+ node.FormatedExpression = "NOT ($c)";
+ return node;
+ }
+
+ private ExpressionNode CompoundBuildCondition(ConditionExpressionBuilder conditionBuilder, ExpressionNode node)
+ {
+ var mode = conditionBuilder._conditionMode switch
+ {
+ ConditionMode.And => "AND",
+ ConditionMode.Or => "OR",
+ _ => throw new InvalidOperationException(
+ $"Unsupported condition operator: {conditionBuilder._conditionMode}")
+ };
+ node.FormatedExpression = string.Join($" {mode} ",
+ node.Children.Select(c => "($c)"));
+ return node;
+ }
+
+ private ExpressionNode CompareBuildCondition(ConditionMode conditionMode, ExpressionNode node)
+ {
+ switch (conditionMode)
+ {
+ case ConditionMode.Equal:
+ node.FormatedExpression =
+ $"$c = $c";
+ break;
+ case ConditionMode.NotEqual:
+ node.FormatedExpression =
+ $"$c <> $c";
+ break;
+ case ConditionMode.LessThan:
+ node.FormatedExpression =
+ $"$c < $c";
+ break;
+ case ConditionMode.LessThanOrEqual:
+ node.FormatedExpression =
+ $"$c <= $c";
+ break;
+ case ConditionMode.GreaterThan:
+ node.FormatedExpression =
+ $"$c > $c";
+ break;
+ case ConditionMode.GreaterThanOrEqual:
+ node.FormatedExpression =
+ $"$c >= $c";
+ break;
+ default:
+ throw new InvalidOperationException($"Unsupported mode: {conditionMode}");
+ }
+
+ return node;
+ }
+
+ private ExpressionNode ContainsBuildCondition(ExpressionNode node)
+ {
+ node.FormatedExpression = "contains ($c, $c)";
+ return node;
+ }
+
+ private ExpressionNode BeginsWithBuildCondition(ExpressionNode node)
+ {
+ node.FormatedExpression = "begins_with ($c, $c)";
+ return node;
+ }
+
+ private ExpressionNode AttributeTypeBuildCondition(ExpressionNode node)
+ {
+ node.FormatedExpression = "attribute_type ($c, $c)";
+ return node;
+ }
+
+ private ExpressionNode AttributeNotExistsBuildCondition(ExpressionNode node)
+ {
+ node.FormatedExpression = "attribute_not_exists ($c)";
+ return node;
+ }
+
+ private ExpressionNode AttributeExistsBuildCondition(ExpressionNode node)
+ {
+ node.FormatedExpression = "attribute_exists ($c)";
+ return node;
+ }
+
+ private ExpressionNode InBuildCondition(ConditionExpressionBuilder conditionBuilder, ExpressionNode node)
+ {
+ node.FormatedExpression = "$c IN (";
+
+ for(int i = 1; i < node.Children.Count; i++){
+ node.FormatedExpression += "$c, ";
+ }
+ // Remove trailing comma and space, if any
+ if (node.FormatedExpression.EndsWith(", "))
+ {
+ node.FormatedExpression = node.FormatedExpression.Substring(0, node.FormatedExpression.Length - 2);
+ }
+ node.FormatedExpression += ")";
+ return node;
+ }
+
+ private ExpressionNode BetweenBuildCondition(ExpressionNode node)
+ {
+ node.FormatedExpression = "$c BETWEEN $c AND $c";
+ return node;
+ }
+
+ #endregion
+
+ }
+
+ ///
+ /// The class is responsible for building attribute names
+ /// for DynamoDB expressions. It allows the creation of conditions based on attribute names
+ /// and supports various comparison, logical, and function-based operations.
+ ///
+ public class NameBuilder : OperandBuilder
+ {
+ private readonly IEnumerable _names;
+
+ ///
+ /// Initializes a new instance of the class with the specified attribute name.
+ ///
+ ///
+ /// The attribute name to be used in the expression. Supports nested attributes using dot notation
+ /// (e.g., "Parent.Child[0].Grandchild").
+ ///
+ internal NameBuilder(string name)
+ {
+ if (!string.IsNullOrWhiteSpace(name))
+ {
+ _names = name.Split('.');
+ }
+ }
+
+ ///
+ /// Creates a new instance of the class with the specified attribute name.
+ ///
+ ///
+ ///
+ public static NameBuilder New(string name)
+ {
+ return new NameBuilder(name);
+ }
+
+ #region ConditionExpressionBuilder
+
+ ///
+ /// Adds an attribute name to the condition.
+ ///
+ /// The attribute name or path.
+ /// A for further configuration.
+ public NameBuilder WithName(string path)
+ {
+ return new NameBuilder(path);
+ }
+
+ ///
+ /// Creates an equal condition between the current attribute and the specified value.
+ ///
+ /// The value to compare against.
+ /// A representing the not-equal condition.
+ public ConditionExpressionBuilder Equal(DynamoDBEntry right)
+ {
+ var rightOperand = new ValueBuilder(right);
+ return ConditionExpressionBuilder.Equal(this, rightOperand);
+ }
+
+ ///
+ /// Creates a not-equal condition between the current attribute and the specified value.
+ ///
+ /// The value to compare against.
+ /// A representing the not-equal condition.
+ public ConditionExpressionBuilder NotEqual(DynamoDBEntry right)
+ {
+ var rightOperand = new ValueBuilder(right);
+ return ConditionExpressionBuilder.NotEqual(this, rightOperand);
+ }
+
+ ///
+ /// Creates a less-than condition between the current attribute and the specified value.
+ ///
+ /// The value to compare against.
+ /// A representing the less-than condition.
+ public ConditionExpressionBuilder LessThan(DynamoDBEntry right)
+ {
+ var rightOperand = new ValueBuilder(right);
+ return ConditionExpressionBuilder.LesThan(this, rightOperand);
+ }
+
+ ///
+ /// Creates a less-than-or-equal condition between the current attribute and the specified value.
+ ///
+ /// The value to compare against.
+ /// A representing the less-than-or-equal condition.
+ public ConditionExpressionBuilder LessThanOrEqual(DynamoDBEntry right)
+ {
+ var rightOperand = new ValueBuilder(right);
+ return ConditionExpressionBuilder.LessThanOrEqual(this, rightOperand);
+ }
+
+ ///
+ /// Creates a greater-than condition between the current attribute and the specified value.
+ ///
+ /// The value to compare against.
+ /// A representing the greater-than condition.
+ public ConditionExpressionBuilder GreaterThan(DynamoDBEntry right)
+ {
+ var rightOperand = new ValueBuilder(right);
+ return ConditionExpressionBuilder.GreaterThan(this, rightOperand);
+ }
+
+ ///
+ /// Creates a greater-than-or-equal condition between the current attribute and the specified value.
+ ///
+ /// The value to compare against.
+ /// A representing the greater-than-or-equal condition.
+ public ConditionExpressionBuilder GreaterThanOrEqual(DynamoDBEntry right)
+ {
+ var rightOperand = new ValueBuilder(right);
+ return ConditionExpressionBuilder.GreaterThanOrEqual(this, rightOperand);
+ }
+
+ ///
+ /// Creates a condition to check if the current attribute's value is between two specified values.
+ ///
+ /// The lower bound of the range.
+ /// The upper bound of the range.
+ /// A representing the between condition.
+ public ConditionExpressionBuilder Between(DynamoDBEntry lower, DynamoDBEntry upper)
+ {
+ var lowerOperand = new ValueBuilder(lower);
+ var upperOperand = new ValueBuilder(upper);
+ return ConditionExpressionBuilder.Between(this, lowerOperand,upperOperand);
+ }
+
+ ///
+ /// Creates a condition to check if the current attribute's value is in a specified set of values.
+ ///
+ /// The first value in the set.
+ /// Additional values in the set.
+ /// A representing the in condition.
+ public ConditionExpressionBuilder In(DynamoDBEntry right, params DynamoDBEntry[] others)
+ {
+ var rightOperand = new ValueBuilder(right);
+ var otherOperands = others?.Select(other => new ValueBuilder(other)).Cast().ToArray();
+ return ConditionExpressionBuilder.In(this, rightOperand, otherOperands);
+ }
+
+ ///
+ /// Creates a condition to check if the current attribute exists in the DynamoDB item.
+ ///
+ /// A representing the attribute exists condition.
+ public ConditionExpressionBuilder AttributeExists()
+ {
+ return ConditionExpressionBuilder.AttributeExists(this);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public ConditionExpressionBuilder AttributeNotExists()
+ {
+ return ConditionExpressionBuilder.AttributeNotExists(this);
+ }
+
+ ///
+ /// Creates a condition to check if the current attribute has a specific DynamoDB attribute type.
+ ///
+ /// The expected DynamoDB attribute type.
+ /// A representing the attribute type condition.
+ public ConditionExpressionBuilder AttributeType(DynamoDBAttributeType type)
+ {
+ var rightOperand = new ValueBuilder(type.Value);
+ return ConditionExpressionBuilder.AttributeType(this,rightOperand);
+ }
+
+ ///
+ /// Creates a condition to check if the current attribute's value begins with a specified prefix.
+ ///
+ /// The prefix to check for.
+ /// A representing the begins with condition.
+ public ConditionExpressionBuilder BeginsWith(DynamoDBEntry right)
+ {
+ var rightOperand = new ValueBuilder(right);
+ return ConditionExpressionBuilder.BeginsWith(this, rightOperand);
+ }
+
+ ///
+ /// Creates a condition to check if the current attribute's value contains a specified substring or value.
+ ///
+ /// The substring or value to check for.
+ /// A representing the contains condition.
+ public ConditionExpressionBuilder Contains(DynamoDBEntry right)
+ {
+ var rightOperand = new ValueBuilder(right);
+ return ConditionExpressionBuilder.Contains(this, rightOperand);
+ }
+
+ #endregion
+
+ #region UpdateExpressionBuilder
+
+ ///
+ /// Creates a "Plus" operation for the current attribute, which adds the specified value to the attribute.
+ ///
+ /// The value to add to the current attribute.
+ /// A representing the "Plus" operation.
+ public SetValueBuilder Plus(DynamoDBEntry right)
+ {
+ var rightOperand = new ValueBuilder(right);
+ return SetValueBuilder.Plus(this, rightOperand);
+ }
+
+ ///
+ /// Creates a "Minus" operation for the current attribute, which subtracts the specified value from the attribute.
+ ///
+ /// The value to subtract from the current attribute.
+ /// A representing the "Minus" operation.
+ public SetValueBuilder Minus(DynamoDBEntry right)
+ {
+ var rightOperand = new ValueBuilder(right);
+ return SetValueBuilder.Minus(this, rightOperand);
+ }
+
+ ///
+ /// Creates a "ListAppend" operation for the current attribute, which appends the specified value to a list attribute.
+ ///
+ /// The value to append to the list attribute.
+ /// A representing the "ListAppend" operation.
+ public SetValueBuilder ListAppend(DynamoDBEntry right)
+ {
+ var rightOperand = new ValueBuilder(right);
+ return SetValueBuilder.ListAppend(this, rightOperand);
+ }
+
+ ///
+ /// Creates an "IfNotExists" operation for the current attribute, which sets the attribute to the specified value
+ /// if it does not already exist.
+ ///
+ /// The value to set if the attribute does not exist.
+ /// A representing the "IfNotExists" operation.
+ public SetValueBuilder IfNotExists(DynamoDBEntry setValue)
+ {
+ var rightOperand = new ValueBuilder(setValue);
+ return SetValueBuilder.IfNotExists(this, rightOperand);
+ }
+
+ #endregion
+
+ ///
+ /// Builds the expression node for the current attribute name.
+ ///
+ /// An representing the attribute name in the expression.
+ /// Thrown if the attribute name is invalid or unset.
+ internal override ExpressionNode Build()
+ {
+ if (_names==null || !_names.Any())
+ throw new InvalidOperationException($"Unset name parameter");
+
+ var node = new ExpressionNode
+ {
+ Names = new Stack()
+ };
+
+ var fmtNames = new List(_names.Count());
+
+ foreach (var originalWord in _names)
+ {
+ if (string.IsNullOrEmpty(originalWord))
+ throw new InvalidOperationException("Invalid parameter Name");
+
+ var word = originalWord;
+ var substr = string.Empty;
+
+ // Ensure brackets are matched and contain only digits
+ var bracketPattern = new Regex(@"\[(\d+)\]");
+ var bracketMatches = bracketPattern.Matches(word);
+
+ if (word.Count(c => c == '[') != word.Count(c => c == ']'))
+ throw new InvalidOperationException("Invalid parameter Name");
+
+ foreach (Match match in bracketMatches)
+ {
+ if (!match.Success || match.Groups[1].Length == 0)
+ throw new InvalidOperationException("Invalid parameter Name");
+ }
+
+ // Extract suffix like [0][1] if present
+ if (word.EndsWith("]"))
+ {
+ for (int j = 0; j < word.Length; j++)
+ {
+ if (word[j] == '[')
+ {
+ substr = word.Substring(j);
+ word = word.Substring(0, j);
+ break;
+ }
+ }
+ }
+
+ if (string.IsNullOrEmpty(word))
+ throw new InvalidOperationException("Invalid parameter Name");
+
+ node.Names.Push(word);
+ fmtNames.Add($"$n{substr}");
+ }
+
+ node.FormatedExpression = string.Join(".", fmtNames);
+
+ return node;
+ }
+
+ }
+
+ ///
+ /// ValueBuilder is used to build attribute values for expressions.
+ ///
+ public class ValueBuilder : OperandBuilder
+ {
+ private DynamoDBEntry _value;
+
+ ///
+ /// Default constructor for ValueBuilder.
+ ///
+ ///
+ internal ValueBuilder(DynamoDBEntry value)
+ {
+ _value = value;
+ }
+
+ ///
+ /// Creates a new instance of ValueBuilder with the specified value.
+ ///
+ ///
+ ///
+ public static ValueBuilder New(DynamoDBEntry value)
+ {
+ return new ValueBuilder(value);
+ }
+
+ ///
+ /// Creates a "Plus" operation which adds the specified value to the current value.
+ ///
+ ///
+ ///
+ public SetValueBuilder Plus(OperandBuilder rightOperand)
+ {
+ return SetValueBuilder.Plus(this, rightOperand);
+ }
+
+ ///
+ /// Creates a "Plus" operation which adds the specified value to the current value.
+ ///
+ ///
+ ///
+ public SetValueBuilder Plus(DynamoDBEntry right)
+ {
+ var rightOperand = new ValueBuilder(right);
+ return SetValueBuilder.Plus(this, rightOperand);
+ }
+
+ ///
+ /// Creates a "Minus" operation which subtracts the specified value from the current value.
+ ///
+ ///
+ ///
+ public SetValueBuilder Minus(DynamoDBEntry right)
+ {
+ var rightOperand=new ValueBuilder(right);
+ return SetValueBuilder.Minus(this, rightOperand);
+ }
+
+ ///
+ /// Creates a "Minus" operation which subtracts the specified value from the current value.
+ ///
+ ///
+ ///
+ public SetValueBuilder Minus(OperandBuilder rightOperand)
+ {
+ return SetValueBuilder.Minus(this, rightOperand);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public SetValueBuilder ListAppend(DynamoDBEntry right)
+ {
+ var rightOperand=new ValueBuilder(right);
+ return SetValueBuilder.ListAppend(this, rightOperand);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public SetValueBuilder ListAppend(OperandBuilder rightOperand)
+ {
+ return SetValueBuilder.ListAppend(this, rightOperand);
+ }
+
+
+ ///
+ ///
+ ///
+ ///
+ internal override ExpressionNode Build()
+ {
+ var values = new Stack();
+ values.Push(_value);
+ return new ExpressionNode
+ {
+ Values = values,
+ FormatedExpression = "$v"
+ };
+ }
+ }
+
+
+ ///
+ /// Abstract base class for building operands used in DynamoDB expressions.
+ /// Operands represent attribute names or values in an expression.
+ ///
+ public abstract class OperandBuilder
+ {
+ ///
+ /// Builds the operand into an for use in an expression.
+ ///
+ /// An representing the operand.
+ internal abstract ExpressionNode Build();
+ }
+
+ ///
+ /// Constants used for properties of type DynamoDB AttributeType.
+ ///
+ public class DynamoDBAttributeType : ConstantClass
+ {
+ ///
+ /// Constant S for DynamoDBAttributeType
+ ///
+ public static readonly DynamoDBAttributeType S = new("S");
+
+ ///
+ /// Constant SS for DynamoDBAttributeType
+ ///
+ public static readonly DynamoDBAttributeType SS = new("SS");
+
+ ///
+ /// Constant N for DynamoDBAttributeType
+ ///
+ public static readonly DynamoDBAttributeType N = new("N");
+
+ ///
+ /// Constant NS for DynamoDBAttributeType
+ ///
+ public static readonly DynamoDBAttributeType NS = new("NS");
+
+ ///
+ /// Constant B for DynamoDBAttributeType
+ ///
+ public static readonly DynamoDBAttributeType B = new("B");
+
+ ///
+ /// Constant BS for DynamoDBAttributeType
+ ///
+ public static readonly DynamoDBAttributeType BS = new("BS");
+
+ ///
+ /// Constant BOOL for DynamoDBAttributeType
+ ///
+ public static readonly DynamoDBAttributeType BOOL = new("BOOL");
+
+ ///
+ /// Constant NULL for DynamoDBAttributeType
+ ///
+ public static readonly DynamoDBAttributeType NULL = new("NULL");
+
+ ///
+ /// Constant L for DynamoDBAttributeType
+ ///
+ public static readonly DynamoDBAttributeType L = new("L");
+
+ ///
+ /// Constant M for DynamoDBAttributeType
+ ///
+ public static readonly DynamoDBAttributeType M = new("M");
+
+ ///
+ ///
+ ///
+ ///
+ protected DynamoDBAttributeType(string value)
+ : base(value)
+ {
+ }
+
+ ///
+ /// Finds the constant for the unique value.
+ ///
+ /// The unique value for the constant
+ /// The constant for the unique value
+ public static DynamoDBAttributeType FindValue(string value)
+ {
+ return FindValue(value);
+ }
+
+ ///
+ /// Utility method to convert strings to the constant class.
+ ///
+ /// The string value to convert to the constant class.
+ ///
+ public static implicit operator DynamoDBAttributeType(string value)
+ {
+ return FindValue(value);
+ }
+ }
+
+ ///
+ /// Enumeration of condition modes used in DynamoDB expressions.
+ /// Defines comparison, logical, and function-based conditions.
+ ///
+ internal enum ConditionMode
+ {
+ // Catches errors for unset ConditionBuilder structs
+ Unset = 0,
+
+ // Comparison Conditions
+ Equal,
+ NotEqual,
+ LessThan,
+ LessThanOrEqual,
+ GreaterThan,
+ GreaterThanOrEqual,
+
+ // Logical Conditions
+ And,
+ Or,
+ Not,
+
+ // Function-based Conditions
+ Between,
+ In,
+ AttributeExists,
+ AttributeNotExists,
+ AttributeType,
+ BeginsWith,
+ Contains
+ }
+
+ internal enum OperationMode
+ {
+ Unset = 0,
+ Set,
+ Remove,
+ Add,
+ Delete
+ }
+
+ ///
+ /// Abstract base class for building operands used in DynamoDB expressions.
+ /// Operands represent attribute names or values in an expression.
+ ///
+ internal class OperationBuilder
+ {
+ ///
+ /// Builds the operand into an for use in an expression.
+ ///
+ /// An representing the operand.
+ internal ExpressionNode Build()
+ {
+ var pathChild = Name.Build();
+
+ var node = new ExpressionNode
+ {
+ Children = new Queue(),
+ FormatedExpression = "$c"
+ };
+
+ node.Children.Enqueue(pathChild);
+
+ if (Mode == OperationMode.Remove)
+ {
+ return node;
+ }
+
+ var valueChild = Value.Build();
+
+ node.Children.Enqueue(valueChild);
+
+ node.FormatedExpression += Mode switch
+ {
+ OperationMode.Set => " = $c",
+ OperationMode.Add or OperationMode.Delete => " $c",
+ _ => throw new InvalidOperationException(
+ $"Update expression construction failed: unsupported update operation mode: {Mode}")
+ };
+
+ return node;
+ }
+
+ internal OperandBuilder Value { get; set; }
+ internal NameBuilder Name { get; set; }
+ internal OperationMode Mode { get; private set; }
+
+ ///
+ ///
+ ///
+ ///
+ internal OperationBuilder(OperationMode mode)
+ {
+ Mode = mode;
+ }
+ }
+
+ ///
+ /// Class for building set value expressions used in DynamoDB.
+ ///
+ public class SetValueBuilder: OperandBuilder
+ {
+ private OperandBuilder _leftOperand;
+ private OperandBuilder _rightOperand;
+ private SetValueMode _mode;
+
+ private SetValueBuilder()
+ {
+ _mode = SetValueMode.Unset;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public static SetValueBuilder New()
+ {
+ return new SetValueBuilder();
+ }
+
+ ///
+ /// Adds an attribute name to the condition.
+ ///
+ /// The attribute name or path.
+ /// A for further configuration.
+ public NameBuilder WithName(string path)
+ {
+ return new NameBuilder(path);
+ }
+
+ ///
+ /// Creates a new ValueBuilder with the specified value.
+ ///
+ ///
+ ///
+ public ValueBuilder WithValue(DynamoDBEntry value)
+ {
+ return new ValueBuilder(value);
+ }
+
+ internal static SetValueBuilder Plus(OperandBuilder left, OperandBuilder right)
+ {
+ return new SetValueBuilder()
+ {
+ _leftOperand = left,
+ _rightOperand = right,
+ _mode = SetValueMode.Plus
+ };
+ }
+ internal static SetValueBuilder Minus(OperandBuilder left, OperandBuilder right)
+ {
+ return new SetValueBuilder()
+ {
+ _leftOperand = left,
+ _rightOperand = right,
+ _mode = SetValueMode.Minus
+ };
+ }
+
+ internal static SetValueBuilder ListAppend(OperandBuilder left, OperandBuilder right)
+ {
+ return new SetValueBuilder()
+ {
+ _leftOperand = left,
+ _rightOperand = right,
+ _mode = SetValueMode.ListAppend
+ };
+ }
+
+ internal static SetValueBuilder IfNotExists(OperandBuilder nameBuilder, OperandBuilder setValue)
+ {
+ return new SetValueBuilder()
+ {
+ _leftOperand = nameBuilder,
+ _rightOperand = setValue,
+ _mode = SetValueMode.IfNotExists
+ };
+ }
+
+ internal override ExpressionNode Build()
+ {
+ if (_mode == SetValueMode.Unset)
+ {
+ throw new ArgumentException("Cannot build operand: SetValueBuilder is in UnsetValue mode.");
+ }
+
+ var left = _leftOperand.Build();
+
+ var right = _rightOperand.Build();
+
+ var node = new ExpressionNode()
+ {
+ Children = new Queue()
+ };
+ node.Children.Enqueue(left);
+ node.Children.Enqueue(right);
+
+ node.FormatedExpression = _mode switch
+ {
+ SetValueMode.Plus => "$c + $c",
+ SetValueMode.Minus => "$c - $c",
+ SetValueMode.ListAppend=> "list_append($c, $c)",
+ SetValueMode.IfNotExists => "if_not_exists($c, $c)",
+ _ => throw new InvalidOperationException($"Unsupported SetValueMode: '{_mode}'.")
+ };
+
+ return node;
+ }
+ }
+
+ internal enum SetValueMode
+ {
+ Unset = 0,
+ Plus = 1,
+ Minus = 2,
+ ListAppend = 3,
+ IfNotExists = 4,
+ }
+ ///
+ /// Represents a node in an expression tree.
+ /// Used to construct the final expression string and manage attribute names and values.
+ ///
+ internal class ExpressionNode
+ {
+ ///
+ /// Stack of attribute names used in the expression.
+ ///
+ public Stack Names { get; set; } = new();
+
+ ///
+ /// Stack of attribute values used in the expression.
+ ///
+ public Stack Values { get; set; } = new();
+
+ ///
+ /// The formatted expression string for this node.
+ ///
+ public string FormatedExpression { get; set; }
+
+ ///
+ /// Stack of child nodes representing sub-expressions.
+ ///
+ public Queue Children { get; set; } = new();
+
+ ///
+ /// Builds the final expression string for this node, including attribute aliases.
+ ///
+ /// A list of attribute aliases for names and values.
+ ///
+ /// The constructed expression string.
+ internal string BuildExpressionString(KeyAttributeAliasList aliasList, string expressionType)
+ {
+ var result = new StringBuilder();
+ int i = 0;
+
+ while (i < FormatedExpression.Length)
+ {
+ if (FormatedExpression[i] == '$' && i + 1 < FormatedExpression.Length)
+ {
+ var next = FormatedExpression[i + 1];
+ switch (next)
+ {
+ case 'n':
+ {
+ if (Names.Count == 0)
+ throw new InvalidOperationException("Missing name for $n");
+
+ string name = Names.Pop();
+ string alias = $"#{expressionType}{aliasList.NamesList.Count}";
+ aliasList.NamesList.Add(name);
+ result.Append(alias);
+ break;
+ }
+ case 'v':
+ {
+ if (Values.Count == 0)
+ throw new InvalidOperationException("Missing value for $v");
+
+ var val = Values.Pop();
+ string alias = $":{expressionType}{aliasList.ValuesList.Count}";
+ aliasList.ValuesList.Add(val);
+ result.Append(alias);
+ break;
+ }
+ case 'c':
+ {
+ if (Children.Count == 0)
+ throw new InvalidOperationException("Missing child for $c");
+
+ var child = Children.Dequeue();
+ string subExpr = child.BuildExpressionString(aliasList, expressionType);
+ result.Append(subExpr);
+ break;
+ }
+ default:
+ result.Append(FormatedExpression[i]); // not a known placeholder
+ break;
+ }
+ i += 2; // skip the placeholder
+ }
+ else
+ {
+ result.Append(FormatedExpression[i]);
+ i++;
+ }
+ }
+
+ return result.ToString();
+ }
+ }
+
+ ///
+ /// Represents a list of attribute aliases for names and values used in DynamoDB expressions.
+ ///
+ internal class KeyAttributeAliasList
+ {
+ ///
+ /// List of attribute names aliases used in the expression.
+ ///
+ public List NamesList { get; set; } = new();
+
+ ///
+ /// List of attribute values aliases used in the expression.
+ ///
+ public List ValuesList { get; set; } = new();
+ }
+}
\ No newline at end of file
diff --git a/sdk/test/Services/DynamoDBv2/UnitTests/AWSSDK.UnitTests.DynamoDBv2.NetFramework.csproj b/sdk/test/Services/DynamoDBv2/UnitTests/AWSSDK.UnitTests.DynamoDBv2.NetFramework.csproj
index f958bebf82b6..7a0470aa49d1 100644
--- a/sdk/test/Services/DynamoDBv2/UnitTests/AWSSDK.UnitTests.DynamoDBv2.NetFramework.csproj
+++ b/sdk/test/Services/DynamoDBv2/UnitTests/AWSSDK.UnitTests.DynamoDBv2.NetFramework.csproj
@@ -1,4 +1,4 @@
-
+
true
net472
diff --git a/sdk/test/Services/DynamoDBv2/UnitTests/Custom/ExpressionBuilderTests.cs b/sdk/test/Services/DynamoDBv2/UnitTests/Custom/ExpressionBuilderTests.cs
new file mode 100644
index 000000000000..47dc126af155
--- /dev/null
+++ b/sdk/test/Services/DynamoDBv2/UnitTests/Custom/ExpressionBuilderTests.cs
@@ -0,0 +1,434 @@
+using Amazon.DynamoDBv2.DocumentModel;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System;
+
+namespace AWSSDK_DotNet.UnitTests
+{
+ [TestClass]
+ public class ExpressionBuilderTests
+ {
+
+ [TestMethod]
+ [ExpectedException(typeof(InvalidOperationException))]
+ [TestCategory("DynamoDBv2")]
+ public void Build_ShouldThrowExceptionWhenNoOperation()
+ {
+ var builder = UpdateExpressionBuilder.New().Build();
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildForSetOperation_ShouldReturnExpression()
+ {
+ var builder = UpdateExpressionBuilder.New();
+ builder.Set(NameBuilder.New("test"),
+ SetValueBuilder.New().WithValue(10).Plus(20));
+
+ var expressionTree = builder.Build();
+
+ Assert.IsTrue(expressionTree.ExpressionStatement.Contains("SET"));
+ Assert.AreEqual(2, expressionTree.ExpressionAttributeValues.Count);
+ Assert.AreEqual(1, expressionTree.ExpressionAttributeNames.Count);
+ }
+
+ [TestMethod]
+ public void BuildForRemoveOperation_ShouldReturnExpression()
+ {
+ var builder = UpdateExpressionBuilder.New();
+ builder.Remove(NameBuilder.New("test"));
+
+ var expressionTree = builder.Build();
+
+ Assert.IsTrue(expressionTree.ExpressionStatement.Contains("REMOVE"));
+ Assert.AreEqual(1, expressionTree.ExpressionAttributeNames.Count);
+ }
+
+ [TestMethod]
+ public void BuildForAddOperation_ShouldReturnExpression()
+ {
+ var builder = UpdateExpressionBuilder.New();
+ builder.Add(NameBuilder.New("Garbage"), ValueBuilder.New("asdf"));
+
+ var expressionTree = builder.Build();
+
+ Assert.IsTrue(expressionTree.ExpressionStatement.Contains("ADD"));
+ Assert.AreEqual(1, expressionTree.ExpressionAttributeNames.Count);
+ Assert.AreEqual(1, expressionTree.ExpressionAttributeValues.Count);
+ }
+
+ [TestMethod]
+ public void BuildForDeleteOperation_ShouldReturnExpression()
+ {
+ var builder = UpdateExpressionBuilder.New();
+ builder.Delete(NameBuilder.New("test"), ValueBuilder.New(10));
+
+ var expressionTree = builder.Build();
+
+ Assert.IsTrue(expressionTree.ExpressionStatement.Contains("DELETE"));
+ Assert.AreEqual(1, expressionTree.ExpressionAttributeNames.Count);
+ Assert.AreEqual(1, expressionTree.ExpressionAttributeValues.Count);
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(InvalidOperationException))]
+ public void BuildForRemoveOperation_WithInvalidName_ShouldThrowException()
+ {
+ var builder = UpdateExpressionBuilder.New();
+ builder.Remove(NameBuilder.New(""));
+ builder.Build();
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void UpdateExpressionBuilder_WithMultipleSetOperations_Should_Build_Correctly()
+ {
+ var updateExpression = UpdateExpressionBuilder.New().
+ Set(NameBuilder.New("test"),
+ SetValueBuilder.New().WithValue(10).Plus(20)).
+ Set(NameBuilder.New("test2"),
+ SetValueBuilder.New().WithValue(20).Minus(30)).
+ Build();
+
+ Assert.AreEqual("SET #U0 = :U0 + :U1, #U1 = :U2 - :U3\n", updateExpression.ExpressionStatement);
+ Assert.AreEqual(2, updateExpression.ExpressionAttributeNames.Count);
+ Assert.AreEqual("test", updateExpression.ExpressionAttributeNames["#U0"]);
+ Assert.AreEqual("test2", updateExpression.ExpressionAttributeNames["#U1"]);
+ Assert.AreEqual(4, updateExpression.ExpressionAttributeValues.Count);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void UpdateExpressionBuilder_SetAndRemoveExpressionBuilder_Build_Correctly()
+ {
+ var updateExpression = UpdateExpressionBuilder.New().
+ Set(NameBuilder.New("test"),
+ SetValueBuilder.New().WithValue(10).Plus(20)).
+ Remove(NameBuilder.New("test2")).
+ Build();
+
+ Assert.AreEqual("SET #U0 = :U0 + :U1\nREMOVE #U1\n" ,updateExpression.ExpressionStatement);
+ Assert.AreEqual(2, updateExpression.ExpressionAttributeNames.Count);
+ Assert.AreEqual(2, updateExpression.ExpressionAttributeValues.Count);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void ConditionExpressionBuilderTest()
+ {
+ var conditionExpression = ConditionExpressionBuilder.New().WithName("Age").Equal(21).
+ And(ConditionExpressionBuilder.New().WithName("Status").GreaterThan(1)).Build();
+
+ Assert.AreEqual("(#C0 = :C0) AND (#C1 > :C1)", conditionExpression.ExpressionStatement);
+ Assert.AreEqual(2, conditionExpression.ExpressionAttributeNames.Count);
+ Assert.AreEqual("Age", conditionExpression.ExpressionAttributeNames["#C0"]);
+ Assert.AreEqual("Status", conditionExpression.ExpressionAttributeNames["#C1"]);
+ Assert.AreEqual(2, conditionExpression.ExpressionAttributeValues.Count);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void NameBuilderConstructor_WhenCalled_CreatesValidInstance()
+ {
+ var nameBuilder = NameBuilder.New("test");
+
+ Assert.IsNotNull(nameBuilder);
+ Assert.IsInstanceOfType(nameBuilder, typeof(OperandBuilder));
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void Complex_Attribute_Paths_Should_Build_Correctly()
+ {
+ var nameBuilder = NameBuilder.New("parent.child[0].attribute");
+ var node = nameBuilder.AttributeExists().Build();
+
+ Assert.AreEqual("attribute_exists (#C0.#C1[0].#C2)", node.ExpressionStatement);
+ Assert.AreEqual(0, node.ExpressionAttributeValues.Count);
+ Assert.AreEqual(3, node.ExpressionAttributeNames.Count);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildNameBuilder_WithSpecialCharacters_Should_Build_Correctly()
+ {
+ var attributeName = "Test#Attribute-Name.123";
+ var nameBuilder = NameBuilder.New(attributeName);
+
+ var result = nameBuilder.AttributeExists().Build();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("attribute_exists (#C0.#C1)", result.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildNameBuilder_Equal_WithValue_ReturnsCondition()
+ {
+ var nameBuilder = NameBuilder.New("TestAttribute");
+
+ var result = nameBuilder.Equal(10);
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(resultNode);
+ Assert.AreEqual("#C0 = :C0", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildNameBuilder_NotEqual_WithValue_ReturnsCondition()
+ {
+ var nameBuilder = NameBuilder.New("TestAttribute");
+
+ var result = nameBuilder.NotEqual(10);
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("#C0 <> :C0", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildNameBuilder_GreaterThan_WithValue_ReturnsCondition()
+ {
+ var nameBuilder = NameBuilder.New("TestAttribute");
+
+ var result = nameBuilder.GreaterThan(10);
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(resultNode);
+ Assert.AreEqual("#C0 > :C0", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildNameBuilder_GreaterThanOrEqual_WithValue_ReturnsCondition()
+ {
+ var nameBuilder = NameBuilder.New("TestAttribute");
+
+ var result = nameBuilder.GreaterThanOrEqual(10);
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(resultNode);
+ Assert.AreEqual("#C0 >= :C0", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildNameBuilder_LessThan_WithValue_ReturnsCondition()
+ {
+ var nameBuilder = NameBuilder.New("TestAttribute");
+
+ var result = nameBuilder.LessThan(10);
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("#C0 < :C0", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildNameBuilder_LessThanOrEqual_WithValue_ReturnsCondition()
+ {
+ var nameBuilder = NameBuilder.New("TestAttribute");
+
+ var result = nameBuilder.LessThanOrEqual(10);
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("#C0 <= :C0", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildNameBuilder_Between_WithValue_ReturnsCondition()
+ {
+ var nameBuilder = NameBuilder.New("TestAttribute");
+
+ var result = nameBuilder.Between(10, 20);
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("#C0 BETWEEN :C0 AND :C1", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildNameBuilder_In_WithValue_ReturnsCondition()
+ {
+ var nameBuilder = NameBuilder.New("TestAttribute");
+
+ var result = nameBuilder.In(10, 20, 30);
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(resultNode);
+ Assert.AreEqual("#C0 IN (:C0, :C1, :C2)", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildNameBuilder_BeginsWith_WithValue_ReturnsCondition()
+ {
+ var nameBuilder = NameBuilder.New("TestAttribute");
+
+ var result = nameBuilder.BeginsWith("test");
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(resultNode);
+ Assert.AreEqual("begins_with (#C0, :C0)", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildNameBuilder_Contains_WithValue_ReturnsCondition()
+ {
+ var nameBuilder = NameBuilder.New("TestAttribute");
+
+ var result = nameBuilder.Contains("test");
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(resultNode);
+ Assert.AreEqual("contains (#C0, :C0)", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildNameBuilder_AttributeExists_ReturnsCondition()
+ {
+ var nameBuilder = NameBuilder.New("TestAttribute");
+
+ var result = nameBuilder.AttributeExists();
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(resultNode);
+ Assert.AreEqual("attribute_exists (#C0)", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildNameBuilder_AttributeNotExists_ReturnsCondition()
+ {
+ var nameBuilder = NameBuilder.New("TestAttribute");
+
+ var result = nameBuilder.AttributeNotExists();
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(resultNode);
+ Assert.AreEqual("attribute_not_exists (#C0)", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void BuildNameBuilder_AttributeType_ReturnsCondition()
+ {
+ var nameBuilder = NameBuilder.New("TestAttribute");
+
+ var result = nameBuilder.AttributeType(DynamoDBAttributeType.B);
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(resultNode);
+ Assert.AreEqual("attribute_type (#C0, :C0)", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void ConditionExpressionBuilder_And_ReturnsCondition()
+ {
+ var result = ConditionExpressionBuilder.
+ And(NameBuilder.New("Attribute1").Equal(10),
+ NameBuilder.New("Attribute2").Equal(10));
+
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("(#C0 = :C0) AND (#C1 = :C1)", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void ConditionExpressionBuilder_And_Multiple_ReturnsCondition()
+ {
+
+ var result = ConditionExpressionBuilder.And(NameBuilder.New("Attribute1").Equal(10),
+ NameBuilder.New("Attribute2").Equal(10),
+ NameBuilder.New("Attribute3").Equal(10),
+ NameBuilder.New("Attribute4").Equal(10));
+
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(resultNode);
+ Assert.AreEqual("(#C0 = :C0) AND (#C1 = :C1) AND (#C2 = :C2) AND (#C3 = :C3)", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void ConditionExpressionBuilder_Or_ReturnsCondition()
+ {
+ var result = ConditionExpressionBuilder.Or(NameBuilder.New("Attribute1").Equal(10),
+ NameBuilder.New("Attribute2").Equal(20));
+
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("(#C0 = :C0) OR (#C1 = :C1)", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void ConditionExpressionBuilder_Or_Multiple_ReturnsCondition()
+ {
+ var result = ConditionExpressionBuilder.Or(NameBuilder.New("Attribute1").Equal(10),
+ NameBuilder.New("Attribute2").Equal(20),
+ NameBuilder.New("Attribute3").Equal(30),
+ NameBuilder.New("Attribute4").Equal(40));
+
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(resultNode);
+ Assert.AreEqual("(#C0 = :C0) OR (#C1 = :C1) OR (#C2 = :C2) OR (#C3 = :C3)", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void ConditionExpressionBuilder_AndNestedOr_ReturnsCondition()
+ {
+ var result = ConditionExpressionBuilder.
+ And(NameBuilder.New("Attribute1").Equal(10),
+ ConditionExpressionBuilder.Or(NameBuilder.New("Attribute2").Equal(20),
+ NameBuilder.New("Attribute3").Equal(30)));
+
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(resultNode);
+ Assert.AreEqual("(#C0 = :C0) AND ((#C1 = :C1) OR (#C2 = :C2))", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void ConditionExpressionBuilder_AndOr_ReturnsCondition()
+ {
+ var result = ConditionExpressionBuilder.
+ And(NameBuilder.New("Attribute1").Equal(10),
+ NameBuilder.New("Attribute2").Equal(20)).Or(
+ NameBuilder.New("Attribute3").Equal(30));
+
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(resultNode);
+ Assert.AreEqual("((#C0 = :C0) AND (#C1 = :C1)) OR (#C2 = :C2)", resultNode.ExpressionStatement);
+ }
+
+ [TestMethod]
+ [TestCategory("DynamoDBv2")]
+ public void ConditionExpressionBuilder_Not_ReturnsCondition()
+ {
+ var result = ConditionExpressionBuilder.
+ Not(NameBuilder.New("Attribute1").Equal(10));
+
+ var resultNode = result.Build();
+
+ Assert.IsNotNull(resultNode);
+ Assert.AreEqual("NOT (#C0 = :C0)", resultNode.ExpressionStatement);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sdk/test/UnitTests/Custom/AWSSDK.UnitTestUtilities.NetFramework.csproj b/sdk/test/UnitTests/Custom/AWSSDK.UnitTestUtilities.NetFramework.csproj
index ac49aaa7d208..e1a59258e5e0 100644
--- a/sdk/test/UnitTests/Custom/AWSSDK.UnitTestUtilities.NetFramework.csproj
+++ b/sdk/test/UnitTests/Custom/AWSSDK.UnitTestUtilities.NetFramework.csproj
@@ -15,7 +15,6 @@
false
false
false
-
CS1591