Skip to content

Instantly share code, notes, and snippets.

@NoelOConnell
Last active July 27, 2020 21:10
Show Gist options
  • Save NoelOConnell/0dee5fc144b0d56a2ce0aa0411316de9 to your computer and use it in GitHub Desktop.
Save NoelOConnell/0dee5fc144b0d56a2ce0aa0411316de9 to your computer and use it in GitHub Desktop.
IDataStore using Azure Table Storage for google-api-dotnet-client
public class DataStoreItem: TableEntity
{
public string Value { get; set; }
}
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}";
}
}
@vmureithi
Copy link

Worked like a charm!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment