/// <summary>
        /// Asynchronously retrieves all domain entities within a given Index.
        /// </summary>
        /// <param name="indexKey">Key to be used as the index. If it's not a string it will be serialized to JSON first and then used as the index name key (a.k.a. Partition Key)</param>
        /// <returns></returns>
        public IEnumerable <TDomainEntity> GetAllItemsFromIndex(object indexKey)
        {
            if (indexKey is string key)
            {
                return(TableOperationsService.GetByPartitionKey(key).Select(tableEntity => tableEntity.DomainObjectInstance));
            }
            var serializedPartitionKey = JsonConvert.SerializeObject(indexKey);

            return(TableOperationsService.GetByPartitionKey(serializedPartitionKey).Select(azureTableEntity => azureTableEntity.DomainObjectInstance));
        }
        /// <summary>
        /// Asynchronously retrieves all domain entities within a given Index.
        /// </summary>
        /// <param name="indexKey">Key to be used as the index. If it's not a string it will be serialized to JSON first and then used as the index name key (a.k.a. Partition Key)</param>
        /// <returns></returns>
        public async Task <List <TDomainEntity> > GetAllItemsFromIndexAsync(object indexKey)
        {
            if (indexKey is string key)
            {
                var entities = await TableOperationsService.GetByPartitionKeyAsync(key).ConfigureAwait(false);

                return(entities.Select(tableEntity => tableEntity.DomainObjectInstance).ToList());
            }
            var serializedPartitionKey = JsonConvert.SerializeObject(indexKey);
            var ents = await TableOperationsService.GetByPartitionKeyAsync(serializedPartitionKey).ConfigureAwait(false);

            return(ents.Select(azureTableEntity => azureTableEntity.DomainObjectInstance).ToList());
        }
        /// <summary>
        /// Asynchronously gets a set of domain entities based on a given index definition with a filter based on the value object (indexedProperty) that gets passed in.
        /// </summary>
        /// <param name="indexDefinitionName">Index definition name</param>
        /// <param name="indexedProperty"></param>
        /// <returns></returns>
        public async Task <List <TDomainEntity> > GetByIndexedPropertyAsync(string indexDefinitionName, object indexedProperty)
        {
            var tempCloudTableEntity = new TableEntityWrapper <TDomainEntity>
            {
                IndexedProperty =
                {
                    ValueBeingIndexed = indexedProperty
                }
            };
            var serializedIndexedProperty = JsonConvert.SerializeObject(tempCloudTableEntity.IndexedProperty);
            var entities = await TableOperationsService.QueryWherePropertyEqualsAsync(indexDefinitionName, CtConstants.PropNameIndexedProperty, serializedIndexedProperty).ConfigureAwait(false);

            return(entities.Select(cte => cte.DomainObjectInstance).ToList());
        }
        /// <summary>
        /// Gets a set of domain entities based on a given indexNameKey with a filter based on the indexProperty that gets passed in.
        /// </summary>
        /// <param name="indexNameKey">Name of the index</param>
        /// <param name="indexedProperty">Value to be searching for inside the index</param>
        /// <returns></returns>
        public IEnumerable <TDomainEntity> GetByIndexedProperty(string indexNameKey, object indexedProperty)
        {
            var tempCloudTableEntity = new TableEntityWrapper <TDomainEntity>
            {
                IndexedProperty =
                {
                    ValueBeingIndexed = indexedProperty
                }
            };
            var serializedIndexedProperty = JsonConvert.SerializeObject(tempCloudTableEntity.IndexedProperty);

            return(TableOperationsService.QueryWherePropertyEquals(indexNameKey, CtConstants.PropNameIndexedProperty, serializedIndexedProperty)
                   .Select(cloudTableEntity => cloudTableEntity.DomainObjectInstance));
        }
        /// <summary>
        /// Initializes a new CloudTableContext object. If the "tableName" parameter is left null, then
        /// the default naming scheme used is the name of the generic type's name with "Table" appended
        /// to it. For example "SomeClass" + "Table" for the table name of "SomeClassTable".
        /// </summary>
        public TableContext(string connectionString, string nameOfEntityIdProperty, string tableName = null)
        {
            if (string.IsNullOrWhiteSpace(nameOfEntityIdProperty))
            {
                throw new ArgumentNullException(nameof(nameOfEntityIdProperty));
            }

            // Need to make sure that the table name is for the domain type and not the TableEntityWrapper type
            var tn = _encoder.CleanTableNameOfInvalidCharacters(string.IsNullOrWhiteSpace(tableName) ? $"{typeof(TDomainEntity).Name}Table" : tableName);

            NameOfEntityIdProperty = nameOfEntityIdProperty;
            TableOperationsService = new TableOperationsService <TableEntityWrapper <TDomainEntity> >(connectionString, tn);
            _tableMetaDataContext  = new TableOperationsService <TableEntityWrapper <PartitionMetaData> >(connectionString, TableOperationsService.TableName);
            LoadTableMetaData();
        }
        /// <summary>
        /// Asynchronously gets a domain entity by the ID using the given entityId and based on the index defined by the given indexNameKey.
        /// If the indexNameKey parameter is left null then the <see cref="DefaultIndex"/> is used.
        /// </summary>
        /// <param name="entityId">Value of the ID property to be used in finding by the ID. This object will get serialized to JSON before being used in the query</param>
        /// <param name="indexNameKey">Optional name of the index used when searching for items by ID. The <see cref="DefaultIndex"/> is usually the one that holds the ID index</param>
        /// <returns></returns>
        public async Task <TDomainEntity> GetByIdAsync(object entityId, string indexNameKey = null)
        {
            if (entityId == null)
            {
                return(null);
            }
            var serializedEntityId = JsonConvert.SerializeObject(entityId);

            if (indexNameKey == null)
            {
                indexNameKey = DefaultIndex.IndexNameKey;
            }
            var tableEntity = await TableOperationsService.FindAsync(indexNameKey, serializedEntityId).ConfigureAwait(false);

            return(tableEntity.DomainObjectInstance);
        }
        /// <summary>
        /// Asynchronously gets a domain entity by the ID using the given entityId and based on the index defined by the given indexNameKey.
        /// If the indexNameKey parameter is left null then the <see cref="DefaultIndex"/> is used.
        /// </summary>
        /// <param name="entityId">Value of the ID property to be used in finding by the ID. This object will get serialized to JSON before being used in the query</param>
        /// <param name="indexNameKey">Optional name of the index used when searching for items by ID. The <see cref="DefaultIndex"/> is usually the one that holds the ID index</param>
        /// <returns></returns>
        public TDomainEntity GetById(object entityId, string indexNameKey = "Default")
        {
            if (entityId == null)
            {
                return(null);
            }
            var serializedEntityId = JsonConvert.SerializeObject(entityId);

            if (string.IsNullOrWhiteSpace(indexNameKey) || string.Equals(indexNameKey, "Default", StringComparison.CurrentCultureIgnoreCase))
            {
                indexNameKey = DefaultIndex.IndexNameKey;
            }
            var tableEntity = TableOperationsService.Find(indexNameKey, serializedEntityId);

            return(tableEntity.DomainObjectInstance);
        }
        private async Task WriteIndexDefinitionsToTableAsync(SaveType batchOperation)
        {
            // Iterate through the index definitions and save all the cloud table entities that are in each index -- This was built up
            // from other methods that came from a call to save the entities
            for (var i = 0; i < IndexDefinitions.Count; i++)
            {
                var indexDefinition = IndexDefinitions[i];
                if (indexDefinition.CloudTableEntities.Count > 0)
                {
                    var entitiesArray = indexDefinition.CloudTableEntities.ToArray();
                    switch (batchOperation)
                    {
                    case SaveType.InsertOrReplace:
                        await TableOperationsService.InsertOrReplaceAsync(entitiesArray).ConfigureAwait(false);

                        break;

                    case SaveType.InsertOrMerge:
                        // Even if the client calls for a merge we need to replace since the whole object is being serialized anyways.
                        await TableOperationsService.InsertOrReplaceAsync(entitiesArray).ConfigureAwait(false);

                        break;

                    case SaveType.Insert:
                        await TableOperationsService.InsertAsync(entitiesArray).ConfigureAwait(false);

                        break;

                    case SaveType.Replace:
                        await TableOperationsService.ReplaceAsync(entitiesArray).ConfigureAwait(false);

                        break;

                    case SaveType.Delete:
                        await TableOperationsService.DeleteAsync(entitiesArray).ConfigureAwait(false);

                        break;
                    }
                }
                indexDefinition.CloudTableEntities.Clear();
            }
        }
        private async Task WriteIndexDefinitionsToTableAsync(SaveType batchOperation)
        {
            for (var i = 0; i < IndexDefinitions.Count; i++)
            {
                var indexDefinition = IndexDefinitions[i];
                if (indexDefinition.CloudTableEntities.Count > 0)
                {
                    var entitiesArray = indexDefinition.CloudTableEntities.ToArray();
                    switch (batchOperation)
                    {
                    case SaveType.InsertOrReplace:
                        await TableOperationsService.InsertOrReplaceAsync(entitiesArray).ConfigureAwait(false);

                        break;

                    case SaveType.InsertOrMerge:
                        // Even if the client calls for a merge we need to replace since the whole object is being serialized anyways.
                        await TableOperationsService.InsertOrReplaceAsync(entitiesArray).ConfigureAwait(false);

                        break;

                    case SaveType.Insert:
                        await TableOperationsService.InsertAsync(entitiesArray).ConfigureAwait(false);

                        break;

                    case SaveType.Replace:
                        await TableOperationsService.ReplaceAsync(entitiesArray).ConfigureAwait(false);

                        break;

                    case SaveType.Delete:
                        await TableOperationsService.DeleteAsync(entitiesArray).ConfigureAwait(false);

                        break;
                    }
                }
                indexDefinition.CloudTableEntities.Clear();
            }
        }
        private void WriteIndexDefinitionsToTable(SaveType batchOperation)
        {
            for (var i = 0; i < IndexDefinitions.Count; i++)
            {
                var indexDefinition = IndexDefinitions[i];
                if (indexDefinition.CloudTableEntities.Count > 0)
                {
                    var entitiesArray = indexDefinition.CloudTableEntities.ToArray();
                    switch (batchOperation)
                    {
                    case SaveType.InsertOrReplace:
                        TableOperationsService.InsertOrReplace(entitiesArray);
                        break;

                    case SaveType.InsertOrMerge:
                        // Even if the client calls for a merge we need to replace since the whole object is being serialized anyways. A merge
                        // is used for updating properties that have changed on the Azure Table
                        TableOperationsService.InsertOrReplace(entitiesArray);
                        break;

                    case SaveType.Insert:
                        TableOperationsService.Insert(entitiesArray);
                        break;

                    case SaveType.Replace:
                        TableOperationsService.Replace(entitiesArray);
                        break;

                    case SaveType.Delete:
                        TableOperationsService.Delete(entitiesArray);
                        break;

                    default:
                        throw new ArgumentOutOfRangeException(nameof(batchOperation), batchOperation, null);
                    }
                }
                indexDefinition.CloudTableEntities.Clear();
            }
        }
        /// <summary>
        /// Retrieves a List of domain <see cref="TableEntityWrapper{TDomainObject}"/> based on a given Index Name (Azure table Partition Key)
        /// and an optional RowKey range.
        /// </summary>
        /// <param name="indexNameKey">Name of index. Ultimately this is the Partition Key inside Azure Table Storage so if you wanted to get all indexed values
        /// inside a given index then you could use this method without the last two parameters</param>
        /// <param name="minIndexedValue">Optional minimum value of the index</param>
        /// <param name="maxIndexedValue">Optional maximum value of the index</param>
        /// <returns></returns>
        public async Task <List <TDomainEntity> > GetFromIndexWithinValueRangeAsync(string indexNameKey, string minIndexedValue = "", string maxIndexedValue = "")
        {
            var entites = await TableOperationsService.GetByPartitionKeyWithRowKeyRangeAsync(indexNameKey, minIndexedValue, maxIndexedValue).ConfigureAwait(false);

            return(entites.Select(ent => ent.DomainObjectInstance).ToList());
        }
        /// <summary>
        /// Gets all the entities via the <see cref="DefaultIndex"/> asynchronously. This is usually the index that retrieves items by ID so all
        /// entities should be unique by default
        /// </summary>
        /// <returns></returns>
        public async Task <List <TDomainEntity> > GetAllAsync()
        {
            var partition = await TableOperationsService.GetByPartitionKeyAsync(_defaultIndexDefinitionName).ConfigureAwait(false);

            return(partition.Select(cte => cte.DomainObjectInstance).ToList());
        }
        /// <summary>
        /// Deletes all instances from a particular index. Under the hood this is deleting everything inside
        /// a Table Partition.
        /// </summary>
        /// <param name="indexNameKey"></param>
        /// <returns></returns>
        public async Task DeleteAllItemsFromIndexAsync(string indexNameKey)
        {
            var entities = await TableOperationsService.GetByPartitionKeyAsync(indexNameKey).ConfigureAwait(false);

            await TableOperationsService.DeleteAsync(entities.ToArray()).ConfigureAwait(false);
        }
 /// <summary>
 /// Returns a TableOperationsService class which allows for more options in constructing custom queries against the table.
 /// </summary>
 /// <returns></returns>
 public TableQuery <TableEntityWrapper <TDomainEntity> > TableQuery()
 {
     return(TableOperationsService.Query());
 }
 /// <summary>
 /// Retrieves a List of domain <see cref="TableEntityWrapper{TDomainObject}"/> based on a given Index Name (Azure table Partition Key)
 /// and an optional RowKey range.
 /// </summary>
 /// <param name="indexNameKey">Name of index. Ultimately this is the Partition Key inside Azure Table Storage so if you wanted to get all indexed values
 /// inside a given index then you could use this method without the last two parameters</param>
 /// <param name="minIndexedValue">Optional minimum value of the index</param>
 /// <param name="maxIndexedValue">Optional maximum value of the index</param>
 /// <returns></returns>
 public IEnumerable <TDomainEntity> GetFromIndexWithinValueRange(string indexNameKey, string minIndexedValue = "", string maxIndexedValue = "")
 {
     return(TableOperationsService.GetByPartitionKeyWithRowKeyRange(indexNameKey, minIndexedValue, maxIndexedValue).Select(azureTableEntity => azureTableEntity.DomainObjectInstance));
 }
 /// <summary>
 /// Gets all the entities via the <see cref="DefaultIndex"/> asynchronously. This is usually the index that retrieves items by ID so all
 /// entities should be unique by default
 /// </summary>
 /// <returns></returns>
 public IEnumerable <TDomainEntity> GetAll()
 {
     return(TableOperationsService.GetByPartitionKey(_defaultIndexDefinitionName).Select(cloudTableEntity => cloudTableEntity.DomainObjectInstance));
 }