How to connect an Azure Storage Account to an ASP.NET Core project?

Asked

Viewed 117 times

-1

I’m using VS2017 with ASP.NET Core 2.0 (Angular template) and I need to do basic Tables-based CRUD operations in Azure Storage.

Is there any way to scaffold my environment?

  • 1

    There is a tool in Visual Studio Marketplace that can help you make Azure Storage Tables Scaffolding. https://marketplace.visualstudio.com/items?itemName=StephanJohnsonBlueMarble.AzureTableStorageScaffolder

  • Unfortunately there is no pro VS2017... only pro 2013, so I believe you can not do with this extension using . NET Core

  • I don’t think so! :(

2 answers

2

Azure Storage Emulator

First you should configure the Azure Storage emulator locally, so you can develop 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 application Azure Storage Emulator, select the type you want to emulate and ready.

Azure Storage Explorer

To help, you can also use Azure Storage Explorer to make your storage consumption more user-friendly - whether in Azure or locally.

Azure Storage Explorer

Download Azure Storage Explorer here.

Sources:

https://docs.microsoft.com/pt-br/azure/vs-azure-tools-storage-emulator-using https://docs.microsoft.com/pt-br/azure/storage/common/storage-use-emulator

1


Unfortunately there is no way to connect an Azure Storage Account via Connected Services using any version and layout of ASP.NET Core (at least in Visual Studio 2017), however there is a manual way to bridge, follow the steps I used to solve the problem:

First, I had to install the Nuget package WindowsAzure.Storage.

In my briefcase Models, created a class Foo which represented the template of my Azure table. All model classes inherit from TableEntity, that 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 folder Repositories at the root of the project and added a class FooRepository, for Tables handling (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, that was more or less 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, each time you reference a Repository, or put it in a structured file such as *.xml or *.json and read in a fixed way, taking out the connectionString of the repositories constructor, which is a practice I recommend, since these connection strings may change eventually (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 the GetEntitiesAsync(TableContinuationToken) is only picking up 15 results, when the user passes the page, you call the method by passing the returned token. More details on handling can be read here.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.