Beispiel #1
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 <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));
        }
        // Called via reflection
        public virtual Task <TResult> ExecuteTypedAsync <TEntity, TRefEntity, TResult>(IRef <TRefEntity> entityRef,
                                                                                       MemberInfo memberInfo,
                                                                                       string rowKeyRef, string partitionKeyRef,
                                                                                       TEntity value, IDictionary <string, EntityProperty> dictionary,
                                                                                       AzureTableDriverDynamic repository,
                                                                                       Func <Func <Task>, TResult> onSuccessWithRollback,
                                                                                       Func <TResult> onFailure)
            where TRefEntity : IReferenceable
        {
            var rowKey       = entityRef.StorageComputeRowKey();
            var partitionKey = entityRef.StorageComputePartitionKey(rowKey);

            return(repository.UpdateAsync <TRefEntity, TResult>(rowKey, partitionKey,
                                                                async(entity, saveAsync) =>
            {
                var referencedEntityType = typeof(TRefEntity);
                var fieldToModifyFieldInfo = referencedEntityType
                                             .GetFields()
                                             .Select(
                    field =>
                {
                    return field
                    .GetAttributesInterface <IPersistInAzureStorageTables>()
                    .Where(attr => attr.Name == this.ReferenceProperty)
                    .First(
                        (attr, next) => field,
                        () => default(FieldInfo));
                })
                                             .Where(v => !v.IsDefaultOrNull())
                                             .First();
                var valueToMutate = fieldToModifyFieldInfo.GetValue(entity);
                var valueToMutateType = valueToMutate.GetType();
                if (valueToMutateType.IsSubClassOfGeneric(typeof(IRefs <>)))
                {
                    var references = valueToMutate as IReferences;
                    var idsOriginal = references.ids;
                    var rowKeyId = Guid.Parse(rowKeyRef);
                    if (idsOriginal.Contains(rowKeyId))
                    {
                        return onSuccessWithRollback(() => 1.AsTask());
                    }

                    var ids = idsOriginal
                              .Append(rowKeyId)
                              .Distinct()
                              .ToArray();
                    var refsInstantiatable = typeof(Refs <>)
                                             .MakeGenericType(valueToMutateType.GenericTypeArguments.First().AsArray());
                    var valueMutated = Activator.CreateInstance(refsInstantiatable, ids.AsArray());

                    fieldToModifyFieldInfo.SetValue(ref entity, valueMutated);

                    var result = await saveAsync(entity);
                    Func <Task> rollback =
                        async() =>
                    {
                        bool rolled = await repository.UpdateAsync <TRefEntity, bool>(rowKey, partitionKey,
                                                                                      async(entityRollback, saveRollbackAsync) =>
                        {
                            fieldToModifyFieldInfo.SetValue(ref entityRollback, valueToMutate);
                            await saveRollbackAsync(entityRollback);
                            return true;
                        },
                                                                                      () => false);
                    };
                    return onSuccessWithRollback(rollback);
                }

                return onFailure();
            },
                                                                onFailure));
        }
        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));
        }