public Task <Func <Task> > CascadeDeleteAsync <TEntity>(MemberInfo memberInfo, string rowKeyRef, string partitionKeyRef, TEntity memberValue, IDictionary <string, EntityProperty> dictionary, AzureTableDriverDynamic repository) { var tableName = GetLookupTableName(memberInfo); return(repository .DeleteAsync <StorageLookupTable, Func <Task> >(rowKeyRef, partitionKeyRef, async(lookupTable, deleteAsync) => { var lookupEntity = await deleteAsync(); var rollbacks = await lookupTable .rowAndPartitionKeys .NullToEmpty() .Select(rowParitionKeyKvp => repository.DeleteAsync <Func <Task> >(rowParitionKeyKvp.Key, rowParitionKeyKvp.Value, memberInfo.DeclaringType, (entity, data) => () => (Task)repository.CreateAsync(entity, memberInfo.DeclaringType, (x) => true, () => false), () => () => 1.AsTask())) .AsyncEnumerable() .Append( () => repository.CreateAsync(lookupEntity, tableName, x => true, () => false)) .ToArrayAsync(); Func <Task> rollbacksAll = () => Task.WhenAll(rollbacks.Select(rollback => rollback())); return rollbacksAll; }, () => () => 0.AsTask(), tableName: tableName)); }
public Task <TResult> ExecuteUpdateAsync <TEntity, TResult>(MemberInfo memberInfo, string rowKey, string partitionKey, TEntity valueExisting, IDictionary <string, EntityProperty> dictionaryExisting, TEntity valueUpdated, IDictionary <string, EntityProperty> dictionaryUpdated, AzureTableDriverDynamic repository, Func <Func <Task>, TResult> onSuccessWithRollback, Func <TResult> onFailure) { var to = (IModifyAzureStorageTablePartitionKey)Activator.CreateInstance(this.To); var toPartitionKey = to.GeneratePartitionKey(rowKey, valueUpdated, memberInfo); var typeWrapper = new TypeWrapper(rowKey, toPartitionKey, dictionaryUpdated); return(repository.InsertOrReplaceAsync <TEntity, TResult>(typeWrapper, (created, newEntity) => onSuccessWithRollback( () => { if (created) { return repository.DeleteAsync <TEntity, bool>(newEntity, () => true, () => true); } var typeWrapperExisting = new TypeWrapper(rowKey, toPartitionKey, dictionaryExisting); return repository.ReplaceAsync <TEntity, bool>(typeWrapperExisting, () => true); }))); }
public async Task <TResult> ExecuteDeleteAsync <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 discard); var hashPartitionKey = memberInfo.DeclaringType.Name; var tableName = GetLookupTableName(memberInfo); return(await repository.DeleteAsync <StorageLookupTable, TResult>( hashRowKey, hashPartitionKey, async (entity, deleteAsync) => { await deleteAsync(); return onSuccessWithRollback( () => repository.CreateAsync(entity, (discardAgain, discard2x) => true, () => false)); }, () => onSuccessWithRollback(() => 1.AsTask()), (codes, why) => onSuccessWithRollback(() => 1.AsTask()), tableName : tableName)); }
public Task <TResult> ExecuteUpdateAsync <TEntity, TResult>(MemberInfo memberInfo, IAzureStorageTableEntity <TEntity> updatedEntity, IAzureStorageTableEntity <TEntity> existingEntity, AzureTableDriverDynamic repository, Func <Func <Task>, TResult> onSuccessWithRollback, Func <TResult> onFailure) { var rowKey = updatedEntity.RowKey; var valueUpdated = updatedEntity.Entity; var dictionaryUpdated = updatedEntity.WriteEntity(null); var to = (IModifyAzureStorageTablePartitionKey)Activator.CreateInstance(this.To); var toPartitionKey = to.GeneratePartitionKey(rowKey, valueUpdated, memberInfo); var typeWrapper = new TypeWrapper(rowKey, toPartitionKey, dictionaryUpdated); var dictionaryExisting = existingEntity.WriteEntity(null); return(repository.InsertOrReplaceAsync <TEntity, TResult>(typeWrapper, (created, newEntity) => onSuccessWithRollback( () => { if (created) { return repository.DeleteAsync <TEntity, bool>(newEntity, () => true, () => true, () => false, (codes, why) => false); } var typeWrapperExisting = new TypeWrapper(rowKey, toPartitionKey, dictionaryExisting); return repository.ReplaceAsync <TEntity, bool>(typeWrapperExisting, () => true); }))); }
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 virtual Task <TResult> ExecuteCreateAsync <TEntity, TResult>(MemberInfo memberInfo, string rowKey, string partitionKey, TEntity value, IDictionary <string, EntityProperty> dictionary, AzureTableDriverDynamic repository, Func <Func <Task>, TResult> onSuccessWithRollback, Func <TResult> onFailure) { var to = (IModifyAzureStorageTablePartitionKey)Activator.CreateInstance(this.To); var toPartitionKey = to.GeneratePartitionKey(rowKey, value, memberInfo); var typeWrapper = new TypeWrapper(rowKey, toPartitionKey, dictionary); return(repository.CreateAsync <TEntity, TResult>(typeWrapper, (entity) => onSuccessWithRollback( () => repository.DeleteAsync <TEntity, bool>(entity, () => true, () => true)), () => onSuccessWithRollback(() => false.AsTask()))); }
public Task <TResult> ExecuteDeleteAsync <TEntity, TResult>(MemberInfo memberInfo, string rowKey, string partitionKey, TEntity value, IDictionary <string, EntityProperty> dictionary, AzureTableDriverDynamic repository, Func <Func <Task>, TResult> onSuccessWithRollback, Func <TResult> onFailure) { var to = (IModifyAzureStorageTablePartitionKey)Activator.CreateInstance(this.To); var toPartitionKey = to.GeneratePartitionKey(rowKey, value, memberInfo); var typeWrapper = new TypeWrapper(rowKey, toPartitionKey, dictionary); return(repository.DeleteAsync <TEntity, TResult>(typeWrapper, () => onSuccessWithRollback( () => { return repository.CreateAsync <TEntity, bool>(typeWrapper, (createdEntity) => true, () => true); }), () => onSuccessWithRollback( () => 1.AsTask()), () => throw new Exception("Delete with ETAG = * failed due to modification."))); }
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)); }