/// <summary> /// Update a new item in a table /// </summary> /// <param name="tableName">Precisely table's name</param> /// <param name="entity">The item which need to be updated</param> public void Update(string tableName, TElement entity) { TableServiceContext.Detach(entity); TableServiceContext.AttachTo(tableName, entity, "*"); TableServiceContext.UpdateObject(entity); TableServiceContext.SaveChanges(SaveChangesOptions.ReplaceOnUpdate); }
/// <summary> /// Test deleting entities inside and outside the given range. /// </summary> /// <param name="testClient">The table client to test.</param> /// <param name="tableName">The name of the table to test.</param> /// <param name="accessPermissions">The access permissions of the table client.</param> /// <param name="startPk">The start partition key range.</param> /// <param name="startRk">The start row key range.</param> /// <param name="endPk">The end partition key range.</param> /// <param name="endRk">The end row key range.</param> private void TestDelete( CloudTableClient testClient, string tableName, SharedAccessTablePermissions accessPermissions, string startPk, string startRk, string endPk, string endRk) { TableServiceContext referenceContext = testClient.GetTableServiceContext(); Action <BaseEntity> deleteDelegate = (tableEntity) => { TableServiceContext context = testClient.GetTableServiceContext(); context.AttachTo(tableName, tableEntity, "*"); context.DeleteObject(tableEntity); context.SaveChangesWithRetries(); context.Detach(tableEntity); }; bool expectSuccess = (accessPermissions & SharedAccessTablePermissions.Delete) != 0; // Perform test TestOperationWithRange( tableName, startPk, startRk, endPk, endRk, deleteDelegate, "delete", expectSuccess, expectSuccess ? HttpStatusCode.NoContent : HttpStatusCode.NotFound); }
// remember that there is no is no rollback functionality for the table storage service right now // be cautious when using this function // if a role does not exist, we stop deleting roles, if a user in a role does not exist, we continue deleting // in case of error conditions, the behavior of this function is different than the SQL role provider public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames) { SecUtility.CheckArrayParameter(ref roleNames, true, true, true, MaxTableRoleNameLength, "roleNames"); SecUtility.CheckArrayParameter(ref usernames, true, true, true, Constants.MaxTableUsernameLength, "usernames"); RoleRow row; try { TableServiceContext svc = CreateDataServiceContext(); foreach (string role in roleNames) { if (!RoleExists(role)) { throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist!", role)); } foreach (string user in usernames) { row = GetUserInRole(svc, role, user); if (row == null) { Log.Write(EventKind.Warning, string.Format(CultureInfo.InstalledUICulture, "The user {0} does not exist in the role {1}.", user, role)); continue; } try { svc.DeleteObject(row); svc.SaveChangesWithRetries(); } catch (Exception e) { var dsce = e.InnerException as DataServiceClientException; if (dsce != null && (dsce.StatusCode == (int)HttpStatusCode.NoContent || dsce.StatusCode == (int)HttpStatusCode.NotFound)) { Log.Write(EventKind.Warning, string.Format(CultureInfo.InstalledUICulture, "The user {0} does not exist in the role {1}.", user, role)); svc.Detach(row); } else { throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Error deleting user {0} from role {1}.", user, role)); } } } } } catch (InvalidOperationException e) { throw new ProviderException("Error while accessing the data store.", e); } }
// Because of limited transactional support in the table storage offering, this function gives limited guarantees // for inserting all users into all roles. // We do not recommend using this function because of missing transactional support. public override void AddUsersToRoles(string[] usernames, string[] roleNames) { SecUtility.CheckArrayParameter(ref roleNames, true, true, true, MaxTableRoleNameLength, "roleNames"); SecUtility.CheckArrayParameter(ref usernames, true, true, true, Constants.MaxTableUsernameLength, "usernames"); RoleRow row; try { TableServiceContext svc = CreateDataServiceContext(); foreach (string role in roleNames) { if (!RoleExists(role)) { throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist!", role)); } foreach (string user in usernames) { row = new RoleRow(_applicationName, role, user); try { svc.AddObject(_tableName, row); svc.SaveChangesWithRetries(); } catch (InvalidOperationException e) { if (e.InnerException is DataServiceClientException && (e.InnerException as DataServiceClientException).StatusCode == (int)HttpStatusCode.Conflict) { // this element already exists or was created in a failed retry // this is not a fatal error; continue adding elements Log.Write(EventKind.Warning, string.Format(CultureInfo.InstalledUICulture, "The user {0} already exists in the role {1}.", user, role)); svc.Detach(row); } else { throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Error adding user {0} to role {1}", user, role)); } } } } } catch (InvalidOperationException e) { throw new ProviderException("Error while accessing the data store.", e); } }
/// <summary> /// Inserts a data entry in the Azure table: creates a new one if does not exists or overwrites (without eTag) an already existing version (the "update in place" semantincs). /// </summary> /// <param name="data">Data to be inserted or replaced in the table.</param> /// <returns>Value promise with new Etag for this data entry after completing this storage operation.</returns> public async Task <string> UpsertTableEntryAsync(T data) { const string operation = "UpsertTableEntry"; var startTime = DateTime.UtcNow; if (Logger.IsVerbose2) { Logger.Verbose2("{0} entry {1} into table {2}", operation, data, TableName); } try { TableServiceContext svc = tableOperationsClient.GetDataServiceContext(); try { Task <DataServiceResponse> savePromise; Func <int, Task <DataServiceResponse> > doSaveChanges = retryNum => { if (retryNum > 0) { svc.Detach(data); } // Try to do update first svc.AttachTo(TableName, data, ANY_ETAG); svc.UpdateObject(data); return(Task <DataServiceResponse> .Factory.FromAsync( svc.BeginSaveChangesWithRetries, svc.EndSaveChangesWithRetries, SaveChangesOptions.ReplaceOnUpdate, null)); }; if (AzureTableDefaultPolicies.MaxBusyRetries > 0) { IBackoffProvider backoff = new FixedBackoff(AzureTableDefaultPolicies.PauseBetweenBusyRetries); savePromise = AsyncExecutorWithRetries.ExecuteWithRetries( doSaveChanges, AzureTableDefaultPolicies.MaxBusyRetries, // Retry automatically iff we get ServerBusy reply from Azure storage (exc, retryNum) => IsServerBusy(exc), AzureTableDefaultPolicies.BusyRetriesTimeout, backoff); } else { // Try single Write only once savePromise = doSaveChanges(0); } await savePromise; EntityDescriptor result = svc.GetEntityDescriptor(data); return(result.ETag); } catch (Exception exc) { Logger.Warn(ErrorCode.AzureTable_06, String.Format("Intermediate error upserting entry {0} to the table {1}", (data == null ? "null" : data.ToString()), TableName), exc); throw; } } finally { CheckAlertSlowAccess(startTime, operation); } }
/// <remarks></remarks> private IEnumerable <CloudEntity <T> > GetInternal <T>(TableServiceContext context, string tableName, Maybe <string> filter) { string continuationRowKey = null; string continuationPartitionKey = null; var stopwatch = Stopwatch.StartNew(); context.MergeOption = MergeOption.AppendOnly; context.ResolveType = ResolveFatEntityType; do { var query = context.CreateQuery <FatEntity>(tableName); if (filter.HasValue) { query = query.AddQueryOption("$filter", filter.Value); } if (null != continuationRowKey) { query = query.AddQueryOption(NextRowKeyToken, continuationRowKey) .AddQueryOption(NextPartitionKeyToken, continuationPartitionKey); } QueryOperationResponse response = null; FatEntity[] fatEntities = null; Retry.Do(_policies.TransientTableErrorBackOff(), CancellationToken.None, () => { try { response = query.Execute() as QueryOperationResponse; fatEntities = ((IEnumerable <FatEntity>)response).ToArray(); } catch (DataServiceQueryException ex) { // if the table does not exist, there is nothing to return var errorCode = RetryPolicies.GetErrorCode(ex); if (TableErrorCodeStrings.TableNotFound == errorCode || StorageErrorCodeStrings.ResourceNotFound == errorCode) { fatEntities = new FatEntity[0]; return; } throw; } }); NotifySucceeded(StorageOperationType.TableQuery, stopwatch); foreach (var fatEntity in fatEntities) { var etag = context.Entities.First(e => e.Entity == fatEntity).ETag; context.Detach(fatEntity); yield return(FatEntity.Convert <T>(fatEntity, _serializer, etag)); } Debug.Assert(context.Entities.Count == 0); if (null != response && response.Headers.ContainsKey(ContinuationNextRowKeyToken)) { continuationRowKey = response.Headers[ContinuationNextRowKeyToken]; continuationPartitionKey = response.Headers[ContinuationNextPartitionKeyToken]; stopwatch.Restart(); } else { continuationRowKey = null; continuationPartitionKey = null; } } while (null != continuationRowKey); }