CRUD .NET Core e DynamoDB
Bom pessoal, estou de volta com a série de artigos .NET Core e AWS. No artigo de hoje faremos acesso ao banco noSQL DynamoDB da AWS.
Apesar de ser iniciante em .Net (e ainda cometer erros), já sei o suficiente para compartilhar o que estou aprendendo com vocês. 🙂
Caso queira ler o artigo anterior, acesse o link abaixo:
DynamoDB
Não vou entrar em detalhes sobre o DynamoDB, mas se você quiser mais informações sobre ele, acesse esse link.
Se isso não for suficiente, a edX está oferecendo um curso totalmente gratuito de DynamoDB. Acesse o post abaixo e veja como se inscrever.
Também existe um guia muito bom que você pode utilizar:
Se você estudar qualquer um dos materiais citados, já estará apto para acompanhar este artigo ! 🙂
Vamos agora continuar e criar o nosso banco DynamoDB na AWS.
Acesse sua conta da AWS, clique em Serviços e procure por DynamoDB.

Clique em Create Table.
Chamaremos a nossa tabela de ProductList.
Clique no botão Criar.

Faça a inserção dos dados no DynamoDb.

Agora já temos o suficiente para testar. Abra o Visual Studio e vamos iniciar o nosso projeto.
Clique em Create a new project e escolha o template ASP.Net Core Web Application.

Vamos chamar o nosso projeto de AWSProductListDynamoDb.

Agora escolha Empty, pois vamos escrever na mão as classes. Clique em Create para criar o nosso projeto.

Vamos instalar o pacote com o SDK da AWS. Para isso, acesse o Package Manager Console em View >> Other Windows >> Package Manager Console.

Digite no Package Manager Console: install-package AWSSDK.Extensions.NETCore.Setup

Verifique se a instalação ocorreu sem erros.

Agora iremos instalar o nuget do DynamoDB. Para isso, digite install-package AWSSDK.DynamoDBv2.

Verifique se a instalação ocorreu sem erros:

Agora vamos começar a implementar as classes que farão todas as operações básicas com o banco de dados:
- Criação da tabela
- Inserção de dados
- Leitura de dados
- Exclusão de dados
Primeiramente, vamos criar o nosso arquivo appsettings.json que conterá as informações necessárias para conexão com o DynamoDB na AWS e também alguns parâmetros referentes a tabela. Os principais para são:
- Region
- AccessKey
- SecretKey
{ "AWS": { "Region": "sa-east-1", "AccessKey": "XXXXXXXXXXXXXXXXXXX", "SecretKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }, "VAR": { "TableName": "ProductList" } }
Dica: Para descobrir o Region onde o seu serviço está executando, no painel da AWS, clique na região e a nomenclatura aparecerá:

Agora vamos ao IAM (Identity Access Management) para criar o nosso usuário de acesso ao DynamoDB e assim gerar o AccessKey e o SecretKey. Para isso acesse USUÁRIOS e clique no botão adicionar usuário.

Vamos dar o nome de nosso usuário de test_dyn, e o tipo de acesso será o programático.

Para simplificar, vamos anexar as políticas de forma direta, como na imagem a seguir. A política de permissão que daremos ao nosso usuário é a AmazonDynamoDBFullAccess. Como o objetivo desse artigo não é a criação de grupos e usuários, faremos do jeito mais simples.

Vamos pular a parte de tags e ir direto a Revisão. Verifique se está tudo Ok e clique em Criar usuário.

Pronto, agora obtemos o AccessKey e o SecretKey para acessarmos o nosso banco de dados.

Agora que já temos o nosso usuário de acesso, e as chaves, vamos voltar ao nosso projeto.
Crie um novo Projeto do tipo Class Library (.NET Core) chamado AWSProductListDynamoDb. Aqui ficará todas as interfaces e classes responsáveis pelo CRUD.
Clique com o botão direito e depois em Add Reference. Selecione AWSProductListDynamoDb.Libs e clique em OK. Assim o nosso projeto principal terá acesso a todas as classes de nossa Lib.


Agora, vamos instalar o pacote do DynamoDB no segundo projeto.

PM> install-package AWSSDK.DynamoDBv2 Restoring packages for C:\Users\gioth\source\repos\AWSProductListDynamoDB\AWSProductListDynamoDB.Libs\AWSProductListDynamoDb.Libs.csproj… GET https://api.nuget.org/v3-flatcontainer/awssdk.dynamodbv2/index.json OK https://api.nuget.org/v3-flatcontainer/awssdk.dynamodbv2/index.json 576ms GET https://api.nuget.org/v3-flatcontainer/awssdk.dynamodbv2/3.3.104.2/awssdk.dynamodbv2.3.3.104.2.nupkg OK https://api.nuget.org/v3-flatcontainer/awssdk.dynamodbv2/3.3.104.2/awssdk.dynamodbv2.3.3.104.2.nupkg 14ms GET https://api.nuget.org/v3-flatcontainer/awssdk.core/index.json OK https://api.nuget.org/v3-flatcontainer/awssdk.core/index.json 566ms GET https://api.nuget.org/v3-flatcontainer/awssdk.core/3.3.104/awssdk.core.3.3.104.nupkg OK https://api.nuget.org/v3-flatcontainer/awssdk.core/3.3.104/awssdk.core.3.3.104.nupkg 10ms Installing AWSSDK.Core 3.3.104. Installing AWSSDK.DynamoDBv2 3.3.104.2. Installing NuGet package AWSSDK.DynamoDBv2 3.3.104.2. Committing restore… Generating MSBuild file C:\Users\gioth\source\repos\AWSProductListDynamoDB\AWSProductListDynamoDB.Libs\obj\AWSProductListDynamoDb.Libs.csproj.nuget.g.props. Writing assets file to disk. Path: C:\Users\gioth\source\repos\AWSProductListDynamoDB\AWSProductListDynamoDB.Libs\obj\project.assets.json Restore completed in 3,36 sec for C:\Users\gioth\source\repos\AWSProductListDynamoDB\AWSProductListDynamoDB.Libs\AWSProductListDynamoDb.Libs.csproj. Successfully installed 'AWSSDK.Core 3.3.104' to AWSProductListDynamoDb.Libs Successfully installed 'AWSSDK.DynamoDBv2 3.3.104.2' to AWSProductListDynamoDb.Libs Committing restore… Assets file has not changed. Skipping assets file writing. Path: C:\Users\gioth\source\repos\AWSProductListDynamoDB\AWSProductListDynamoDB\obj\project.assets.json Restore completed in 19,77 ms for C:\Users\gioth\source\repos\AWSProductListDynamoDB\AWSProductListDynamoDB\AWSProductListDynamoDb.csproj. NuGet Config files used: C:\Users\gioth\AppData\Roaming\NuGet\NuGet.Config C:\Program Files (x86)\NuGet\Config\Microsoft.VisualStudio.Offline.config C:\Program Files (x86)\NuGet\Config\Xamarin.Offline.config Feeds used: https://api.nuget.org/v3/index.json C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\ Executing nuget actions took 357,9 ms Time Elapsed: 00:00:05.5700886 PM>
Nosso ambiente agora está pronto ! Vamos começar a implementar o nosso CRUD.
No projeto .Net Core teremos:

AWSProductListDynamoDB – Startup.cs
using System; using System.IO; using Amazon.DynamoDBv2; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Configuration; using AWSProductListDynamoDb.Libs.AWSProductListDynamoDb; namespace AWSProductListDynamoDb { public class Startup { public Startup() { Configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json") .Build(); } public IConfigurationRoot Configuration { get; set; } // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddMvcCore(); services.AddDefaultAWSOptions(Configuration.GetAWSOptions()); Environment.SetEnvironmentVariable("AWS_ACCESS_KEY_ID",Configuration["AWS:AccessKey"]); Environment.SetEnvironmentVariable("AWS_SECRET_ACCESS_KEY",Configuration["AWS:SecretKey"]); Environment.SetEnvironmentVariable("AWS_REGION", Configuration["AWS:Region"]); Environment.SetEnvironmentVariable("AWS_CONTENT", Configuration["VAR:TableName"]); services.AddAWSService<IAmazonDynamoDB>(); services.AddSingleton<IAWSProductListDynamoDbExamples, AWSProductListDynamoDbExamples>(); services.AddSingleton<IInsertItem, InsertItem>(); services.AddSingleton<IQueryItem, QueryItem>(); services.AddSingleton<IDeleteItem, DeleteItem>(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}"); }); } } }
AWSProductListDynamoDB – Program.cs
using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; namespace AWSProductListDynamoDb { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); } }
AWSProductListDynamoDB – Controllers – AWSProductListDynamoDb.cs
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using AWSProductListDynamoDb.Libs.AWSProductListDynamoDb; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace AWSProductListDynamoDb.Controllers { [Produces("application/json")] [Route("api/[controller]")] public class AWSProductListDynamoDBController : Controller //Base { private readonly IAWSProductListDynamoDbExamples _dynamoDbExamples; private readonly IInsertItem _insertItem; private readonly IQueryItem _queryItem; private readonly IDeleteItem _deleteItem; public AWSProductListDynamoDBController(IAWSProductListDynamoDbExamples dynamoDbExamples, IInsertItem insertItem, IQueryItem queryItem, IDeleteItem deleteItem) { _dynamoDbExamples = dynamoDbExamples; _insertItem = insertItem; _queryItem = queryItem; _deleteItem = deleteItem; } [Route("createtable")] public IActionResult CreateDynamoDbTable() { _dynamoDbExamples.CreateDynamoDbTable(); return Ok(); } [HttpPost] [Route("insertitem")] public IActionResult InsertItem([FromQuery] string productName, int productQuantity) { _insertItem.AddNewEntry(productName, productQuantity); return Ok(); } [HttpGet] [Route("queryitems")] public async Task<IActionResult> GetItems([FromQuery] string productName) { var response = await _queryItem.GetItems(productName); return Ok(response); } [HttpDelete] [Route("deleteitems")] public IActionResult DeleteItems([FromQuery] string productName) { _deleteItem.DeleteEntry(productName); return Ok(); } } }
O projeto Lib teremos:

AWSProductListDynamoDB.Libs – AWSProductListDynamoDB – IAWSProductListDynamoDbExamples.cs (Interface)
namespace AWSProductListDynamoDb.Libs.AWSProductListDynamoDb
{
public interface IAWSProductListDynamoDbExamples
{
void CreateDynamoDbTable();
}
}
AWSProductListDynamoDB.Libs – AWSProductListDynamoDB – AWSProductListDynamoDbExamples.cs (Classe responsável pela criação da tabela no DynamoDb)
Caso a tabela não exista, ela será criada no DynamoDB.
using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; using System; using System.Collections.Generic; using System.Threading; namespace AWSProductListDynamoDb.Libs.AWSProductListDynamoDb { public class AWSProductListDynamoDbExamples : IAWSProductListDynamoDbExamples { private readonly IAmazonDynamoDB _dynamoDbClient; private static readonly string tableName = Environment.GetEnvironmentVariable("AWS_CONTENT"); public AWSProductListDynamoDbExamples(IAmazonDynamoDB dynamoDbClient) { _dynamoDbClient = dynamoDbClient; } public void CreateDynamoDbTable() { try { CreateTempTable(); } catch (Exception e) { Console.WriteLine(e); throw; } } private async void CreateTempTable() { Console.WriteLine("Criando a tabela..."); var request = new CreateTableRequest { AttributeDefinitions = new List<AttributeDefinition> { new AttributeDefinition { AttributeName = "ProductName", AttributeType = ScalarAttributeType.S //String }, new AttributeDefinition { AttributeName = "ProductQuantity", AttributeType = ScalarAttributeType.N //Number } }, KeySchema = new List<KeySchemaElement> { new KeySchemaElement { AttributeName = "ProductName", KeyType = "HASH" }, new KeySchemaElement { AttributeName = "ProductQuantity", KeyType = "RANGE" } }, ProvisionedThroughput = new ProvisionedThroughput { ReadCapacityUnits = 5, WriteCapacityUnits = 5 }, TableName = tableName }; await _dynamoDbClient.CreateTableAsync(request); WaitUntilTableReady(tableName); } public void WaitUntilTableReady(string tableName) { string status = null; do { Thread.Sleep(5000); try { var res = _dynamoDbClient.DescribeTableAsync(tableName); status = res.Result.Table.TableStatus; } catch (ResourceNotFoundException) { Console.WriteLine(status); } } while (status != "ACTIVE"); { Console.WriteLine("Tabela criada com sucesso !!!"); } } } }
AWSProductListDynamoDB.Libs – AWSProductListDynamoDB – Models – DynamoDbTableItems.cs
using System; using System.Collections.Generic; using System.Text; namespace AWSProductListDynamoDb.Libs.Models { public class DynamoDbTableItems { public IEnumerable Items { get; set; } } }
AWSProductListDynamoDB.Libs – AWSProductListDynamoDB – Models – Item.cs
using System; using System.Collections.Generic; using System.Text; namespace AWSProductListDynamoDb.Libs.Models { public class Item { public string productName { get; set; } public int productQuantity { get; set; } } }
AWSProductListDynamoDB.Libs – AWSProductListDynamoDB – IInsertItem.cs (Interface)
using System.Threading.Tasks; namespace AWSProductListDynamoDb.Libs.AWSProductListDynamoDb { public interface IInsertItem { Task AddNewEntry(string productName, int productQuantity); } }
AWSProductListDynamoDB.Libs – AWSProductListDynamoDB – InsertItem.cs (Insere os dados no banco de dados)
using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace AWSProductListDynamoDb.Libs.AWSProductListDynamoDb { public class InsertItem : IInsertItem { private static readonly string tableName = Environment.GetEnvironmentVariable("AWS_CONTENT"); private readonly IAmazonDynamoDB _dynamoDbClient; public InsertItem(IAmazonDynamoDB dynamoDbClient) { _dynamoDbClient = dynamoDbClient; } public async Task AddNewEntry(string productName, int productQuantity) { try { var queryRequest = RequestBuilder(productName, productQuantity); await PutItemAsync(queryRequest); } catch (InternalServerErrorException) { Console.WriteLine("Erro 1"); } catch (ResourceNotFoundException) { Console.WriteLine("Erro 2"); } catch (Exception ex) { Console.WriteLine(ex.InnerException.StackTrace.ToString()); } } private PutItemRequest RequestBuilder(string productName, int productQuantity) { Dictionary<string, AttributeValue> attributes = new Dictionary<string, AttributeValue>(); attributes["ProductName"] = new AttributeValue { S = productName.ToString() }; attributes["ProductQuantity"] = new AttributeValue { N = productQuantity.ToString() }; return new PutItemRequest { TableName = tableName, Item = attributes }; } private async Task PutItemAsync(PutItemRequest request) { try { await _dynamoDbClient.PutItemAsync(request); } catch (Exception ex) { Console.WriteLine("Erro: " + ex.InnerException.StackTrace.ToString()); } } } }
AWSProductListDynamoDB.Libs – AWSProductListDynamoDB – IQueryItem.cs (IInterface)
using System.Threading.Tasks; using AWSProductListDynamoDb.Libs.Models; namespace AWSProductListDynamoDb.Libs.AWSProductListDynamoDb { public interface IQueryItem { Task GetItems(string? productName); } }
AWSProductListDynamoDB.Libs – AWSProductListDynamoDB – QueryItem.cs (Busca os dados no banco de dados)
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; using AWSProductListDynamoDb.Libs.Models; namespace AWSProductListDynamoDb.Libs.AWSProductListDynamoDb { public class QueryItem : IQueryItem { private readonly IAmazonDynamoDB _dynamoDbClient; private static readonly string tableName = Environment.GetEnvironmentVariable("AWS_CONTENT"); public QueryItem(IAmazonDynamoDB dynamoDbClient) { _dynamoDbClient = dynamoDbClient; } public async Task<DynamoDbTableItems> GetItems(string productName) { var queryRequest = RequestBuilder(productName); var result = await ScanAsync(queryRequest); return new DynamoDbTableItems { Items = result.Items.Select(Map).ToList() }; } private Item Map(Dictionary<string, AttributeValue> result) { return new Item { productName = result["ProductName"].S, productQuantity = Convert.ToInt32(result["ProductQuantity"].N) }; } private async Task<ScanResponse> ScanAsync(ScanRequest request) { var response = await _dynamoDbClient.ScanAsync(request); return response; } private ScanRequest RequestBuilder(string? productName) { if (productName.Length <= 0) { return new ScanRequest { TableName = tableName }; } return new ScanRequest { TableName = tableName, ExpressionAttributeValues = new Dictionary<string, AttributeValue> { { ":v_ProductName", new AttributeValue { S = productName } } }, FilterExpression = "ProductName = :v_ProductName", ProjectionExpression = "ProductName, ProductQuantity" }; } } }
AWSProductListDynamoDB.Libs – AWSProductListDynamoDB – IUpdateItem.cs (Interface)
using System.Threading.Tasks; using AWSProductListDynamoDb.Libs.Models; namespace AWSProductListDynamoDb.Libs.AWSProductListDynamoDb { public interface IUpdateItem { Task Update(string productName, int productQuantity); } }
AWSProductListDynamoDB.Libs – AWSProductListDynamoDB – UpdateItem.cs (Altera os dados no banco de dados)
using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; using AWSProductListDynamoDb.Libs.Models; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace AWSProductListDynamoDb.Libs.AWSProductListDynamoDb { public class UpdateItem : IUpdateItem { private readonly IQueryItem _queryItem; private static readonly string tableName = Environment.GetEnvironmentVariable("AWS_CONTENT"); private readonly IAmazonDynamoDB _dynamoDbClient; public UpdateItem(IQueryItem queryItem, IAmazonDynamoDB dynamoDbClient) { _queryItem = queryItem; _dynamoDbClient = dynamoDbClient; } public async Task<Item> Update(string productName, int productQuantity) { var response = await _queryItem.GetItems(productName); var currentQuantity = response.Items.Select(p => p.productQuantity).FirstOrDefault(); var request = RequestBuilder(productName, productQuantity, currentQuantity); var result = await UpdateItemAsync(request); return new Item { productName = result.Attributes["ProductName"].S, productQuantity = Convert.ToInt32(result.Attributes["ProductQuantity"].N) }; } private UpdateItemRequest RequestBuilder(string productName, int productQuantity, int currentQuantity) { var request = new UpdateItemRequest { Key = new Dictionary<string, AttributeValue> { { "ProductName", new AttributeValue { S = productName } }//, //{ // "ProductQuantity", new AttributeValue // { // N = currentQuantity.ToString() // } // } }, ExpressionAttributeNames = new Dictionary<string, string> { {"#P", "ProductQuantity"} }, ExpressionAttributeValues = new Dictionary<string, AttributeValue> { { ":newquantity", new AttributeValue { N = productQuantity.ToString() } }, { ":currquantity", new AttributeValue { N = currentQuantity.ToString() } } }, UpdateExpression = "SET #P = :newquantity", ConditionExpression = "#P = :currquantity", TableName = tableName, ReturnValues = "ALL_NEW" }; return request; } private async Task<UpdateItemResponse> UpdateItemAsync(UpdateItemRequest request) { var response = await _dynamoDbClient.UpdateItemAsync(request); return response; } } }
AWSProductListDynamoDB.Libs – AWSProductListDynamoDB – IDeleteItem.cs (Interface)
using System.Threading.Tasks; namespace AWSProductListDynamoDb.Libs.AWSProductListDynamoDb { public interface IDeleteItem { Task DeleteEntry(string productName, int productQuantity); } }
AWSProductListDynamoDB.Libs – AWSProductListDynamoDB – DeleteItem.cs (Exclui dados do banco de dados)
using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace AWSProductListDynamoDb.Libs.AWSProductListDynamoDb { public class DeleteItem : IDeleteItem { private static readonly string tableName = Environment.GetEnvironmentVariable("AWS_CONTENT"); private readonly IAmazonDynamoDB _dynamoDbClient; public DeleteItem(IAmazonDynamoDB dynamoDbClient) { _dynamoDbClient = dynamoDbClient; } public async Task DeleteEntry(string productName, int productQuantity) { try { var queryRequest = RequestBuilder(productName, productQuantity); await DeleteItemAsync(queryRequest); } catch (InternalServerErrorException) { Console.WriteLine("Erro 1"); } catch (ResourceNotFoundException) { Console.WriteLine("Erro 2"); } catch (Exception ex) { Console.WriteLine(ex.InnerException.StackTrace.ToString()); } } private DeleteItemRequest RequestBuilder(string productName, int productQuantity) { var item = new Dictionary<string, AttributeValue> { {"ProductName", new AttributeValue {S = productName}} }; return new DeleteItemRequest { TableName = tableName, Key = item }; } private async Task DeleteItemAsync(DeleteItemRequest request) { await _dynamoDbClient.DeleteItemAsync(request); } } }
Com todas as classes criadas em nosso projeto, vamos compilá-lo ! CTRL+SHIFT+B e depois F5 para executarmos ! 🙂

Agora é hora de testarmos. Vamos abrir o postman e começar !
Primeiramente vamos inserir um novo item: 1 Motherboard
Clique em Send e veja o resultado:

Sem erros ! Vejamos agora no DynamoDb:

Agora vamos alterar a quantidade do Monitor:


Agora vamos excluir o Keyboard:


E por último, vamos pesquisar o Mouse:

É isso aí pessoal ! Espero que esse artigo seja útil a vocês !
Abraço
Fontes

Formado em Gestão em Tecnologia da Informação, com sólidos conhecimentos em SQL, PL/SQL, Oracle Forms, Reports e E-Business Suite (AP,AR e GL).
Foi durante 3 anos gerente de tecnologia de grande empresa do setor de saúde, e atualmente atua como Analista de Sistema Sênior na Scania Latin America e também como Diretor-fundador do GPO (Grupo de Profissionais Oracle).
A parte de “criar um novo Projeto do tipo Class Library (.NET Core) chamado AWSProductListDynamoDb” ficou muito confuso. Os nomes dos projetos se repetem e do nada é para selecionar a opção “AWSProductListDynamoDb.Libs”, que até então não foi criada.
Boa tarde Andreson ! Muito obrigado pelo retorno !
Vou analisar e fazer a correção. Valeu mesmo por chamar minha atenção a isso. 🙂