Last active
July 27, 2020 21:10
-
-
Save NoelOConnell/0dee5fc144b0d56a2ce0aa0411316de9 to your computer and use it in GitHub Desktop.
IDataStore using Azure Table Storage for google-api-dotnet-client
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 characters
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 characters
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}"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Worked like a charm!