The Visual Studio Connected Services lets service providers create Visual Studio extensions that can be added to a project without leaving the IDE. Connected Services also allows you to connect your C# application, ASP.NET Core Application, or Mobile Services, to Azure Storage services. Connected Services takes care of all the references and connection code, and modifies your configuration files accordingly. This allows your application to perform Insert, Update, Delete, Read operations with Azure Table, Blob, Queue, etc.
In this article, we will use Visual Studio Connected Services to access the Table Storage in an ASP.NET Core application. The prerequisites for this article is as following:
- Visual Studio 2015
- Microsoft Azure SDK
- The Azure Account - http://portal.azure.com
To access the storage service, we need to have a storage account with Microsoft Azure. The storage account provides TableStorage, BlobStorage, as wella s QueueStorage on the cloud. The steps to access Azure Storage in an ASP.NET MVC application can be seen at Using Azure Storage API in an ASP.NET MVC Application.
Connecting an ASP.NET Core app with Azure Storage using Connected Services
Step 1: Open Visual Studio 2015 and create a new ASP.NET Core application of the name ASPNet_Core_ConnectedServices as shown in the following image
Click the Ok button, and the following window will be displayed using which we can select the Web Application template as shown below:
Step 2: To use the connected service in the application, right-click on References and select Add Connected Services.. option as shown in the following image:
This will start a wizard to add connected services. We need to select Azure Storage
Click on the Configure button, and a window showing your existing Azure Storage for your Azure Subscription will be loaded. If there are no existing Azure Storages, then we need to click on the Create a New Storage Account link as shown in the following image:
On clicking on the Create a new Storage Account link, the Create Storage Account window will be displayed where the following information has to be entered
- Name
- Pricing Tier
- Resource Group
- Location
If the Resource Group is not available, then it has to be created. The dropdown of the Resource Group provides an option for creating a New resource group. Once we select the new option, the following window will be displayed:
Once the Create button is clicked, the wizard will end with the Storage Account as shown in the following image:
Click on the Add button to add the Storage Account to the project:
This step will add the WindowsAzure.Storage assembly to the project, and will now allow the project to access Storage APIs to perform CRUD operations with Azure Storage. The project adds the config.json file. This file contains the Azure Storage connection string as shown in the following code
{
"MicrosoftAzureStorage": {
"lmstms_AzureStorageConnectionString": "<Azure-Storage-Connection-string>"
}
}
Now the connection string has to be read to access the storage for performing CRUD operations.
Step 4: In the project, add a folder of the name Models. In this folder, add a new class of the name Book.cs. Add the following class in it:
using Microsoft.WindowsAzure.Storage.Table;
namespace ASPNet_Core_ConnectedServices.Models
{
public class Book : TableEntity
{
public Book()
{
}
public Book(int bookid, string publisher)
{
this.RowKey = bookid.ToString();
this.PartitionKey = publisher;
}
public int BookId { get; set; }
public string BookName { get; set; }
public string Author { get; set; }
public int Price { get; set; }
public string Publisher { get; set; }
}
}
In the above code, the Book class is derived from TableEntity class. This class contains properties which represents Schema for the Book table created in the Table Storage.
Performing CRUD Operations on Azure Storage
Step 5: Add a repository class which contains methods for performing CRUD operations. In the project, add a new folder of name Repositories. In this folder, add a new class file. Add the following code in the file:
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using ASPNet_Core_ConnectedServices.Models;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
namespace ASPNet_Core_ConnectedServices.Repositories
{
//1.
public interface ITableRepositories
{
void CreateBook(Book bk);
List<Book> GetBooks();
Book GetBook(string pKey, string rKey);
}
public class TableClientOperationsService : ITableRepositories
{
//2.
CloudStorageAccount storageAccount;
//3.
CloudTableClient tableClient;
//4.
IConfigurationRoot configs;
//5.
public TableClientOperationsService(IConfigurationRoot c)
{
this.configs = c;
var connStr = this.configs.GetSection("MicrosoftAzureStorage:lmstms_AzureStorageConnectionString");
storageAccount = CloudStorageAccount.Parse(connStr.Value);
tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference("Book");
table.CreateIfNotExists();
}
//6.
public void CreateBook(Book bk)
{
Random rnd = new Random();
bk.BookId = rnd.Next(100);
bk.RowKey = bk.BookId.ToString();
bk.PartitionKey = bk.Publisher;
CloudTable table = tableClient.GetTableReference("Book");
TableOperation insertOperation = TableOperation.Insert(bk);
table.Execute(insertOperation);
}
//7.
public Book GetBook(string pKey, string rKey)
{
Book entity = null;
CloudTable table = tableClient.GetTableReference("Book");
TableOperation tableOperation = TableOperation.Retrieve<Book>(pKey, rKey);
entity = table.Execute(tableOperation).Result as Book;
return entity;
}
//8.
public List<Book> GetBooks()
{
List<Book> Books = new List<Book>();
CloudTable table = tableClient.GetTableReference("Book");
TableQuery<Book> query = new TableQuery<Book>();
foreach (var item in table.ExecuteQuery(query))
{
Books.Add(new Book()
{
BookId = item.BookId,
BookName = item.BookName,
Author = item.Author,
Publisher = item.Publisher,
Price = item.Price
});
}
return Books;
}
}
}
The above code does the following (line numbers shown below map with the numbers applied as comments on the code)
1. The interface ITableRepositories contains methods declaration for performing operations on the Book table.
2. The TableClientOperationsService class implements ITableRepositories interface. The CloudStorageAccount object is declared to access the storage account based on the connection string.
3. The CloudTableClient object will be used to perform Create and Read operations.
4. The IConfigurationRoot reference will be used to read JSON files in the project. In our project, we have appsettings.json, config.json which contains key/value pairs for project settings.
5. The constructor of the class plays a very important role. It is injected with IConfigurationRoot object so that it can read the keys from the config.json file. The IConfigurationRoot reads the storage connection string based on the key i.e. MicrosoftAzureStorage:lmstms_AzureStorageConnectionString. The code creates an instance of the storage account based on the connection string. The code creates a table client instance, and creates a table of the name Book if it does not already exist.
6. The CreateBook method adds a new entry in the table.
7. The GetBook method retrieves the book based on Table RowKey and Partition key.
8. The GetBooks method reads all books from the Book Table.
Step 6: Since we have config.json in the project that contains the Azure Storage Connection string, we need to load the file in the hosting environment. Open the Startup.cs file and modify the Startup constructor as shown in the following code (highlighted)
public Startup(IHostingEnvironment env)
{
var path = env.ContentRootPath;
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddJsonFile("config.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
This will load the config.json file.
Step 7: Since ASP.NET MVC core provides default dependency injection, we need to register the TableClientOperationsService class in the container using IServiceCollection. We need to register the IConfigurationRoot in the container so that it can provide an access to the keys from the JSON files in the project. Modify the ConfigureServices() method to register the components as shown in the following code (highlighted)
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(typeof(ITableRepositories), typeof(TableClientOperationsService));
services.AddSingleton<IConfigurationRoot>(Configuration);
services.AddMvc();
}
Step 8: In the Controllers folder, add a new MVC controller of the name TableStorageController.cs. Add the following code in it:
using Microsoft.AspNetCore.Mvc;
using ASPNet_Core_ConnectedServices.Models;
using ASPNet_Core_ConnectedServices.Repositories;
namespace ASPNet_Core_ConnectedServices.Controllers
{
public class TableStorageController : Controller
{
ITableRepositories serv;
public TableStorageController(ITableRepositories s)
{
serv = s;
}
// GET: /<controller>/
public IActionResult Index()
{
var books = serv.GetBooks();
return View(books);
}
public IActionResult Create()
{
var book = new Book();
return View(book);
}
[HttpPost]
public IActionResult Create(Book bk)
{
serv.CreateBook(bk);
return RedirectToAction("Index");
}
}
}
The controller class is injected with ITableRepositories which is registered in the Dependency Injection. This provides an instance of the TableClientOperationsService class. The Index() action method accesses GetBooks() method of the TableClientOperationsService class to read all books. The Create() method with HttpPost attribute is used to create a new Book in the storage using CreateBook() method of the TableClientOperationsService class.
Step 9: Modify the Configure() method of the Startup class to load the TableStorage controller when the application is executed
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=TableStorage}/{action=Index}/{id?}");
});
Step 10: In the Views folder, add a new subfolder of the name TableStorage. In this folder, add a new MVC View of the name Index.cshtml. Add the following code in this file:
@model IEnumerable<ASPNet_Core_ConnectedServices.Models.Book>
<a asp-controller="TableStorage" asp-action="Create">Create New</a>
<table class="table table-striped table-bordered table-condensed">
<thead>
<tr>
<td>Book Id</td>
<td>Book Name</td>
<td>Author</td>
<td>Publisher</td>
<td>Price</td>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>@item.BookId</td>
<td>@item.BookName</td>
<td>@item.Author</td>
<td>@item.Publisher</td>
<td>@item.Price</td>
</tr>
}
</tbody>
</table>
The above code displays all books.
Add the Create.cshtml in the same folder with the following code:
@model ASPNet_Core_ConnectedServices.Models.Book
<form asp-controller="TableStorage" asp-action="Create" method="post">
<table class="table table-striped table-bordered table-bordered">
<tr>
<td>Book Name</td>
<td>
<input type="text" asp-for="BookName" class="form-control"/>
</td>
</tr>
<tr>
<td>Author</td>
<td>
<input type="text" asp-for="Author" class="form-control"/>
</td>
</tr>
<tr>
<td>Publisher</td>
<td>
<input type="text" asp-for="Publisher" class="form-control"/>
</td>
</tr>
<tr>
<td>Price</td>
<td>
<input type="text" asp-for="Price" class="form-control"/>
</td>
</tr>
<tr>
<td>
<input type="submit" class="btn btn-success" />
</td>
<td>
<input type="reset" class="btn btn-default" />
</td>
</tr>
</table>
</form>
The above code provides a View to insert a new Book.
Run the application, and the following result will be displayed:
Click on the Create New link to display the Create View:
Enter book details in it as shown in the following image
Click on the Submit button, and the book will be saved and the Index view will be loaded as shown in the following image:
Further, code for performing all other actions can be implemented in a similar manner.
Conclusion
The Visual Studio Connected Services provide a proxy of Azure Storage services to access Azure Storage in an ASP.NET Core application to perform CRUD operations.
Download the entire source code of this article (Github)
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Mahesh Sabnis is a DotNetCurry author and a Microsoft MVP having over two decades of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions), and Front-end technologies like Angular and React. Follow him on twitter @
maheshdotnet or connect with him on
LinkedIn