diff --git a/material-sistemas-distribuidos/src/assets/img/49_vsc_using_mysqlconnector.png b/material-sistemas-distribuidos/src/assets/img/49_vsc_using_mysqlconnector.png new file mode 100644 index 0000000..fc64d8d Binary files /dev/null and b/material-sistemas-distribuidos/src/assets/img/49_vsc_using_mysqlconnector.png differ diff --git a/material-sistemas-distribuidos/src/css/code.css b/material-sistemas-distribuidos/src/css/code.css index be8c532..e359930 100644 --- a/material-sistemas-distribuidos/src/css/code.css +++ b/material-sistemas-distribuidos/src/css/code.css @@ -29,4 +29,5 @@ pre:has(code) { border-radius: 10px; margin-top: var(--paragraph-spacing); margin-bottom: var(--paragraph-spacing); + overflow-x: auto; } \ No newline at end of file diff --git a/material-sistemas-distribuidos/src/routes/+page.svelte b/material-sistemas-distribuidos/src/routes/+page.svelte index f582aba..a4a8812 100644 --- a/material-sistemas-distribuidos/src/routes/+page.svelte +++ b/material-sistemas-distribuidos/src/routes/+page.svelte @@ -33,11 +33,7 @@
- Os endpoints que escrevemos no capítulo anterior não fazem nada além de exibir os dados que você envia na requisição. - Para que possamos fazer com que eles persistam os dados enviados, vamos utilizar um banco de dados. - Neste capítulo, vamos criar a comunicação entre o nosso projeto e esse banco de dados. + Os endpoints que escrevemos no capítulo anterior não fazem nada além de devolver os dados exatamente como você os envia na requisição. + Para que possamos fazer com que estes dados sejam persistidos (ou seja, registrados em algum lugar onde possamos consultá-los futuramente), vamos utilizar um banco de dados. + Neste capítulo, vamos criar a comunicação entre o nosso projeto e um gerenciador de banco de dados MariaDB, + um fork open-source do MySQL criado e mantido pelos seus desenvolvedores originais.
- Vamos utilizar um banco de dados MySQL, acessível através das credenciais:
+ Vamos utilizar um banco de dados previamente criado para persistir e consultar livros.
+ Para acessá-lo a partir do nosso projeto, precisamos de uma biblioteca. Como o MariaDB é um fork do
+ MySQL, os dois são normalmente intercambiáveis. Bibliotecas desenvolvidas para o MySQL quase sempre irão funcionar com o MariaDB, com exceção de algumas
+ funcionalidades específicas. A documentação do MariaDB recomenda a utilização da
+ biblioteca MySqlConnector, portanto, iremos instalá-la no nosso projeto.
sistemasdistribuidos.alunoeW03avS7M8kOUL1A9bZWW2RTIfzEI1Di- Para gerenciar conexões e realizar consultas, vamos utilizar a biblioteca MySqlConnector. Abra - o terminal no seu projeto, e execute o comando: + Abra o terminal no seu projeto, e execute o comando:
{`cd Biblioteca
dotnet add package MySqlConnector`}
- Note que o comando precisa ser executado no diretório onde existe o arquivo Biblioteca.csproj,
- por isso o comando cd Biblioteca.
+ Note que o comando precisa ser executado no diretório onde existe o arquivo Biblioteca.csproj. Para confirmar se a biblioteca foi adicionada corretamente,
+ adicione a seguinte linha no topo do seu Program.cs.
+
{`using MySqlConnector;`}
+
+ + Se o Visual Studio Code não mostra nenhum erro, então a instalação foi bem sucedida. +
+ +
+ O banco de dados que vamos utilizar possui uma tabela com o nome Livro. Esta tabela possui os seguintes campos:
+
{`Isbn VARCHAR(255) PRIMARY KEY NOT NULL,
+Titulo VARCHAR(512) NOT NULL,
+Autor TEXT NOT NULL,
+Genero TEXT NOT NULL,
+Descricao TEXT NOT NULL,
+Foto TEXT NOT NULL,
+Keywords TEXT NOT NULL,
+Ativo BOOLEAN NOT NULL DEFAULT 0,
+CriadoEm DATETIME NOT NULL,
+AtualizadoEm DATETIME NOT NULL,`}
+
+
+ Vamos criar duas novas classes no projeto: uma classe Livro que irá espelhar a tabela no banco de dados, e uma classe LivroRepository que irá possuir
+ métodos para a consulta e manipulação dos registros nessa tabela. Mas antes disso, vamos limpar o Program.cs, removendo partes do código que escrevemos anteriormente apenas para testar, e que
+ não vamos precisar mais, deixando-o pronto para utilizarmos as novas classes que iremos criar.
+
+ Apague todo o conteúdo do seu Program.cs e deixe-o exatamente assim:
+
{`var builder = WebApplication.CreateBuilder(args);
+
+var app = builder.Build();
+
+// Obtém uma lista com os livros registrados.
+app.MapGet("/livros", () =>
+{
+
+});
+
+// Cria um novo livro.
+app.MapPost("/livros", () =>
+{
+
+});
+
+// Edita um livro.
+app.MapPut("/livros/{isbn}", () =>
+{
+
+});
+
+// Obtém os dados de um livro individual.
+app.MapGet("/livros/{isbn}", () =>
+{
+
+});
+
+// Remove um livro.
+app.MapDelete("/livros/{isbn}", () =>
+{
+
+});
+
+app.Run();`}
+
+
+ Tendo feito isso, crie uma pasta Biblioteca/Models e, dentro dela, crie um arquivo Livro.cs.
+ Dentro deste arquivo, vamos criar uma classe Livro com campos que refletem os campos na tabela do banco de dados.
+
{`namespace Biblioteca.Models;
+
+public class Livro
+{
+ public string? Isbn { get; set; }
+ public string? Titulo { get; set; }
+ public string? Autor { get; set; }
+ public string? Genero { get; set; }
+ public string? Descricao { get; set; }
+ public string? Foto { get; set; }
+ public string? Keywords { get; set; }
+ public bool Ativo { get; set; }
+ public DateTime CriadoEm { get; set; }
+ public DateTime AtualizadoEm { get; set; }
+}`}
+
+
+ Agora, crie uma pasta Biblioteca/Repositories e, dentro dela, crie um arquivo LivroRepository.cs,
+ onde existirá a classe LivroRepository.
+
{`namespace Biblioteca.Repositories;
+
+public class LivroRepository
+{
+
+}`}
+
+ + Nesta classe, vamos precisar de uma constante que vai conter uma string de conexão, que servirá para indicar para o MySqlConnector o endereço e as credenciais do banco de dados que vamos utilizar. +
+ +{`namespace Biblioteca.Repositories;
+
+public class LivroRepository
+{
+ private const string ConnString = "Server=gbrl.dev;Port=5306;User ID=sistemasdistribuidos.aluno;Password=eW03avS7M8kOUL1A9bZWW2RTIfzEI1Di;Database=sistemasdistribuidos";
+}`}
+
+ + Vamos escrever nesta classe métodos para as operações de listagem, consulta, inclusão, edição e remoção de livros na base de dados. Vamos começar pela + operação de listagem. +
+ +{`public async Task> Obter(int pagina)
+{
+
+}`}
+
+
+ Este método é público (public), assíncrono (async Task), retorna um IEnumerable<Livro>, e recebe
+ como parâmetro um valor inteiro que representa a página da consulta
+
+ Um método público é um método que pode ser executado por qualquer outro método no projeto. O nosso método precisa ser público pois ele vai ser chamado
+ nos endpoints que estão no Program.cs.
+
+ O valor de retorno será um IEnumerable<Livro>. Um IEnumerable é uma interface do dotnet que representa
+ qualquer sequência iterável de objetos. Por exemplo, um objeto List<T> ou um vetor T[] são iteráveis,
+ portanto podem ser retornados neste método.
+
+ Um método assíncrono é um método que pode ser executado assíncronamente, de forma independente do fluxo de execução do método que o chamou. + Nós não vamos utilizar nenhuma forma de paralelismo, mas precisamos que nosso método seja assíncrono mesmo assim. +
+ +
+ O parâmetro int pagina recebido indica a página da consulta. Você provavelmente já usou algum site onde interagiu com uma listagem de um conteúdo que era feita
+ através de páginas, a ideia aqui é a mesma. Imagine que temos três milhões de livros na base de dados, nós não queremos que nosso método leia todos eles.
+ Ao invés disso, vamos retornar os livros de forma paginada, com N livros por página.
+
+ Agora, vamos implementar o método, que ficará assim: +
+ +{`public async Task> Obter(int pagina)
+{
+ using var conn = new MySqlConnection(ConnString);
+ using var cmd = conn.CreateCommand();
+
+ await conn.OpenAsync();
+
+ var take = 30;
+ var offset = take * Math.Max(pagina-1, 0);
+ var lista = new List();
+
+ cmd.CommandText = "SELECT Isbn, Titulo, Autor, Genero, Descricao, Foto, Keywords, Ativo, CriadoEm, AtualizadoEm FROM Livro ORDER BY CriadoEm LIMIT @offset,@take";
+ cmd.Parameters.AddWithValue("offset", offset);
+ cmd.Parameters.AddWithValue("take", take);
+
+ using var reader = await cmd.ExecuteReaderAsync();
+
+ while (await reader.ReadAsync())
+ {
+ lista.Add(new()
+ {
+ Isbn = reader.GetString(0),
+ Titulo = reader.GetString(1),
+ Autor = reader.GetString(2),
+ Genero = reader.GetString(3),
+ Descricao = reader.GetString(4),
+ Foto = reader.GetString(5),
+ Keywords = reader.GetString(6),
+ Ativo = reader.GetBoolean(7),
+ CriadoEm = reader.GetDateTime(8),
+ AtualizadoEm = reader.GetDateTime(9),
+ });
+ }
+
+ return lista;
+}`}
+
+ + Vamos analisar cada trecho do método. +
+ +{`using var conn = new MySqlConnection(ConnString);
+using var cmd = conn.CreateCommand();
+
+await conn.OpenAsync();`}
+
+
+ As variáveis conn e cmd representam, respectivamente, a conexão com o banco de dados, e o comando que iremos executar.
+ A declaração de ambas é feita com using pois estes dois objetos alocam recursos que devem ser liberados ao fim da execução do método. O
+ using é uma construção da linguagem que garante que isso aconteça sempre.
+
+ Com await conn.OpenAsync() fazemos com que a conexão com o banco de dados seja aberta.
+
+ Note que conn.OpenAsync() também é um método assíncrono (pois ele retorna Task). Por isso, devemos prefixar a chamada deste método com
+ um await. Isto é uma indicação de que queremos aguardar a conclusão de conn.OpenAsync() antes de prosseguirmos com a execução do nosso método.
+ E para utilizarmos um await, o nosso método deve ser também, obrigatoriamente, assíncrono. Por isso o declaramos como async Task.
+
+ O modelo de programação assíncrona é uma característica que
+ é praticamente ubíqua no dotnet, mas não precisamos nos aprofundar nela por enquanto. Basta saber que devemos utilizar await em métodos assíncronos, e
+ que se o utilizarmos, o nosso método deve ser também assíncrono, sendo declarado com async Task.
+
{`var take = 30;
+var offset = take * Math.Max(pagina-1, 0);
+var lista = new List();`}
+
+
+ Em seguida criamos três variáveis. take é o número de livros que queremos obter por página.
+ offset é um número que indica quantos registros pular para obter a página desejada. Por exemplo, se
+ desejamos a primeira página, devemos obter os primeiros livros. Já se quisermos a segunda página,
+ vamos pular livros e depois obter os próximos.
+ Generalizando, para obter a página (considerando que as páginas começam do ), com elementos, precisamos pular livros.
+
+ Como int pagina é um valor inteiro que pode assumir valores negativos, fazemos Math.Max(pagina-1, 0) como uma garantia para que offset nunca tenha um valor menor do que zero.
+
+ A variável lista é uma lista de objetos Livro que irá guardar todos os resultados da consulta, e depois será retornada.
+
{`cmd.CommandText = "SELECT Isbn, Titulo, Autor, Genero, Descricao, Foto, Keywords, Ativo, CriadoEm, AtualizadoEm FROM Livro ORDER BY CriadoEm LIMIT @offset,@take";
+cmd.Parameters.AddWithValue("offset", offset);
+cmd.Parameters.AddWithValue("take", take);`}
+
+
+ A primeira linha adiciona a consulta que queremos executar à variável cmd. As duas linhas seguintes adicionam take e offset
+ como parâmetros da consulta.
+
{`using var reader = await cmd.ExecuteReaderAsync();`}
+
+
+ Enfim, executamos a consulta através de cmd.ExecuteReaderAsync(). Este método é assíncrono, portanto deve ser prefixado por await, e além disso também aloca
+ recursos que devem ser liberados com using. O retorno desta função é um objeto onde podemos ler os resultados da consulta.
+
{`while (await reader.ReadAsync())
+{
+ lista.Add(new()
+ {
+ Isbn = reader.GetString(0),
+ Titulo = reader.GetString(1),
+ Autor = reader.GetString(2),
+ Genero = reader.GetString(3),
+ Descricao = reader.GetString(4),
+ Foto = reader.GetString(5),
+ Keywords = reader.GetString(6),
+ Ativo = reader.GetBoolean(7),
+ CriadoEm = reader.GetDateTime(8),
+ AtualizadoEm = reader.GetDateTime(9),
+ });
+}`}
+
+
+ Este laço é repetido enquanto await reader.ReadAsync() retornar true, o que significa que existe um resultado da consulta que pode ser lido.
+ Quando isso acontece, podemos ler as colunas do resultado através dos métodos de reader, como reader.GetString(0). O número passado como
+ parâmetro para estas funções indica a posição da coluna lida. Por exemplo, na consulta que realizamos (SELECT Isbn, Titulo, ...), a coluna Isbn é a primeira,
+ portanto, para ler este valor, devemos executar reader.GetString(0). Titulo é a segunda, então o seu valor é obtido através de reader.GetString(1), e assim
+ sucessivamente.
+
+ Realizamos a leitura de todas as colunas do resultado, e montamos um objeto do tipo Livro, o qual adicionamos à lista lista, que é o valor de retorno do método de listagem.
+
{`return lista;`}
+
+
+ Para testarmos este método, vamos chamá-lo no endpoint de listagem em Program.cs
+
{`// Obtém uma lista com os livros registrados.
+app.MapGet("/livros", async () =>
+{
+ var repo = new LivroRepository();
+ var res = await repo.Obter(pagina: 1);
+
+ return res;
+});`}
+
+
+ Como LivroRepository.Obter(int) é assíncrono, o endpoint também deve ser. Fazemos isso adicionando async na declaração da sua função.
+
{`app.MapGet("/livros", async () => ...`}
+
+ + Agora execute este endpoint. Se tudo deu certo, você verá alguns livros cadastrados no retorno. +
+ ++ O ISBN é a chave primária da tabela. Vamos implementar um método que consulta um único livro a partir deste dado. +
+ +{`public async Task Obter(string isbn)
+{
+ using var conn = new MySqlConnection(ConnString);
+ using var cmd = conn.CreateCommand();
+
+ await conn.OpenAsync();
+
+ cmd.CommandText = "SELECT Isbn, Titulo, Autor, Genero, Descricao, Foto, Keywords, Ativo, CriadoEm, AtualizadoEm FROM Livro WHERE Isbn=@isbn";
+ cmd.Parameters.AddWithValue("isbn", isbn);
+
+ using var reader = await cmd.ExecuteReaderAsync();
+
+ var existe = await reader.ReadAsync();
+
+ if (!existe)
+ {
+ throw new Exception($"Livro com ISBN {isbn} não encontrado");
+ }
+
+ return new()
+ {
+ Isbn = reader.GetString(0),
+ Titulo = reader.GetString(1),
+ Autor = reader.GetString(2),
+ Genero = reader.GetString(3),
+ Descricao = reader.GetString(4),
+ Foto = reader.GetString(5),
+ Keywords = reader.GetString(6),
+ Ativo = reader.GetBoolean(7),
+ CriadoEm = reader.GetDateTime(8),
+ AtualizadoEm = reader.GetDateTime(9),
+ };
+}`}
+
+ + As três primeiras linhas deste método, assim como no de listagem, abre uma conexão com o banco de dados. +
+ +{`cmd.CommandText = "SELECT Isbn, Titulo, Autor, Genero, Descricao, Foto, Keywords, Ativo, CriadoEm, AtualizadoEm FROM Livro WHERE Isbn=@isbn";
+cmd.Parameters.AddWithValue("isbn", isbn);`}
+
+
+ A consulta é montada na variável cmd, o parâmetro isbn é adicionado ao comando.
+
{`using var reader = await cmd.ExecuteReaderAsync();
+
+var existe = await reader.ReadAsync();
+
+if (!existe)
+{
+ throw new Exception($"Livro com ISBN {isbn} não encontrado");
+}
+
+return new()
+{
+ Isbn = reader.GetString(0),
+ Titulo = reader.GetString(1),
+ Autor = reader.GetString(2),
+ Genero = reader.GetString(3),
+ Descricao = reader.GetString(4),
+ Foto = reader.GetString(5),
+ Keywords = reader.GetString(6),
+ Ativo = reader.GetBoolean(7),
+ CriadoEm = reader.GetDateTime(8),
+ AtualizadoEm = reader.GetDateTime(9),
+};`}
+
+
+ Assim como na listagem, await cmd.ExecuteReaderAsync() é chamado para ler os resultados da consulta.
+ Como esperamos que só haja um registro com aquele ISBN, executamos await reader.ReadAsync() apenas uma vez.
+ Se este método retorna false, é por que nenhum registro foi encontrado com aquele ISBN. Neste caso, lançamos um erro.
+ Do contrário, utilizamos os métodos de leitura do registro lido para montar um objeto Livro que será retornado.
+
+ Vamos chamar este novo método ao endpoint de consulta no Program.cs.
+
{`// Obtém os dados de um livro individual.
+app.MapGet("/livros/{isbn}", async (string isbn) =>
+{
+ var repo = new LivroRepository();
+ var res = await repo.Obter(isbn);
+
+ return res;
+});`}
+
+ + Vamos implementar agora o método de criação de livros: +
+ +{`public async Task Criar(Livro dados)
+{
+
+}`}
+
+
+ Este método recebe um objeto Livro como parâmetro, contendo os dados do novo livro que deve ser inserido no banco de dados. Vamos implementá-lo.
+
{`public async Task Criar(Livro dados)
+{
+ using var conn = new MySqlConnection(ConnString);
+ using var cmd = conn.CreateCommand();
+
+ await conn.OpenAsync();
+
+ var livro = new Livro
+ {
+ Isbn = dados.Isbn?.Trim() ?? "",
+ Titulo = dados.Titulo?.Trim() ?? "",
+ Autor = dados.Autor?.Trim() ?? "",
+ Genero = dados.Genero?.Trim() ?? "",
+ Descricao = dados.Descricao?.Trim() ?? "",
+ Foto = dados.Foto?.Trim() ?? "",
+ Keywords = dados.Keywords?.Trim() ?? "",
+ Ativo = true,
+ CriadoEm = DateTime.Now,
+ AtualizadoEm = default,
+ };
+
+ if (livro.Isbn == "")
+ {
+ throw new Exception("O ISBN do livro é obrigatório.");
+ }
+
+ if (livro.Titulo == "")
+ {
+ throw new Exception("O título do livro é obrigatório.");
+ }
+
+ cmd.CommandText =
+ @"
+ INSERT INTO Livro
+ (Isbn, Titulo, Autor, Genero, Descricao, Foto, Keywords, Ativo, CriadoEm, AtualizadoEm)
+ VALUES
+ (@isbn, @titulo, @autor, @genero, @descricao, @foto, @keywords, @ativo, @criadoem, @atualizadoem)
+ ";
+
+ cmd.Parameters.AddWithValue("isbn", livro.Isbn);
+ cmd.Parameters.AddWithValue("titulo", livro.Titulo);
+ cmd.Parameters.AddWithValue("autor", livro.Autor);
+ cmd.Parameters.AddWithValue("genero", livro.Genero);
+ cmd.Parameters.AddWithValue("descricao", livro.Descricao);
+ cmd.Parameters.AddWithValue("foto", livro.Foto);
+ cmd.Parameters.AddWithValue("keywords", livro.Keywords);
+ cmd.Parameters.AddWithValue("ativo", livro.Ativo);
+ cmd.Parameters.AddWithValue("criadoem", livro.CriadoEm);
+ cmd.Parameters.AddWithValue("atualizadoem", livro.AtualizadoEm);
+
+ await cmd.ExecuteNonQueryAsync();
+
+ return livro;
+}`}
+
+ + O método se inicia com a abertura da conexão com o banco de dados. +
+ +{`var livro = new Livro
+{
+ Isbn = dados.Isbn?.Trim() ?? "",
+ Titulo = dados.Titulo?.Trim() ?? "",
+ Autor = dados.Autor?.Trim() ?? "",
+ Genero = dados.Genero?.Trim() ?? "",
+ Descricao = dados.Descricao?.Trim() ?? "",
+ Foto = dados.Foto?.Trim() ?? "",
+ Keywords = dados.Keywords?.Trim() ?? "",
+ Ativo = true,
+ CriadoEm = DateTime.Now,
+ AtualizadoEm = default,
+};`}
+
+ + Criamos uma variável que será uma cópia do objeto recebido no parâmetro. Fazemos isso para tratar os dados do livro, formatando-os, e assegurando + que os seus campos assumam os valores corretos. Este objeto é contém os dados que serão enviados para o banco de dados. +
+ +
+ O símbolo ? é o operador de propagação de nulos. Em uma cadeia de expressões de acesso de membros em um objeto, um propagador de nulo faz com que, se um membro for nulo, o resultado de toda a expressão seja nula.
+ Por exemplo, dados.Descricao é um valor do tipo string, que pode assumir um valor nulo. Na expressão dados.Descricao.Trim(), se dados.Descricao for nulo, um
+ NullReferenceException será lançado, pois não é possível executar um método ou acessar uma propriedade em um objeto nulo. Já na expressão dados.Descricao?.Trim(), se dados.Descricao
+ for nulo, como há o operador de propagação de nulos, .Trim() não será executado, e o resultado da expressão será null.
+
+ De forma semelhante, o ?? age sobre dois operandos. Na expressão var x = A ?? B, a variável x irá assumir o valor de A apenas se ela não for nula. Do contrário,
+ x assumirá o valor de B.
+
+ Portanto, na expressão dados.Descricao?.Trim() ?? "", se dados.Descricao for nulo, então a expressão ficará null ?? "", que terá como resultado a string vazia "". Ou seja,
+ Se dados.Descricao for nulo, livro.Descricao será uma string vazia, e nunca assumirá null.
+
+ Fazemos isso nos campos do objeto livro para garantir que nenhum deles seja nulo, já que no banco de dados, todos os campos da tabela são NOT NULL.
+
{`if (livro.Isbn == "")
+{
+ throw new Exception("O ISBN do livro é obrigatório.");
+}
+
+if (livro.Titulo == "")
+{
+ throw new Exception("O título do livro é obrigatório.");
+}`}
+
+ + Fazemos algumas validações básicas, para assegurar que os dados obrigatórios do registro tenham sido preenchidos. +
+ +{`cmd.CommandText =
+ @"
+ INSERT INTO Livro
+ (Isbn, Titulo, Autor, Genero, Descricao, Foto, Keywords, Ativo, CriadoEm, AtualizadoEm)
+ VALUES
+ (@isbn, @titulo, @autor, @genero, @descricao, @foto, @keywords, @ativo, @criadoem, @atualizadoem)
+ ";
+
+cmd.Parameters.AddWithValue("isbn", livro.Isbn);
+cmd.Parameters.AddWithValue("titulo", livro.Titulo);
+cmd.Parameters.AddWithValue("autor", livro.Autor);
+cmd.Parameters.AddWithValue("genero", livro.Genero);
+cmd.Parameters.AddWithValue("descricao", livro.Descricao);
+cmd.Parameters.AddWithValue("foto", livro.Foto);
+cmd.Parameters.AddWithValue("keywords", livro.Keywords);
+cmd.Parameters.AddWithValue("ativo", livro.Ativo);
+cmd.Parameters.AddWithValue("criadoem", livro.CriadoEm);
+cmd.Parameters.AddWithValue("atualizadoem", livro.AtualizadoEm);`}
+
+
+ Assim como no método de listagem e da consulta por ISBN, montamos o SQL com um INSERT, e depois adicionamos os parâmetros do comando, com os
+ dados do objeto livro.
+
{`await cmd.ExecuteNonQueryAsync();
+
+return livro;`}
+
+
+ Em seguida, executamos o insert através de await cmd.ExecuteNonQueryAsync(). Como a operação é de escrita, não há resultado a ser lido.
+
+ Adicionando-o ao endpoint de criação, temos: +
+ +{`// Cria um novo livro.
+app.MapPost("/livros", async (Livro livro) =>
+{
+ var repo = new LivroRepository();
+ var res = await repo.Criar(livro);
+
+ return res;
+});`}
+
+ + O método de edição é similar ao de criação. +
+ +{`public async Task Editar(string isbn, Livro dados)
+{
+ using var conn = new MySqlConnection(ConnString);
+ using var cmd = conn.CreateCommand();
+
+ await conn.OpenAsync();
+
+ var livro = await Obter(isbn);
+
+ livro.Titulo = dados.Titulo?.Trim() ?? "";
+ livro.Autor = dados.Autor?.Trim() ?? "";
+ livro.Genero = dados.Genero?.Trim() ?? "";
+ livro.Descricao = dados.Descricao?.Trim() ?? "";
+ livro.Foto = dados.Foto?.Trim() ?? "";
+ livro.Keywords = dados.Keywords?.Trim() ?? "";
+ livro.AtualizadoEm = DateTime.Now;
+
+ cmd.CommandText =
+ @"
+ UPDATE Livro SET
+ Titulo=@titulo, Autor=@autor, Genero=@genero, Descricao=@descricao, Foto=@foto, Keywords=@keywords, AtualizadoEm=@atualizadoem
+ WHERE Isbn=@isbn
+ ";
+
+ cmd.Parameters.AddWithValue("isbn", isbn);
+ cmd.Parameters.AddWithValue("titulo", livro.Titulo);
+ cmd.Parameters.AddWithValue("autor", livro.Autor);
+ cmd.Parameters.AddWithValue("genero", livro.Genero);
+ cmd.Parameters.AddWithValue("descricao", livro.Descricao);
+ cmd.Parameters.AddWithValue("foto", livro.Foto);
+ cmd.Parameters.AddWithValue("keywords", livro.Keywords);
+ cmd.Parameters.AddWithValue("atualizadoem", livro.AtualizadoEm);
+
+ await cmd.ExecuteNonQueryAsync();
+
+ return livro;
+}`}
+
+ + O método recebe dois parâmetros, um isbn que identifica o livro que será editado, e um objeto com os novos dados do livro. +
+ +{`var livro = await Obter(isbn);
+
+livro.Titulo = dados.Titulo?.Trim() ?? "";
+livro.Autor = dados.Autor?.Trim() ?? "";
+livro.Genero = dados.Genero?.Trim() ?? "";
+livro.Descricao = dados.Descricao?.Trim() ?? "";
+livro.Foto = dados.Foto?.Trim() ?? "";
+livro.Keywords = dados.Keywords?.Trim() ?? "";
+livro.AtualizadoEm = DateTime.Now;`}
+
+
+ Chamamos a função Obter(string) que criamos para obter do banco de dados o livro que será editado. Em seguida, atribuímos a este objeto
+ os valores do objeto dados, formatando-os devidamente.
+
{`cmd.CommandText =
+ @"
+ UPDATE Livro SET
+ Titulo=@titulo, Autor=@autor, Genero=@genero, Descricao=@descricao, Foto=@foto, Keywords=@keywords, AtualizadoEm=@atualizadoem
+ WHERE Isbn=@isbn
+ ";
+
+cmd.Parameters.AddWithValue("isbn", isbn);
+cmd.Parameters.AddWithValue("titulo", livro.Titulo);
+cmd.Parameters.AddWithValue("autor", livro.Autor);
+cmd.Parameters.AddWithValue("genero", livro.Genero);
+cmd.Parameters.AddWithValue("descricao", livro.Descricao);
+cmd.Parameters.AddWithValue("foto", livro.Foto);
+cmd.Parameters.AddWithValue("keywords", livro.Keywords);
+cmd.Parameters.AddWithValue("atualizadoem", livro.AtualizadoEm);
+
+await cmd.ExecuteNonQueryAsync();
+
+return livro;`}
+
+ + Em seguida, montamos o comando um UPDATE, atribuímos os parâmetros, executamos a consulta e retornamos o livro que foi editado. +
+ ++ Vamos agora chamar este método no seu endpoint. +
+ +{`// Edita um livro.
+app.MapPut("/livros/{isbn}", async (string isbn, Livro livro) =>
+{
+ var repo = new LivroRepository();
+ var res = await repo.Editar(isbn, livro);
+
+ return res;
+});`}
+
+
+ Nós não iremos remover livros do banco de dados, ao invés disso, a operação de remoção irá
+ editar apenas o campo Ativo de um livro, alterando-o para false.
+ Portanto, o método de remover será basicamente uma edição.
+
{`public async Task Desativar(string isbn)
+{
+ using var conn = new MySqlConnection(ConnString);
+ using var cmd = conn.CreateCommand();
+
+ await conn.OpenAsync();
+
+ cmd.CommandText = "UPDATE Livro SET Ativo=false WHERE Isbn=@isbn";
+ cmd.Parameters.AddWithValue("isbn", isbn);
+
+ await cmd.ExecuteNonQueryAsync();
+}`}
+
+ + Vamos adicioná-lo agora ao seu endpoint. +
+ +{`// Remove um livro.
+app.MapDelete("/livros/{isbn}", async (string isbn) =>
+{
+ var repo = new LivroRepository();
+
+ await repo.Desativar(isbn);
+});`}
+
+ + Implementamos todas as operações básicas de manipulação dos registros, e os adicionamos aos endpoints da nossa API. Mais ainda precisamos ajustar algumas coisas.
/livros?pagina=1), para que seja possível obter as próximas páginas além da primeira.
+ Ativo=false) ainda aparecem. Não queremos que isso aconteça, queremos
+ que os livros inativos não apareçam. Como você poderia fazer isso?
+ diff --git a/material-sistemas-distribuidos/src/routes/projeto_de_api/+page.svelte b/material-sistemas-distribuidos/src/routes/projeto_de_api/+page.svelte index 13a419b..1f8a111 100644 --- a/material-sistemas-distribuidos/src/routes/projeto_de_api/+page.svelte +++ b/material-sistemas-distribuidos/src/routes/projeto_de_api/+page.svelte @@ -19,7 +19,7 @@