Storage Table - Easy data access layer
You can simply use the below code structure when you want to write a data access layer for Azure Storage Table.
Use blow Interface as the skeleton of your data access class.
public interface ITableStorage<T>
{
Task<List<T>> GetAllAsync(string partitionKey);
Task<List<T>> RetrieveEntityAsync(string partitionKey, string rowPrefix);
Task<T> GetSingleAsync(T entity);
Task<T> GetSingleAsync(string partitionKey, string rowKey);
Task InsertEntityAsync(T entity, bool forInsert = true);
Task DeleteEntityAsync(T entity);
Task DeleteSingleAsync(string partitionKey, string rowKey);
Task<List<T>> RetrieveAsync(string partitionKey);
}
Here is the generic concrete class for the main interactions you do with the Storage Table. Add-Edit-Get-Delete.
public class TableStorage<T> : ITableStorage<T> where T : TableEntity, new()
{
private readonly CloudStorageAccount _cloudStorageAccount;
private readonly CloudTable _table;
public TableStorage(string connectionString)
{
_cloudStorageAccount = CloudStorageAccount.Parse(connectionString);
CloudTableClient tableClient = _cloudStorageAccount.CreateCloudTableClient();
_table = tableClient.GetTableReference(typeof(T).Name);
_table.CreateIfNotExistsAsync();
}
public async Task DeleteEntityAsync(T entity)
{
try
{
var DeleteOperation = TableOperation.Delete(entity);
await _table.ExecuteAsync(DeleteOperation);
}
catch (Exception ExceptionObj)
{
throw ExceptionObj;
}
}
public async Task DeleteSingleAsync(string partitionKey, string rowKey)
{
DynamicTableEntity dynamicTableEntity = new DynamicTableEntity(partitionKey, rowKey);
TableResult tableResult = await _table.ExecuteAsync(TableOperation.Retrieve<T>(partitionKey, rowKey, (List<string>)null), null, null);
if(tableResult == null || tableResult.Result == null)
{
return;
}
dynamicTableEntity.ETag = "*";
await _table.ExecuteAsync(TableOperation.Delete(dynamicTableEntity), null, null);
}
public async Task<List<T>> GetAllAsync(string partitionKey)
{
try
{
TableContinuationToken token = null;
TableQuerySegment<T> dataList = await _table.ExecuteQuerySegmentedAsync(new TableQuery<T>()
.Where(TableQuery.GenerateFilterCondition("PartitionKey", "eq", partitionKey)),
token, null, null);
List<T> returnDataList = new List<T>();
returnDataList.AddRange(dataList);
return returnDataList;
}
catch (Exception ExceptionObj)
{
throw ExceptionObj;
}
}
public async Task<T> GetSingleAsync(T entity)
{
return await GetSingleAsync(entity.PartitionKey, entity.RowKey);
}
public async Task<T> GetSingleAsync(string partitionKey, string rowKey)
{
TableResult tableResult = await _table.ExecuteAsync(TableOperation.Retrieve<T>(partitionKey, rowKey, (List<string>)null), null, null);
if(tableResult == null)
{
return default(T);
}
return tableResult.HttpStatusCode == 404 ? default(T) : (T)tableResult.Result;
}
public async Task InsertEntityAsync(T entity, bool forInsert = true)
{
try
{
if (forInsert)
{
var insertOperation = TableOperation.Insert(entity);
await _table.ExecuteAsync(insertOperation);
}
else
{
var insertOrMergeOperation = TableOperation.InsertOrReplace(entity);
await _table.ExecuteAsync(insertOrMergeOperation);
}
}
catch (Exception ExceptionObj)
{
throw ExceptionObj;
}
}
public async Task<List<T>> RetrieveEntityAsync(string partitionKey, string rowPrefix)
{
try
{
int length = rowPrefix.Length - 1;
char ch = (char)((uint)rowPrefix[length] + 1U);
string givenValue = rowPrefix.Substring(0, length) + ch.ToString();
string filterX = TableQuery.CombineFilters(TableQuery.GenerateFilterCondition("RowKey", "ge", rowPrefix),
"and", TableQuery.GenerateFilterCondition("RowKey", "lt", givenValue));
string filter = TableQuery.CombineFilters(TableQuery.GenerateFilterCondition("PartitionKey", "eq", partitionKey),
"and", filterX);
TableContinuationToken token = null;
TableQuerySegment<T> dataList = await _table.ExecuteQuerySegmentedAsync(new TableQuery<T>().Where(filter),
token, null, null);
List<T> returnDataList = new List<T>();
returnDataList.AddRange(dataList);
return returnDataList;
}
catch (Exception ExceptionObj)
{
throw ExceptionObj;
}
}
public async Task<List<T>> RetrieveAsync(string partitionKey)
{
try
{
TableQuery<T> partitionScanQuery = new TableQuery<T>().Where
(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, partitionKey));
TableContinuationToken token = null;
TableQuerySegment<T> dataList = await _table.ExecuteQuerySegmentedAsync(partitionScanQuery, token);
List<T> returnDataList = new List<T>();
returnDataList.AddRange(dataList);
return returnDataList;
}
catch(Exception ex)
{
throw ex;
}
}
}
This is all you need for your data access layer.
Here is how you need to use these components in your code.
Assume you need to write a code to store Student information in the Storage table. You can write your Student model calls like below.
public class Student : TableEntity
{
public Guid StudentId => Guid.Parse(RowKey);
public string SchoolName => PartitionKey;
public DateTime DOB { get; set; }
public string Email { get; set; }
public string Address { get; set; }
public string Phone { get; set; }
public Student() { }
public Student(string partitionKey, string rowKey) : base(partitionKey, rowKey) { }
}
Note: When you inherit your models with the TableEntity and write code to access Storage table client you must install below NuGet packages
- WindowsAzure.Storage
- Microsoft.Azure.Storage.Common
- Microsoft.Azure.Cosmos.Table
When you inject your services to read Student's data you can inject it as below
string storageConnectionString = "THE CONNECTION STRING FOR STORAGE TABLE";
services.AddSingleton<ITableStorage<Student>>(new TableStorage<Student>(storageConnectionString));
You can write your service class as below.
public interface IStudentService
{
Task<Guid> AddStudentAsync(Student student);
// ......
}
public class StudentService : IStudentService
{
private readonly ITableStorage<Student> _tableStorage;
public StudentService(ITableStorage<Student> tableStorage)
{
_tableStorage = tableStorage;
}
public async Task<Guid> AddStudentAsync(Student student)
{
await _tableStorage.InsertEntityAsync(student, true);
return student.StudentId;
}
// ......
}
Hope this will help you to speed up your dev work.
Cheers!!
Comments
Post a Comment