public async Task <TResult> ExecuteInsertOrReplaceAsync <TEntity, TResult>(MemberInfo memberInfo,
                                                                                   string rowKeyRef, string partitionKeyRef,
                                                                                   TEntity value, IDictionary <string, EntityProperty> dictionary,
                                                                                   AzureTableDriverDynamic repository,
                                                                                   Func <Func <Task>, TResult> onSuccessWithRollback,
                                                                                   Func <TResult> onFailure)
        {
            if (IsIgnored(memberInfo, value))
            {
                return(onSuccessWithRollback(() => true.AsTask()));
            }
            var hashRowKey       = GetHashRowKey(memberInfo, value, out string[] hashKeys);
            var hashPartitionKey = memberInfo.DeclaringType.Name;
            var tableName        = GetLookupTableName(memberInfo);

            return(await repository.UpdateOrCreateAsync <StorageLookupTable, TResult>(
                       hashRowKey, hashPartitionKey,
                       async (created, lookup, saveAsync) =>
            {
                bool ClaimedByDifferentResource()
                {
                    if (created)
                    {
                        return false;
                    }

                    if (lookup.rowKeyRef != rowKeyRef)
                    {
                        return true;
                    }

                    if (lookup.partitionKeyRef != partitionKeyRef)
                    {
                        return true;
                    }

                    return false;
                }

                if (ClaimedByDifferentResource())
                {
                    return onFailure();
                }

                lookup.rowKeyRef = rowKeyRef;
                lookup.partitionKeyRef = partitionKeyRef;
                lookup.hashvalues = hashKeys;
                await saveAsync(lookup);
                Func <Task <bool> > rollback =
                    async() =>
                {
                    return await repository.DeleteAsync <StorageLookupTable, bool>(hashRowKey, hashPartitionKey,
                                                                                   () => true,
                                                                                   () => false,
                                                                                   tableName: tableName);
                };
                return onSuccessWithRollback(rollback);
            },
                       tableName : tableName));
        }
            public static Task CreateOrUpdateAsync(AzureTableDriverDynamic destRepo, string resource, string filter, int?count = default, double?seconds = default)
            {
                var backupRef = GetBackupRef(resource, filter);

                return(destRepo.UpdateOrCreateAsync <Backup, Backup>(
                           backupRef.StorageComputeRowKey(),
                           backupRef.StorageComputePartitionKey(),
                           async(created, storageToUpdate, saveAsync) =>
                {
                    storageToUpdate.resource = resource;
                    storageToUpdate.filter = filter;
                    storageToUpdate.count = count;
                    storageToUpdate.seconds = seconds;
                    await saveAsync(storageToUpdate);
                    return storageToUpdate;
                }));
            }
Example #3
0
        public async Task <Func <Task> > MutateLookupTable(string rowKey, string partitionKey,
                                                           MemberInfo memberInfo, AzureTableDriverDynamic repository,
                                                           Func <IEnumerable <KeyValuePair <string, string> >, IEnumerable <KeyValuePair <string, string> > > mutateCollection)
        {
            var tableName = GetLookupTableName(memberInfo);

            return(await repository.UpdateOrCreateAsync <StorageLookupTable, Func <Task> >(rowKey, partitionKey,
                                                                                           async (created, lookup, saveAsync) =>
            {
                // store for rollback
                var orignalRowAndPartitionKeys = lookup.rowAndPartitionKeys
                                                 .NullToEmpty()
                                                 .ToArray();
                var updatedRowAndPartitionKeys = mutateCollection(orignalRowAndPartitionKeys)
                                                 .ToArray();

                if (Unmodified(orignalRowAndPartitionKeys, updatedRowAndPartitionKeys))
                {
                    return () => true.AsTask();
                }

                lookup.rowAndPartitionKeys = updatedRowAndPartitionKeys;
                await saveAsync(lookup);

                Func <Task <bool> > rollback =
                    async() =>
                {
                    var removed = orignalRowAndPartitionKeys.Except(updatedRowAndPartitionKeys, rk => $"{rk.Key}|{rk.Value}");
                    var added = updatedRowAndPartitionKeys.Except(orignalRowAndPartitionKeys, rk => $"{rk.Key}|{rk.Value}");
                    var table = repository.TableClient.GetTableReference(tableName);
                    return await repository.UpdateAsync <StorageLookupTable, bool>(rowKey, partitionKey,
                                                                                   async(currentDoc, saveRollbackAsync) =>
                    {
                        var currentLookups = currentDoc.rowAndPartitionKeys
                                             .NullToEmpty()
                                             .ToArray();
                        var rolledBackRowAndPartitionKeys = currentLookups
                                                            .Concat(removed)
                                                            .Except(added, rk => $"{rk.Key}|{rk.Value}")
                                                            .ToArray();
                        if (Unmodified(rolledBackRowAndPartitionKeys, currentLookups))
                        {
                            return true;
                        }
                        currentDoc.rowAndPartitionKeys = rolledBackRowAndPartitionKeys;
                        await saveRollbackAsync(currentDoc);
                        return true;
                    },
                                                                                   table: table);
                };
                return rollback;
            },
                                                                                           tableName : tableName));

            bool Unmodified(
                KeyValuePair <string, string>[] rollbackRowAndPartitionKeys,
                KeyValuePair <string, string>[] modifiedDocRowAndPartitionKeys)
            {
                var modified = modifiedDocRowAndPartitionKeys
                               .Except(rollbackRowAndPartitionKeys, rk => $"{rk.Key}|{rk.Value}")
                               .Any();
                var unmodified = !modified;

                return(unmodified);
            }
        }
        public async Task <TResult> ExecuteUpdateAsync <TEntity, TResult>(MemberInfo memberInfo,
                                                                          IAzureStorageTableEntity <TEntity> updatedEntity,
                                                                          IAzureStorageTableEntity <TEntity> existingEntity,
                                                                          AzureTableDriverDynamic repository,
                                                                          Func <Func <Task>, TResult> onSuccessWithRollback,
                                                                          Func <TResult> onFailure)
        {
            var valueExisting        = existingEntity.Entity;
            var existingRowKey       = valueExisting.StorageGetRowKey();
            var existingPartitionKey = valueExisting.StorageGetPartitionKey();

            var valueUpdated       = updatedEntity.Entity;
            var rowKeyRef          = updatedEntity.RowKey;
            var partitionKeyRef    = updatedEntity.PartitionKey;
            var dictionaryExisting = existingEntity.WriteEntity(null);

            if (IsIgnored(memberInfo, valueUpdated))
            {
                if (IsIgnored(memberInfo, valueExisting))
                {
                    return(onSuccessWithRollback(() => true.AsTask()));
                }
                var rollbackMaybeTask = await ExecuteDeleteAsync(memberInfo, rowKeyRef, partitionKeyRef,
                                                                 valueExisting, dictionaryExisting,
                                                                 repository,
                                                                 (rb) => rb,
                                                                 () => default);

                return(onSuccessWithRollback(rollbackMaybeTask));
            }

            var hashRowKey       = GetHashRowKey(memberInfo, valueUpdated, out string hashKeys);
            var hashPartitionKey = memberInfo.DeclaringType.Name;
            var tableName        = GetLookupTableName(memberInfo);

            return(await repository.UpdateOrCreateAsync <StorageLookupTable, TResult>(
                       hashRowKey, hashPartitionKey,
                       async (created, lookup, saveAsync) =>
            {
                if (!created)
                {
                    return onFailure();
                }

                async Task <Func <Task> > RollbackMaybeAsync()
                {
                    if (IsIgnored(memberInfo, valueExisting))
                    {
                        return default(Func <Task>);
                    }
                    return await ExecuteDeleteAsync(memberInfo, rowKeyRef, partitionKeyRef,
                                                    valueExisting, dictionaryExisting,
                                                    repository,
                                                    (rb) => rb,
                                                    () => default);
                }

                var rollbackMaybeTask = RollbackMaybeAsync();
                lookup.rawHash = hashKeys;     // Store raw hash to make the table easier to debug
                await saveAsync(lookup);
                var rollbackMaybe = await rollbackMaybeTask;

                Func <Task <bool> > rollback =
                    async() =>
                {
                    if (!rollbackMaybe.IsDefaultOrNull())
                    {
                        await rollbackMaybe();
                    }
                    return await repository.DeleteAsync <StorageLookupTable, bool>(hashRowKey, hashPartitionKey,
                                                                                   (discard) => true,
                                                                                   () => false,
                                                                                   onFailure: (codes, why) => false,
                                                                                   tableName: tableName);
                };
                return onSuccessWithRollback(rollback);
            },
                       tableName : tableName));
        }
        public async Task <TResult> ExecuteUpdateAsync <TEntity, TResult>(MemberInfo memberInfo,
                                                                          string rowKeyRef, string partitionKeyRef,
                                                                          TEntity valueExisting, IDictionary <string, EntityProperty> dictionaryExisting,
                                                                          TEntity valueUpdated, IDictionary <string, EntityProperty> dictionaryUpdated,
                                                                          AzureTableDriverDynamic repository,
                                                                          Func <Func <Task>, TResult> onSuccessWithRollback,
                                                                          Func <TResult> onFailure)
        {
            var existingRowKey       = valueExisting.StorageGetRowKey();
            var existingPartitionKey = valueExisting.StorageGetPartitionKey();

            if (IsIgnored(memberInfo, valueUpdated))
            {
                if (IsIgnored(memberInfo, valueExisting))
                {
                    return(onSuccessWithRollback(() => true.AsTask()));
                }
                var rollbackMaybeTask = await ExecuteDeleteAsync(memberInfo, rowKeyRef, partitionKeyRef,
                                                                 valueExisting, dictionaryExisting,
                                                                 repository,
                                                                 (rb) => rb,
                                                                 () => default);

                return(onSuccessWithRollback(rollbackMaybeTask));
            }

            var hashRowKey       = GetHashRowKey(memberInfo, valueUpdated, out string[] hashKeys);
            var hashPartitionKey = memberInfo.DeclaringType.Name;
            var tableName        = GetLookupTableName(memberInfo);

            return(await repository.UpdateOrCreateAsync <StorageLookupTable, TResult>(
                       hashRowKey, hashPartitionKey,
                       async (created, lookup, saveAsync) =>
            {
                if (!created)
                {
                    return onFailure();
                }

                async Task <Func <Task> > RollbackMaybeAsync()
                {
                    if (IsIgnored(memberInfo, valueExisting))
                    {
                        return default(Func <Task>);
                    }
                    return await ExecuteDeleteAsync(memberInfo, rowKeyRef, partitionKeyRef,
                                                    valueExisting, dictionaryExisting,
                                                    repository,
                                                    (rb) => rb,
                                                    () => default);
                }

                var rollbackMaybeTask = RollbackMaybeAsync();
                lookup.hashvalues = hashKeys;     // TODO: Why is this necessary?
                await saveAsync(lookup);
                var rollbackMaybe = await rollbackMaybeTask;

                Func <Task <bool> > rollback =
                    async() =>
                {
                    if (!rollbackMaybe.IsDefaultOrNull())
                    {
                        await rollbackMaybe();
                    }
                    return await repository.DeleteAsync <StorageLookupTable, bool>(hashRowKey, hashPartitionKey,
                                                                                   () => true,
                                                                                   () => false,
                                                                                   tableName: tableName);
                };
                return onSuccessWithRollback(rollback);
            },
                       tableName : tableName));
        }
        public async Task <Func <Task> > MutateLookupTable(string rowKey, string partitionKey,
                                                           MemberInfo memberInfo, AzureTableDriverDynamic repository,
                                                           Func <IEnumerable <KeyValuePair <string, string> >, IEnumerable <KeyValuePair <string, string> > > mutateCollection)
        {
            var tableName = GetLookupTableName(memberInfo);

            return(await repository.UpdateOrCreateAsync <StorageLookupTable, Func <Task> >(rowKey, partitionKey,
                                                                                           async (created, lookup, saveAsync) =>
            {
                var rollbackRowAndPartitionKeys = lookup.rowAndPartitionKeys;     // store for rollback
                lookup.rowAndPartitionKeys = mutateCollection(rollbackRowAndPartitionKeys)
                                             .Distinct(rpKey => rpKey.Key)
                                             .ToArray();
                await saveAsync(lookup);
                Func <Task <bool> > rollback =
                    async() =>
                {
                    if (created)
                    {
                        return await repository.DeleteAsync <StorageLookupTable, bool>(rowKey, partitionKey,
                                                                                       () => true,
                                                                                       () => false,
                                                                                       tableName: tableName);
                    }
                    var table = repository.TableClient.GetTableReference(tableName);
                    return await repository.UpdateAsync <StorageLookupTable, bool>(rowKey, partitionKey,
                                                                                   async(modifiedDoc, saveRollbackAsync) =>
                    {
                        bool Modified()
                        {
                            if (rollbackRowAndPartitionKeys.Length != modifiedDoc.rowAndPartitionKeys.Length)
                            {
                                return true;
                            }

                            var matchKeys = rollbackRowAndPartitionKeys.SelectKeys().AsHashSet();
                            var matchValues = rollbackRowAndPartitionKeys.SelectKeys().AsHashSet();
                            var allValuesAccountedFor = modifiedDoc.rowAndPartitionKeys
                                                        .All(
                                rowAndPartitionKey =>
                            {
                                if (!matchKeys.Contains(rowAndPartitionKey.Key))
                                {
                                    return false;
                                }
                                if (!matchValues.Contains(rowAndPartitionKey.Value))
                                {
                                    return false;
                                }
                                return true;
                            });
                            return !allValuesAccountedFor;
                        }
                        if (!Modified())
                        {
                            return true;
                        }
                        modifiedDoc.rowAndPartitionKeys = rollbackRowAndPartitionKeys;
                        await saveRollbackAsync(modifiedDoc);
                        return true;
                    },
                                                                                   table: table);
                };
                return rollback;
            },
                                                                                           tableName : tableName));
        }
        public async Task <Func <Task> > MutateLookupTable(string rowKey, string partitionKey,
                                                           MemberInfo memberInfo, AzureTableDriverDynamic repository,
                                                           Func <IEnumerable <KeyValuePair <string, string> >, IEnumerable <KeyValuePair <string, string> > > mutateCollection)
        {
            var tableName = GetLookupTableName(memberInfo);

            return(await repository.UpdateOrCreateAsync <DateTimeLookupTable, Func <Task> >(rowKey, partitionKey,
                                                                                            async (created, lookup, saveAsync) =>
            {
                // store for rollback
                var rollbackRowAndPartitionKeys = lookup.rows
                                                  .NullToEmpty()
                                                  .Zip(lookup.partitions.NullToEmpty(), (k, v) => k.PairWithValue(v))
                                                  .ToArray();
                await saveAsync(lookup);
                Func <Task <bool> > rollback =
                    async() =>
                {
                    if (created)
                    {
                        return await repository.DeleteAsync <DateTimeLookupTable, bool>(rowKey, partitionKey,
                                                                                        () => true,
                                                                                        () => false,
                                                                                        tableName: tableName);
                    }
                    var table = repository.TableClient.GetTableReference(tableName);
                    return await repository.UpdateAsync <DateTimeLookupTable, bool>(rowKey, partitionKey,
                                                                                    async(modifiedDoc, saveRollbackAsync) =>
                    {
                        bool Modified()
                        {
                            if (rollbackRowAndPartitionKeys.Length != modifiedDoc.rows.Length)
                            {
                                return true;
                            }

                            var matchKeys = rollbackRowAndPartitionKeys.SelectKeys().AsHashSet();
                            var matchValues = rollbackRowAndPartitionKeys.SelectKeys().AsHashSet();
                            var allRowAccountedFor = modifiedDoc.rows
                                                     .All(
                                row =>
                            {
                                if (!matchKeys.Contains(row))
                                {
                                    return false;
                                }
                                return true;
                            });
                            var allPartitionsAccountedFor = modifiedDoc.partitions
                                                            .All(
                                partition =>
                            {
                                if (!matchValues.Contains(partition))
                                {
                                    return false;
                                }
                                return true;
                            });
                            var allValuesAccountedFor = allRowAccountedFor & allPartitionsAccountedFor;
                            return !allValuesAccountedFor;
                        }
                        if (!Modified())
                        {
                            return true;
                        }
                        modifiedDoc.rows = rollbackRowAndPartitionKeys.SelectKeys().ToArray();
                        modifiedDoc.partitions = rollbackRowAndPartitionKeys.SelectValues().ToArray();
                        await saveRollbackAsync(modifiedDoc);
                        return true;
                    },
                                                                                    table: table);
                };
                return rollback;
            },
                                                                                            tableName : tableName));
        }