Last active
July 27, 2020 21:10
Revisions
-
NoelOConnell revised this gist
Jun 20, 2018 . No changes.There are no files selected for viewing
-
NoelOConnell revised this gist
Jun 20, 2018 . No changes.There are no files selected for viewing
-
NoelOConnell created this gist
Jun 20, 2018 .There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,4 @@ public class DataStoreItem: TableEntity { public string Value { get; set; } } This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,107 @@ public class TableStorageDataStore : IDataStore { private CloudTable _table; public const string TABLE_NAME = "GoogleDataStore"; public const string PARTITION_NAME = "OAuth2Responses"; public TableStorageDataStore() { // Get TableStorage Connection String CloudStorageAccount storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString); var tableClient = storageAccount.CreateCloudTableClient(); _table = tableClient.GetTableReference(TABLE_NAME); _table.CreateIfNotExists(); } public async Task ClearAsync() { // A batch operation may contain up to 100 individual table operations // https://docs.microsoft.com/en-us/dotnet/api/microsoft.windowsazure.storage.table.tablebatchoperation?view=azure-dotnet TableQuery<DataStoreItem> query = new TableQuery<DataStoreItem>(); IList<DataStoreItem> items = _table.ExecuteQuery(query).ToList(); int chunkSize = 99; var batchedItems = items.Select((x, i) => new { Index = i, Value = x }) .GroupBy(x => x.Index / chunkSize) .Select(x => x.Select(v => v.Value).ToList()) .ToList(); foreach(List<DataStoreItem> batch in batchedItems) { TableBatchOperation batchDeleteOperation = new TableBatchOperation(); foreach (DataStoreItem item in batch) batchDeleteOperation.Delete(item); await _table.ExecuteBatchAsync(batchDeleteOperation); } await Task.CompletedTask; } public async Task DeleteAsync<T>(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentException("Key MUST have a value"); } DataStoreItem item = await GetAsync<DataStoreItem>(key); var deleteOperation = TableOperation.Delete(item); await _table.ExecuteAsync(deleteOperation); } public async Task<T> GetAsync<T>(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentException("Key MUST have a value"); } string generatedStoreKey = GenerateStoredKey(key, typeof(T)); TableOperation retrieveOperation = TableOperation.Retrieve<DataStoreItem>(PARTITION_NAME, generatedStoreKey); TableResult retrievedResult = await _table.ExecuteAsync(retrieveOperation); DataStoreItem item = (DataStoreItem)retrievedResult.Result; T value = item == null ? default(T) : JsonConvert.DeserializeObject<T>(item.Value); return value; } public async Task StoreAsync<T>(string key, T value) { if (string.IsNullOrEmpty(key)) { throw new ArgumentException("Key MUST have a value"); } string serializedValue = JsonConvert.SerializeObject(value); string generatedStoreKey = GenerateStoredKey(key, typeof(T)); DataStoreItem item = new DataStoreItem() { PartitionKey = PARTITION_NAME, RowKey = generatedStoreKey, Value = serializedValue }; TableOperation insertOperation = TableOperation.InsertOrMerge(item); TableResult result = await _table.ExecuteAsync(insertOperation); if (result.HttpStatusCode < (int)HttpStatusCode.OK || result.HttpStatusCode > (int)HttpStatusCode.MultipleChoices) { throw new Exception($"[{result.HttpStatusCode}] Failed to insert record at {TABLE_NAME} {item.RowKey}"); } await Task.CompletedTask; } // <summary>Creates a unique stored key based on the key and the class type.</summary> /// <param name="key">The object key.</param> /// <param name="t">The type to store or retrieve.</param> public static string GenerateStoredKey(string key, Type t) { return $"{t.FullName}-{key}"; } }