public override void CreateUninitializedItem(HttpContext context, string id, int timeout) { Debug.Assert(context != null); SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id"); if (timeout < 0) { throw new ArgumentException("Parameter timeout must be a non-negative integer!"); } try { TableStorageDataServiceContext svc = CreateDataServiceContext(); SessionRow session = new SessionRow(id, _applicationName); session.Lock = 0; // no lock session.Initialized = false; session.Id = id; session.Timeout = timeout; session.ExpiresUtc = DateTime.UtcNow.AddMinutes(timeout); svc.AddObject(_tableName, session); svc.SaveChangesWithRetries(); } catch (InvalidOperationException e) { throw new ProviderException("Error accessing the data store.", e); } }
private static void DeleteAllEntriesFromSampleTable(TableStorageDataServiceContext svc, string tableName) { IEnumerable <SampleEntity> res; TableStorageDataServiceQuery <SampleEntity> q; Console.WriteLine("Deleting all entities from the table..."); Console.WriteLine("This can take a while..."); var qResult = from c in svc.CreateQuery <SampleEntity>(tableName) select c; q = new TableStorageDataServiceQuery <SampleEntity>(qResult as DataServiceQuery <SampleEntity>); res = q.ExecuteAllWithRetries(); int i = 0; foreach (SampleEntity s in res) { svc.DeleteObject(s); svc.SaveChangesWithRetries(); if (++i % 50 == 0) { Console.WriteLine("Deleted element " + i); } } }
public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item) { Debug.Assert(context != null); Debug.Assert(lockId != null); Debug.Assert(_blobProvider != null); SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id"); try { TableStorageDataServiceContext svc = CreateDataServiceContext(); SessionRow session = GetSession(id, svc); if (session == null) { Debug.Assert(false); return; } if (session.Lock != (int)lockId) { Debug.Assert(false); return; } svc.DeleteObject(session); svc.SaveChangesWithRetries(); } catch (InvalidOperationException e) { throw new ProviderException("Error accessing the data store!", e); } // delete associated blobs try { IEnumerable <BlobProperties> e = _blobProvider.ListBlobs(GetBlobNamePrefix(id)); if (e == null) { return; } IEnumerator <BlobProperties> props = e.GetEnumerator(); if (props == null) { return; } while (props.MoveNext()) { if (props.Current != null) { if (!_blobProvider.DeleteBlob(props.Current.Name)) { // ignore this; it is possible that another thread could try to delete the blob // at the same time } } } } catch (StorageException e) { throw new ProviderException("Error accessing blob storage.", e); } }
public override void ResetItemTimeout(HttpContext context, string id) { Debug.Assert(context != null); SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id"); _providerRetry(() => { TableStorageDataServiceContext svc = CreateDataServiceContext(); SessionRow session = GetSession(id, svc); session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); svc.UpdateObject(session); svc.SaveChangesWithRetries(); }); }
// 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 { TableStorageDataServiceContext 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) { HttpStatusCode status; if (TableStorageHelpers.EvaluateException(e, out status) && (status == HttpStatusCode.NoContent || status == 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); } }
private static void ReleaseItemExclusive(TableStorageDataServiceContext svc, SessionRow session, object lockId) { if ((int)lockId != session.Lock) { // obviously that can happen, but let's see when at least in Debug mode Debug.Assert(false); return; } session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); session.Locked = false; svc.UpdateObject(session); svc.SaveChangesWithRetries(); }
// 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 { TableStorageDataServiceContext 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) { HttpStatusCode status; if (TableStorageHelpers.EvaluateException(e, out status) && status == 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); } }
public override void CreateRole(string roleName) { SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename"); try { TableStorageDataServiceContext svc = CreateDataServiceContext(); RoleRow newRole = new RoleRow(_applicationName, roleName, string.Empty); svc.AddObject(_tableName, newRole); svc.SaveChangesWithRetries(); } catch (InvalidOperationException e) { HttpStatusCode status; // when retry policies are used we cannot distinguish between a conflict and success // so, in the case of a conflict, we just retrun success here if (TableStorageHelpers.EvaluateException(e, out status) && status == HttpStatusCode.Conflict) { return; // the role already exists } throw new ProviderException("Error accessing role table.", e); } }
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) { SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename"); try { TableStorageDataServiceContext svc = CreateDataServiceContext(); DataServiceQuery <RoleRow> queryObj = svc.CreateQuery <RoleRow>(_tableName); IEnumerable <RoleRow> query = from userRole in queryObj where userRole.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) >= 0 && userRole.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && userRole.RowKey == SecUtility.Escape(roleName) select userRole; TableStorageDataServiceQuery <RoleRow> q = new TableStorageDataServiceQuery <RoleRow>(query as DataServiceQuery <RoleRow>, _tableRetry); IEnumerable <RoleRow> userRows = q.ExecuteAllWithRetries(); if (userRows == null) { return(false); } List <RoleRow> l = new List <RoleRow>(userRows); if (l.Count == 0) { // the role does not exist return(false); } RoleRow role; if (IsStaleRole(l, out role)) { return(false); } if (l.Count > 1 && throwOnPopulatedRole) { throw new ProviderException("Cannot delete populated role."); } svc.DeleteObject(role); svc.SaveChangesWithRetries(); // lets try to remove all remaining elements in the role foreach (RoleRow row in l) { if (row != role) { try { svc.DeleteObject(row); svc.SaveChangesWithRetries(); } catch (InvalidOperationException ex) { HttpStatusCode status; if (TableStorageHelpers.EvaluateException(ex, out status) && (status == HttpStatusCode.NoContent || status == HttpStatusCode.NotFound)) { // this element already was already deleted by another process or during a failed retry // this is not a fatal error; continue deleting elements Log.Write(EventKind.Warning, string.Format(CultureInfo.InstalledUICulture, "The user {0} does not exist in the role {1}.", row.UserName, row.RoleName)); } else { throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Error deleting user {0} from role {1}.", row.UserName, row.RoleName)); } } } } return(true); } catch (InvalidOperationException e) { throw new ProviderException("Error while accessing the data store.", e); } }
// we don't use the retry policy itself in this function because out parameters are not well handled by // retry policies private SessionStateStoreData GetSession(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions, bool exclusive) { Debug.Assert(context != null); SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id"); SessionRow session = null; int curRetry = 0; bool retry = false; // Assign default values to out parameters locked = false; lockId = null; lockAge = TimeSpan.Zero; actions = SessionStateActions.None; do { retry = false; try { TableStorageDataServiceContext svc = CreateDataServiceContext(); session = GetSession(id, svc); // Assign default values to out parameters locked = false; lockId = null; lockAge = TimeSpan.Zero; actions = SessionStateActions.None; // if the blob does not exist, we return null // ASP.NET will call the corresponding method for creating the session if (session == null) { return(null); } if (session.Initialized == false) { Debug.Assert(session.Locked == false); actions = SessionStateActions.InitializeItem; session.Initialized = true; } session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); if (exclusive) { if (!session.Locked) { if (session.Lock == Int32.MaxValue) { session.Lock = 0; } else { session.Lock++; } session.LockDateUtc = DateTime.UtcNow; } lockId = session.Lock; locked = session.Locked; session.Locked = true; } lockAge = DateTime.UtcNow.Subtract(session.LockDateUtc); lockId = session.Lock; if (locked == true) { return(null); } // let's try to write this back to the data store // in between, someone else could have written something to the store for the same session // we retry a number of times; if all fails, we throw an exception svc.UpdateObject(session); svc.SaveChangesWithRetries(); } catch (InvalidOperationException e) { HttpStatusCode status; // precondition fails indicates problems with the status code if (TableStorageHelpers.EvaluateException(e, out status) && status == HttpStatusCode.PreconditionFailed) { retry = true; } else { throw new ProviderException("Error accessing the data store.", e); } } } while (retry && curRetry++ < NumRetries); // ok, now we have successfully written back our state // we can now read the blob // note that we do not need to care about read/write locking when accessing the // blob because each time we write a new session we create a new blob with a different name SessionStateStoreData result = null; MemoryStream stream = null; StreamReader reader = null; BlobProperties properties; try { try { stream = _blobProvider.GetBlobContent(session.BlobName, out properties); } catch (StorageException e) { throw new ProviderException("Couldn't read session blob!", e); } reader = new StreamReader(stream); if (actions == SessionStateActions.InitializeItem) { // Return an empty SessionStateStoreData result = new SessionStateStoreData(new SessionStateItemCollection(), SessionStateUtility.GetSessionStaticObjects(context), session.Timeout); } else { // Read Items, StaticObjects, and Timeout from the file byte[] items = Convert.FromBase64String(reader.ReadLine()); byte[] statics = Convert.FromBase64String(reader.ReadLine()); int timeout = session.Timeout; // Deserialize the session result = DeserializeSession(items, statics, timeout); } } finally { if (stream != null) { stream.Close(); } if (reader != null) { reader.Close(); } } return(result); }
public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem) { Debug.Assert(context != null); SecUtility.CheckParameter(ref id, true, true, false, TableStorageConstants.MaxStringPropertySizeInChars, "id"); _providerRetry(() => { TableStorageDataServiceContext svc = CreateDataServiceContext(); SessionRow session; if (!newItem) { session = GetSession(id, svc); if (session == null || session.Lock != (int)lockId) { Debug.Assert(false); return; } } else { session = new SessionRow(id, _applicationName); session.Lock = 1; session.LockDateUtc = DateTime.UtcNow; } session.Initialized = true; Debug.Assert(session.Timeout >= 0); session.Timeout = item.Timeout; session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); session.Locked = false; // yes, we always create a new blob here session.BlobName = GetBlobNamePrefix(id) + Guid.NewGuid().ToString("N"); // Serialize the session and write the blob byte[] items, statics; SerializeSession(item, out items, out statics); string serializedItems = Convert.ToBase64String(items); string serializedStatics = Convert.ToBase64String(statics); MemoryStream output = new MemoryStream(); StreamWriter writer = new StreamWriter(output); try { writer.WriteLine(serializedItems); writer.WriteLine(serializedStatics); writer.Flush(); // for us, it shouldn't matter whether newItem is set to true or false // because we always create the entire blob and cannot append to an // existing one _blobProvider.UploadStream(session.BlobName, output); writer.Close(); output.Close(); } catch (Exception e) { if (!newItem) { ReleaseItemExclusive(svc, session, lockId); } throw new ProviderException("Error accessing the data store.", e); } finally { if (writer != null) { writer.Close(); } if (output != null) { output.Close(); } } if (newItem) { svc.AddObject(_tableName, session); svc.SaveChangesWithRetries(); } else { // Unlock the session and save changes ReleaseItemExclusive(svc, session, lockId); } }); }
// shows alternative ways of generating DataServiceContext objects internal static void RunSamples2() { StorageAccountInfo account = null; try { account = StorageAccountInfo.GetDefaultTableStorageAccountFromConfiguration(); TableStorage tableStorage = TableStorage.Create(account); tableStorage.RetryPolicy = RetryPolicies.RetryN(3, TimeSpan.FromSeconds(1)); // the DataServiceContext object inherits its retry policy from tableStorage in this case TableStorageDataServiceContext svc = tableStorage.GetDataServiceContext(); Console.WriteLine("Table creation, delete and list samples..."); string sampleTableName = SampleDataServiceContext.SampleTableName; tableStorage.TryCreateTable(sampleTableName); DeleteAllEntriesFromSampleTable(svc, sampleTableName); Console.WriteLine("List all tables in the account."); IEnumerable <string> tables2 = tableStorage.ListTables(); foreach (string n1 in tables2) { Console.WriteLine(n1); } Console.WriteLine("Inserting entities into the table..."); SampleEntity t = new SampleEntity("samplepartitionkey", "samplerowkey"); svc.AddObject(sampleTableName, t); svc.SaveChangesWithRetries(); //Detach the existing entity so that we can demonstrate the server side //error when you try to insert an same object with the same keys svc.Detach(t); // Insert an entity with the same keys Console.WriteLine("Try to insert the same entity into the table and show how to deal with error conditions."); t = new SampleEntity("samplepartitionkey", "samplerowkey"); svc.AddObject(sampleTableName, t); try { svc.SaveChangesWithRetries(); // getting here is an error because inserting the same row twice raises an exception Console.WriteLine("Should not get here. Succeeded inserting two entities with the same keys"); } catch (Exception e) { HttpStatusCode status; StorageExtendedErrorInformation errorInfo; if (TableStorageHelpers.EvaluateException(e, out status, out errorInfo) && status == HttpStatusCode.Conflict) { // the row has already been inserted before, this is expected here if (errorInfo != null) { Console.WriteLine("Attempting to insert row with same keys resulted in error {0} : {1}", errorInfo.ErrorCode, errorInfo.ErrorMessage); } } else { throw; } } svc.Detach(t); Console.WriteLine("Insert a large item into the table."); t = new SampleEntity("samplepartitionkey", "samplerowkey1"); t.B = new String('a', 1000); svc.AddObject(sampleTableName, t); svc.SaveChangesWithRetries(); Console.WriteLine("Create a normal DataServiceContext object (not TableStorageDataServiceContext) and attach it to a TableStorage object."); DataServiceContext svc2 = new DataServiceContext( TableStorage.GetServiceBaseUri(account.BaseUri, account.UsePathStyleUris, account.AccountName)); tableStorage.Attach(svc2); var qResult = from c in svc2.CreateQuery <SampleEntity>(sampleTableName) where c.RowKey == "samplerowkey1" select c; foreach (SampleEntity cust in qResult) { if (cust.B != t.B) { Console.WriteLine("Sample failed. Did not read the entity property just written"); } } Console.WriteLine("Insert many rows in a table and show the API for dealing with query result pagination."); int num = 2100; Console.WriteLine("Inserting {0} rows.", num.ToString(CultureInfo.CurrentUICulture)); for (int i = 0; i < num; i++) { t = new SampleEntity("samplestring", i.ToString(CultureInfo.InvariantCulture)); svc.AddObject(sampleTableName, t); svc.SaveChangesWithRetries(); if ((i + 1) % 50 == 0) { Console.WriteLine("Inserted row {0}.", (i + 1).ToString(CultureInfo.CurrentUICulture)); } } Console.WriteLine("Executing query that will return many results. This can take a while..."); var qResult2 = from c in svc.CreateQuery <SampleEntity>(sampleTableName) where c.PartitionKey == "samplestring" select c; TableStorageDataServiceQuery <SampleEntity> tableStorageQuery = new TableStorageDataServiceQuery <SampleEntity>(qResult2 as DataServiceQuery <SampleEntity>); IEnumerable <SampleEntity> res = tableStorageQuery.ExecuteAllWithRetries(); Console.WriteLine("Retrieved query results:"); foreach (SampleEntity entity in res) { Console.WriteLine("Partition key: {0}, row key: {1}.", entity.PartitionKey, entity.RowKey); } Console.WriteLine("Delete all entries in the sample table."); DeleteAllEntriesFromSampleTable(tableStorage.GetDataServiceContext(), sampleTableName); tableStorage.DeleteTable(sampleTableName); Console.WriteLine("Table samples finished!"); } catch (DataServiceRequestException dsre) { Console.WriteLine("DataServiceRequestException: " + GetExceptionMessage(dsre)); ShowTableStorageErrorMessage(account.BaseUri.ToString()); } catch (InvalidOperationException ioe) { Console.WriteLine("Storage service error: " + GetExceptionMessage(ioe)); ShowTableStorageErrorMessage(account.BaseUri.ToString()); } }