I'm using VS2017 with ASP.NET Core 2.0 (Angular template) and I need to do basic table-based CRUD operations in Azure Storage.
Is there any way to do scaffolding in my environment?
I'm using VS2017 with ASP.NET Core 2.0 (Angular template) and I need to do basic table-based CRUD operations in Azure Storage.
Is there any way to do scaffolding in my environment?
First, I needed to install the NuGet package WindowsAzure.Storage
.
In my Models
folder, I created a Foo
class that represented the model of my Azure table. All model classes inherit from TableEntity
, which has those basic properties for all Azure tables: PartitionKey
, RowKey
, ETag
, etc.
public class Foo : TableEntity {
public string FooField { get; set; }
public string FooField2 { get; set; }
}
I created a Repositories
folder in the project root and added a FooRepository
class for table manipulation (CRUD)
public class FooRepository {
private readonly CloudStorageAccount storageAccount { get; }
public FooRepository(string connectionString) {
storageAccount = CloudStorageAccount.Parse(connectionString);
}
public Task<IEnumerable<Foo>> GetAsync() {
// ...
}
public Task<Foo> GetAsync(string partitionKey, string rowKey) {
// ...
}
public Task UpdateAsync(string partitionKey, string rowKey, Foo updatedEntity) {
// ...
}
public Task DeleteAsync(string partitionKey, string rowKey) {
// ...
}
public Task CreateAsync(Foo newEntity) {
// ...
}
}
The FooRepository
was referenced in my FooController
, which looked something like this:
[Produces("application/json")]
[Route("api/Foo")]
public class FooController : Controller {
readonly FooRepository repo = new FooRepository("connectionString");
[HttpGet]
public async Task<List<Foo>> Get() {
return (await repo.GetEntitiesAsync()).Item1;
}
}
The connection string can be found in the Azure portal. You can pass it as I did, every time I reference a Repository
, or put it in a structured file like * .xml or * .json and read it fixedly, by taking the connectionString
of the repositories constructor, which is a practice I recommend, since these connection strings may eventually change (DRY!).
A Repository
can be more abstract, as I did in the example below:
public class BaseRepository<T> : IRepository where T : class, ITableEntity, new() {
CloudStorageAccount IRepository.StorageAccount { get; set; }
CloudTableClient IRepository.TableClient { get; set; }
CloudTable IRepository.Table => ((IRepository)this).TableClient.GetTableReference(nameof(T));
public BaseRepository(string connString) {
((IRepository) this).StorageAccount = CloudStorageAccount.Parse(connString);
((IRepository) this).TableClient = ((IRepository) this).StorageAccount.CreateCloudTableClient();
}
public Task CreateAsync(T entity) {
TableOperation insertOperation = TableOperation.Insert(entity);
return ((IRepository) this).Table.ExecuteAsync(insertOperation);
}
public async Task<T> GetEntityAsync(string partitionKey, string rowKey) {
TableOperation retrieveOperation = TableOperation.Retrieve<T>(partitionKey, rowKey);
T entity = (await ((IRepository)this).Table.ExecuteAsync(retrieveOperation)).Result as T;
return entity;
}
public async Task<Tuple<List<T>, TableContinuationToken>> GetEntitiesAsync(TableContinuationToken token = null) {
List<T> list = new List<T>();
TableQuery<T> query = new TableQuery<T>().Take(15);
TableQuerySegment<T> tableQueryResult = await ((IRepository)this).Table.ExecuteQuerySegmentedAsync(query, token);
list.AddRange(tableQueryResult.Results);
return Tuple.Create(list, tableQueryResult.ContinuationToken);
}
public async Task<T> DeleteEntityAsync(string partitionKey, string rowKey) {
T entityToDelete = await GetEntityAsync(partitionKey, rowKey);
TableOperation deleteOperation = TableOperation.Delete(entityToDelete);
await ((IRepository)this).Table.ExecuteAsync(deleteOperation);
return entityToDelete;
}
}
Being IRepository
a simple interface:
interface IRepository {
CloudStorageAccount StorageAccount { get; set; }
CloudTableClient TableClient { get; set; }
CloudTable Table { get; }
}
See that GetEntitiesAsync(TableContinuationToken)
is only taking 15 results, when the user passes the page, you call the method by passing the returned token. More details about handling can be read here >.
First you must configure the Azure Storage emulator locally, so you can develop it without the need to go remote.
The storage emulator is available as part of Microsoft Azure SDK . You can also install the storage emulator individually ( download here ).
After installing, just run the Azure Storage Emulator application, select the type you want to emulate and that's it.
To help, you can also use Azure Storage Explorer to make your storage consumption more friendly - either in Azure, or locally .
Download Azure Storage Explorer here .
Sources: