/* * 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. */ using System; using System.Collections.Generic; using Amazon.DynamoDBv2.DocumentModel; using Amazon.DynamoDBv2.Model; using System.Globalization; using Amazon.Util; namespace Amazon.DynamoDBv2.DataModel { /// /// Interface for converting arbitrary objects to DynamoDB-supported /// object types. /// /// Implementing type must be public, instantiable, and should have /// a zero-parameter constructor. /// public interface IPropertyConverter { /// /// Convert object to DynamoDBEntry /// /// Object to be deserialized /// Object deserialized as DynamoDBEntry DynamoDBEntry ToEntry(object value); /// /// Convert DynamoDBEntry to the specified object /// /// DynamoDBEntry to be serialized /// Serialized object object FromEntry(DynamoDBEntry entry); } /// /// Configuration object for setting options on the DynamoDBContext. /// and individual operations. /// public class DynamoDBContextConfig { /// /// Default constructor /// public DynamoDBContextConfig() { TableNamePrefix = AWSConfigsDynamoDB.Context.TableNamePrefix; Conversion = DynamoDBEntryConversion.CurrentConversion; } /// /// Property that directs DynamoDBContext to use consistent reads. /// If property is not set, behavior defaults to non-consistent reads. /// public bool? ConsistentRead { get; set; } /// /// Property that directs DynamoDBContext to skip version checks /// when saving or deleting an object with a version attribute. /// If property is not set, version checks are performed. /// public bool? SkipVersionCheck { get; set; } /// /// Property that directs DynamoDBContext to prefix all table names /// with a specific string. /// If property is null or empty, no prefix is used and default /// table names are used. /// public string TableNamePrefix { get; set; } /// /// Property that directs DynamoDBContext to ignore null values /// on attributes during a Save operation. /// If the property is false (or not set), null values will be /// interpreted as directives to delete the specific attribute. /// public bool? IgnoreNullValues { get; set; } /// /// Property that directs DynamoDBContext to enable empty string values /// on attributes during a Save operation. /// If the property is false (or not set), empty string values will be /// interpreted as null values. /// public bool? IsEmptyStringValueEnabled { get; set; } /// /// Conversion specification which controls how conversion between /// .NET and DynamoDB types happens. /// public DynamoDBEntryConversion Conversion { get; set; } } /// /// Configuration object for setting options for individual operations. /// This will override any settings specified by the DynamoDBContext's DynamoDBContextConfig object. /// public class DynamoDBOperationConfig : DynamoDBContextConfig { /// /// Property that indicates the table to save an object to overriding the DynamoDBTable attribute /// declared for the type. /// public string OverrideTableName { get; set; } /// /// Property that indicates a query should traverse the index backward. /// If the property is false (or not set), traversal shall be forward. /// public bool? BackwardQuery { get; set; } /// /// Property indicating the name of the index to query or scan against. /// This value is optional if the index name can be inferred from the call. /// public string IndexName { get; set; } /// /// A logical operator to apply to the filter conditions: /// AND - If all of the conditions evaluate to true, then the entire filter evaluates to true. /// OR - If at least one of the conditions evaluate to true, then the entire filter evaluates to true. /// /// Default value is AND. /// public ConditionalOperatorValues ConditionalOperator { get; set; } /// /// Query filter for the Query operation operation. Evaluates the query results and returns only /// the matching values. If you specify more than one condition, then by default all of the /// conditions must evaluate to true. To match only some conditions, set ConditionalOperator to Or. /// Note: Conditions must be against non-key properties. /// public List QueryFilter { get; set; } /// /// Default constructor /// public DynamoDBOperationConfig() { QueryFilter = new List(); } // Checks if the IndexName is set on the config internal bool IsIndexOperation { get { return !string.IsNullOrEmpty(IndexName); } } } /// /// Class describing a single scan condition /// public class ScanCondition { #region Public properties /// /// Name of the property being tested /// public string PropertyName { get; set; } /// /// Comparison operator /// public ScanOperator Operator { get; set; } /// /// Values being tested against. /// /// The values should be of the same type as the property. /// In the cases where the property is a collection, the values /// should be of the same type as the items in the collection. /// public object[] Values { get; set; } #endregion #region Constructor /// /// Initializes a ScanCondition with the target property, the /// comparison operator and values being tested against. /// /// Name of the property /// Comparison operator /// /// Value(s) being tested against. /// /// The values should be of the same type as the property. /// In the cases where the property is a collection, the values /// should be of the same type as the items in the collection. /// public ScanCondition(string propertyName, ScanOperator op, params object[] values) { PropertyName = propertyName; Operator = op; Values = values; } #endregion } /// /// Class describing a single query condition /// public class QueryCondition { #region Public properties /// /// Name of the property being tested /// public string PropertyName { get; set; } /// /// Comparison operator /// public QueryOperator Operator { get; set; } /// /// Values being tested against. /// /// The values should be of the same type as the property. /// In the cases where the property is a collection, the values /// should be of the same type as the items in the collection. /// public object[] Values { get; set; } #endregion #region Constructor /// /// Initializes a ScanCondition with the target property, the /// comparison operator and values being tested against. /// /// Name of the property /// Comparison operator /// /// Value(s) being tested against. /// /// The values should be of the same type as the property. /// In the cases where the property is a collection, the values /// should be of the same type as the items in the collection. /// public QueryCondition(string propertyName, QueryOperator op, params object[] values) { PropertyName = propertyName; Operator = op; Values = values; } #endregion } internal class DynamoDBFlatConfig { public static string DefaultIndexName = string.Empty; private static DynamoDBOperationConfig _emptyOperationConfig = new DynamoDBOperationConfig { ConsistentRead = null, OverrideTableName = null, SkipVersionCheck = null, TableNamePrefix = null, IgnoreNullValues = null, BackwardQuery = null, IndexName = null, ConditionalOperator = ConditionalOperatorValues.And, Conversion = null, IsEmptyStringValueEnabled = null }; private static DynamoDBContextConfig _emptyContextConfig = new DynamoDBContextConfig { ConsistentRead = null, SkipVersionCheck = null, TableNamePrefix = null, IgnoreNullValues = null, Conversion = null, IsEmptyStringValueEnabled = null }; public DynamoDBFlatConfig(DynamoDBOperationConfig operationConfig, DynamoDBContextConfig contextConfig) { if (operationConfig == null) operationConfig = _emptyOperationConfig; if (contextConfig == null) contextConfig = _emptyContextConfig; bool consistentRead = operationConfig.ConsistentRead ?? contextConfig.ConsistentRead ?? false; bool skipVersionCheck = operationConfig.SkipVersionCheck ?? contextConfig.SkipVersionCheck ?? false; bool ignoreNullValues = operationConfig.IgnoreNullValues ?? contextConfig.IgnoreNullValues ?? false; bool isEmptyStringValueEnabled = operationConfig.IsEmptyStringValueEnabled ?? contextConfig.IsEmptyStringValueEnabled ?? false; string overrideTableName = !string.IsNullOrEmpty(operationConfig.OverrideTableName) ? operationConfig.OverrideTableName : string.Empty; string tableNamePrefix = !string.IsNullOrEmpty(operationConfig.TableNamePrefix) ? operationConfig.TableNamePrefix : !string.IsNullOrEmpty(contextConfig.TableNamePrefix) ? contextConfig.TableNamePrefix : string.Empty; bool backwardQuery = operationConfig.BackwardQuery ?? false; string indexName = !string.IsNullOrEmpty(operationConfig.IndexName) ? operationConfig.IndexName : DefaultIndexName; List queryFilter = operationConfig.QueryFilter ?? new List(); ConditionalOperatorValues conditionalOperator = operationConfig.ConditionalOperator; DynamoDBEntryConversion conversion = operationConfig.Conversion ?? contextConfig.Conversion ?? DynamoDBEntryConversion.CurrentConversion; ConsistentRead = consistentRead; SkipVersionCheck = skipVersionCheck; IgnoreNullValues = ignoreNullValues; IsEmptyStringValueEnabled = isEmptyStringValueEnabled; OverrideTableName = overrideTableName; TableNamePrefix = tableNamePrefix; BackwardQuery = backwardQuery; IndexName = indexName; QueryFilter = queryFilter; ConditionalOperator = conditionalOperator; Conversion = conversion; State = new OperationState(); } /// /// Property that directs DynamoDBContext to use consistent reads. /// If property is not set, behavior defaults to non-consistent reads. /// public bool? ConsistentRead { get; set; } /// /// Property that directs DynamoDBContext to skip version checks /// when saving or deleting an object with a version attribute. /// If property is not set, version checks are performed. /// public bool? SkipVersionCheck { get; set; } /// /// Property that directs DynamoDBContext to prefix all table names /// with a specific string. /// If property is null or empty, no prefix is used and default /// table names are used. /// public string TableNamePrefix { get; set; } /// /// Property that directs DynamoDBContext to ignore null values /// on attributes during a Save operation. /// If the property is false (or not set), null values will be /// interpreted as directives to delete the specific attribute. /// public bool? IgnoreNullValues { get; set; } /// /// Property that directs DynamoDBContext to enable empty string values /// on attributes during a Save operation. /// If the property is false (or not set), empty string values will be /// interpreted as null values. /// public bool IsEmptyStringValueEnabled { get; set; } /// /// Property that indicates the table to save an object to overriding the DynamoDBTable attribute /// declared for the type. /// public string OverrideTableName { get; set; } /// /// Property that indicates a query should traverse the index backward. /// If the property is false (or not set), traversal shall be forward. /// public bool? BackwardQuery { get; set; } /// /// Property indicating the name of the index to query or scan against. /// This value is optional if the index name can be inferred from the call. /// public string IndexName { get; set; } /// /// A logical operator to apply to the filter conditions: /// AND - If all of the conditions evaluate to true, then the entire filter evaluates to true. /// OR - If at least one of the conditions evaluate to true, then the entire filter evaluates to true. /// /// Default value is AND. /// public ConditionalOperatorValues ConditionalOperator { get; set; } /// /// Query filter for the Query operation operation. Evaluates the query results and returns only /// the matching values. If you specify more than one condition, then by default all of the /// conditions must evaluate to true. To match only some conditions, set ConditionalOperator to Or. /// Note: Conditions must be against non-key properties. /// public List QueryFilter { get; set; } /// /// Conversion specification which controls how conversion between /// .NET and DynamoDB types happens. /// public DynamoDBEntryConversion Conversion { get; set; } // Checks if the IndexName is set on the config internal bool IsIndexOperation { get { return !string.IsNullOrEmpty(IndexName); } } // State of the operation using this config internal OperationState State { get; private set; } public class OperationState { private CircularReferenceTracking referenceTracking; public OperationState() { referenceTracking = new CircularReferenceTracking(); } public IDisposable Track(object target) { return referenceTracking.Track(target); } } } }