![]()  | 
    
     Neste artigo eu vamos iniciar a criação de uma aplicação ASP .NET Core MVC usando o Entity Framework Core no Visual Studio.  | 
  
Continuando a terceira parte do artigo vamos criar e ajustar os métodos CRUD - Create , Read, Update e Delete gerados pelo Scaffolding.
Customizando os métodos CRUD
Até o momento temos uma aplicação Web MVC que permite acessar e exibir informações de um banco de dados SQL Server Local DB usando o Entity Framework.
No artigo anterior nós criamos um controlador e as respectivas views para gerenciar as informações sobre estudantes usando o Scaffolding, e, dessa forma agora podemos acessar e exibir os dados dos estudantes, bem como, editar, incluir e excluir suas informações.
Vamos rever e customizar o código CRUD gerado pelo Scaffolding iniciando pela página de detalhes de um estudante: a página Details
Executando a aplicação e clicando no link Detalhes iremos veremos a exibição da página Details abaixo:

Observe que o código gerado pelo Scaffolding para página não esta exibindo as informações sobre a propriedade Matriculas definida na classe Estudante, porque esta propriedade representa uma coleção. A página portanto não esta exibindo as matriculas feitas pelo estudante.
Vamos ajustar isso e exibir a coleção de matriculas em uma tabela HTML. Para começar temos que primeiro alterar o código do método Action Details do controlador EstudantesController.
No código original gerado pelo Scaffolding o método Action Details utiliza o método SingleOrDefaulAsync para recuperar uma única entidade Estudante.
Vamos alterar o código original (que foi comentado) incluindo o código destacado em azul conforme mostrado abaixo:
| 
								                     // GET: Estudantes/Details/5 public async Task<IActionResult> Details(int? id) { if (id == null) { return NotFound(); } 
								                                  //var estudante = await _context.Estudantes                             var estudante = await _context.Estudantes                         if (estudante == null)  | 
							
Alteramos a consulta para agora retornar um estudante e suas matriculas onde os métodos Include e ThenInclude fazem com que o contexto carregue a propriedade de navegação Estudantes.Matriculas e dentro de cada inscrição a propriedade de navegação Matricula.Curso.
O método AsNoTracking melhora o desempenho em cenários onde as entidades retornadas não serão atualizadas no tempo de vida do contexto atual.
Nota: O método de extensão AsNoTracking() retorna uma nova consulta e as entidades retornadas não serão armazenadas em cache pelo contexto (DbContext ou Object Context). Isso significa que o Entity Framework não executa qualquer processamento ou armazenamento adicional das entidades retornadas pela consulta. Observe que não é possível atualizar essas entidades sem anexar ao contexto.
O valor chave (Id) que é passado para o método Details vem dos dados da rota. Os dados de rota são dados que o model binder encontra em um segmento da URL. Por exemplo, a rota padrão especifica os segmentos controller, action e id conforme vemos abaixo:
| 
								
								App.UseMvc (routes => { Routes.MapRoute ( Nome: "default", Template: "{controller = Home} / {action = Index} / {id?}"); });  | 
								Nota : O model binder da ASP.NET Core MVC mapeia dados de solicitações HTTP para parâmetros dos métodos Actions. Os parâmetros podem ser tipos simples, como strings, inteiros ou floats, ou podem ser tipos complexos. | 
						Na URL a seguir, a rota padrão mapeia o Instrutor como 
						sendo o Controller, Index como a 
						sendo a Action e 1 como o id; Estes são valores 
						de dados de rota.
						
						
						Http://localhost:1230/Instrutor/Index/1?CursoID=2021
						
						A última parte do URL ("?CursoID=2021") é um 
						valor de seqüência de caracteres de consulta. O model 
						binder também passará o valor ID para o parâmetro ID 
						do método Details se o passar como um valor de 
						cadeia de consulta:
						
						
						
						
						Http://localhost:1230/Instrutor/Index?Id=1&CursoID=2021
						
						Na página Index, as URLs de hiperlink são criadas por 
						instruções de tag helper na exibição Razor. No código 
						Razor a seguir, o parâmetro id corresponde à rota 
						padrão, assim id é adicionado aos dados da rota.
						
						
						<a asp-action="Edit" 
						asp-route-id="@item.ID">Editar</a>
						
						Quando o valor de ID for 6 esse código gera o seguinte 
						HTML:
						
						
						<a href="/Estudantes/Edit/6">Editar</a>
Agora temos que exibir as informações na respectiva View.
Para isso vamos abrir a view Details.cshtml na pasta Views/Estudantes e vamos incluir o código em azul conforme abaixo:
| 
								@model UniversidadeMacoratti.Models.Estudante @{ ViewData["Title"] = "Details"; } <h2>Detalhes</h2> <div>  | 
							
O código inserido cria uma tabela HTML e itera através das entidades na propriedade de navegação Matriculas onde para cada matricula ele exibe o nome do curso e a nota.
O nome do curso é retornado a partir da entidade Curso que esta armazenada na propreidade de navegação da entidade Matriculas.
Executando o projeto novamente e clicando no link detalhes para um estudante agora iremos obter o seguinte resultado:
						
						
Atualizando a página Create
Vamos agora ajustar o código do método Create do controlador EstudantesController conforme o código a seguir:
| 
								                [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Create([Bind("SobreNome,Nome,DataMatricula")] Estudante estudante) { try { if (ModelState.IsValid) { _context.Add(estudante); await _context.SaveChangesAsync(); return RedirectToAction("Index"); } } catch (DbUpdateException /* ex */) { //Logar o erro (descomente a variável ex e escreva um log ModelState.AddModelError("", "Não foi possível salvar. " + "Tente novamente, e se o problema persistir " + "chame o suporte."); } return View(estudante); }  | 
							
						Este código adiciona a 
						entidade Estudante criada pelo 
						model binder da ASP.NET MVC ao conjunto de 
						entidades Estudantes e salva as alterações no 
						banco de dados. 
						
						Nós removemos EstudanteID do atributo Bind 
						porque EstudanteID é o valor da chave primária 
						que o SQL Server definirá automaticamente quando a linha 
						for inserida. A entrada do usuário não define o valor 
						ID.
						
						Diferente do atributo Bind, o bloco try-catch 
						é a única alteração que fizemos no código gerado. Se uma 
						exceção que deriva de DbUpdateException é 
						detectada enquanto as alterações estão sendo salvas, uma 
						mensagem de erro genérica é exibida. As exceções do 
						DbUpdateException às vezes são causadas por algo 
						externo ao aplicativo, em vez de um erro de programação, 
						pelo que o usuário é aconselhado a tentar novamente. 
						Embora não implementado neste exemplo, um aplicativo de 
						qualidade de produção registraria a
						exceção usando um log.
						
						Nota: O 
						atributo Bind do código gerado inclui no método
						Create é uma maneira de se proteger contra o 
						overposting em cenários de criação de informações. Ele 
						limita que o model binder usa quando ele cria uma 
						instância de Estudante.
						
						O atributo ValidateAntiForgeryToken ajuda a 
						evitar ataques de falsificação de solicitação entre 
						sites (CSRF). O token é automaticamente injetado na 
						visualização por FormTagHelper e é incluído 
						quando o formulário é enviado pelo usuário. O token é 
						validado pelo atributo ValidateAntiForgeryToken.
Vamos testar a criação de um novo Estudante. Execute a aplicação novamente e clique no link Estudantes e a seguir em Criar Novo;
Informe os dados para o novo estudante e clique no botão Criar;
Vemos o resultado exibido nas páginas abaixo:
								
								![]()  | 
							
								
								![]()  | 
							
Ajustando a página para Editar dados: O método Edit (HttpGet e HttpPost)
Vamos agora alterar o código dos métodos Edit do controlador EstudantesController. Note que temos dois métodos Edit. Um que não usa nenhum atributo, e , neste caso é o método Edit HttpGet e o outro método que usa o atributo [HttPost].
O ´método HttpGet não precisa ser alterado. Vamos alterar o método Edit HttpPost conforme mostrado abaixo:
								       [HttpPost, ActionName("Edit")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> EditPost(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
            var atualizarEstudante = await _context.Estudantes.SingleOrDefaultAsync(s => s.EstudanteID == id);
            if (await TryUpdateModelAsync<Estudante>(
                atualizarEstudante,
                "",
                s => s.Nome, s => s.SobreNome, s => s.DataMatricula))
            {
                try
                {
                    await _context.SaveChangesAsync();
                    return RedirectToAction("Index");
                }
                catch (DbUpdateException /* ex */)
                {
                    //Logar o erro (descomente a variável ex e escreva um log
                    ModelState.AddModelError("", "Não foi possível salvar. " +
                        "Tente novamente, e se o problema persistir " +
                        "chame o suporte.");
                }
            }
            return View(atualizarEstudante);
        }
  | 
							
						As mudanças feitas no 
						código implementam uma prática recomendada de segurança 
						para evitar a sobreposição de post. O código gerado pelo 
						Scaffolding usava o atributo Bind e adicionava a 
						entidade criada pelo model binder ao conjunto de 
						entidades com um sinalizador Modified. Esse 
						código não é recomendado para muitos cenários porque o 
						atributo Bind limpa quaisquer dados 
						pré-existentes em campos não listados no parâmetro 
						Include.
						
						O novo código lê a entidade existente e chama 
						TryUpdateModel para atualizar os campos na entidade 
						recuperada com base na entrada do usuário nos dados do 
						formulário postados. 
						
						O controle de alterações automático do Entity Framework 
						define o sinalizador Modified nos campos que são 
						alterados pela entrada de formulário. Quando o método 
						SaveChanges é chamado, o Entity Framework cria 
						instruções SQL para atualizar o registro do banco de 
						dados. Os conflitos de simultaneidade são ignorados e 
						somente as colunas da tabela atualizadas pelo usuário 
						são atualizadas no banco de dados. 
						
						Como uma prática mais recomendada para evitar o 
						overposting, os campos que você deseja que sejam 
						atualizáveis pela página Editar estão na lista 
						vazia nos parâmetros TryUpdateModel. (A string 
						vazia que precede a lista de campos na lista de 
						parâmetros é para um prefixo a ser usado com os nomes 
						dos campos de formulário.) Atualmente, não há campos 
						extras que você está protegendo, mas listando os campos 
						que você deseja que o model binder vincule 
						garante que, se você adicionar campos ao modelo de dados 
						no futuro, eles serão protegidos automaticamente até que 
						você os adicione explicitamente aqui.
						
						Como resultado dessas alterações, a assinatura de método 
						do método HttpPost Edit é a mesma que o método 
						HttpGet Edit; por isso alteramos o nome do método 
						para EditPost.
						
						O contexto do banco 
						de dados controla se as entidades na memória estão 
						sincronizadas com suas linhas correspondentes no banco 
						de dados e essas informações determinam o que acontece 
						quando você chama o método SaveChanges. Por 
						exemplo, quando você passa uma nova entidade para o 
						método Add, o estado dessa entidade é definido 
						como Added e a seguir, quando você chama o método 
						SaveChanges, o contexto do banco de dados emite um 
						comando SQL INSERT.
						
						O estado da entidade é uma enumeração do tipo 
						System.Data.EntityState que declara os seguintes 
						valores:
						
						
						Added - A entidade é marcada como adicionada;
						Deleted - A entidade é marcada como deletada;
						Modified - A entidade foi modificada;
						Unchanged - A entidade não foi modificada;
						Detached - A entidade não esta sendo tratada no 
						contexto;  
						
						Em um aplicativo desktop, as alterações de estado são 
						normalmente definidas automaticamente. Você lê uma 
						entidade e faz alterações em alguns de seus valores de 
						propriedade. Isso faz com que seu estado seja alterado 
						automaticamente para Modified. Em seguida, quando 
						você chamar SaveChanges, o Entity Framework gera 
						uma instrução SQL UPDATE que atualiza somente as 
						propriedades reais que você alterou.
						
						Em um aplicação web, o DbContext que inicialmente 
						lê uma entidade e exibe seus dados a serem editados é 
						descartado após uma página ser processada. Quando o 
						método Action HttpPost Edit é chamado, uma nova 
						solicitação é feita e você tem uma nova instância do 
						DbContext. Se você reler a entidade nesse novo 
						contexto, você simula o processamento da área de 
						trabalho.
Agora vamos testar a edição de dados.
Execute a aplicação e clique no link Editar para um estudante e altere uma informação clicando a seguir no botão Salvar.
Abaixo vemos o resultado da alteração feita.
								
								![]()  | 
							
								
								![]()  | 
							
Ajustando o método Delete e sua View
Como vimos nas operações para atualizar e criar um estudante, a operação para excluir um estudante também exige dois métodos Action.
								Vamos adicionar 
								um bloco try-catch ao método HttpPost 
								Delete para lidar com quaisquer erros que 
								possam ocorrer quando o banco de dados for 
								atualizado. Se ocorrer um erro, o método 
								HttpPost Delete chama o método HttpGet 
								Delete, passando um parâmetro que indica que 
								ocorreu um erro. O método HttpGet Delete 
								então exibe novamente a página de confirmação 
								juntamente com a mensagem de erro, dando ao 
								usuário a oportunidade de cancelar ou tentar 
								novamente.
								
								Substitua o método Action HttpGet Delete 
								com o código a seguir, que gerencia o relatório 
								de erros.
										       public async Task<IActionResult> Delete(int? id, bool? saveChangesError = false)
        {
            if (id == null)
            {
                return NotFound();
            }
										            var estudante = await _context.Estudantes
                .AsNoTracking()
                .SingleOrDefaultAsync(m => m.EstudanteID == id);
            if (estudante == null)
            {
                return NotFound();
            }
										            if (saveChangesError.GetValueOrDefault())
            {
                ViewData["ErrorMessage"] =
                    "A exclusão falhou. Tente novamente e se o problema persistir " +
                    "contate o suporte.";
            }
										            return View(estudante);
        }
										 | 
									
Este código aceita um parâmetro opcional que indica se o método foi chamado após uma falha para salvar as alterações. Este parâmetro é false quando o método HttpGet Delete é chamado sem uma falha anterior. Quando ele é chamado pelo método HttpPost Delete em resposta a um erro de atualização de banco de dados, o parâmetro é true e uma mensagem de erro é passada para a exibição
Agora vamos ajustar o método HttPost Delete substitindo o método Action HttpPost Delete pelo código abaixo onde alteramos o nome do método para DeleteConfirmed.
								       [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            var estudante = await _context.Estudantes
                .AsNoTracking()
                .SingleOrDefaultAsync(m => m.EstudanteID == id);
								            if (estudante == null)
            {
                return RedirectToAction("Index");
            }
								            try
            {
                _context.Estudantes.Remove(estudante);
                await _context.SaveChangesAsync();
                return RedirectToAction("Index");
            }
            catch (DbUpdateException /* ex */)
            {
                //Logar o erro
                return RedirectToAction("Delete", new { id = id, saveChangesError = true });
            }
        }
								
  | 
							
Este código retorna uma entidade selecionada, e então chama o método Remove para definir o status da entidade como Deleted. Quando o método SaveChanges for invocado, o comando SQL DELETE e gerado para excluir o registro da tabela.
Agora para concluir vamos alterar a view Delete.cshtml na pasta /Views/Estudantes incluindo uma mensagem de erro na página conforme o código abaixo:
								@model UniversidadeMacoratti.Models.Estudante
								@{
    ViewData["Title"] = "Delete";
}
								<h2>Deletar</h2>
								<p class="text-danger">@ViewData["ErrorMessage"]</p>
								<h3>Tem certeza que deseja deletar este registro?</h3>
<div>
    <h4>Estudante</h4>
    <hr />
    <dl class="dl-horizontal">
        <input type="hidden" asp-for="EstudanteID" />
        <dt>
            @Html.DisplayNameFor(model => model.SobreNome)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.SobreNome)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.Nome)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Nome)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.DataMatricula)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.DataMatricula)
        </dd>
    </dl>
    <form asp-action="Delete">
        <div class="form-actions no-color">
            <input type="submit" value="Deletar" class="btn btn-default" /> |
            <a asp-action="Index">Retornar para Lista</a>
        </div>
    </form>
</div>
								 | 
							
Agora vamos testar a exclusão de dados executando novamente a aplicação, selecionado um estudante e clicando no botão Deletar:
						
						
A página Index irá exibir a relação de estudantes sem o estudante excluído.
E assim concluímos os ajustes nas operações CRUD para a nossa entidade Estudante.
						
						Fechando 
						conexões de banco de dados
						
						Para liberar os recursos que uma conexão de banco de 
						dados utiliza, a instância de contexto deve ser 
						descartada assim que você terminar de usá-la. A injeção 
						de dependência incorporada do ASP.NET Core cuida dessa 
						tarefa para você.
						
						No arquivo Startup.cs chamamos o método de 
						extensão AddDbContext para provisionar a classe
						DbContext no recipiente 
						ASP.NET DI. Esse método define a vida útil do 
						serviço como Scoped por padrão. Scoped significa 
						que o tempo de vida do objeto de contexto coincide com o 
						tempo de vida da requisição web e o método Dispose será 
						chamado automaticamente no final da solicitação web.
						
						Gerenciando Transações
						
						Por padrão, o Entity Framework implícitamente implementa 
						transações. Nos cenários em que você faz alterações em 
						várias linhas ou tabelas e, em seguida, chama 
						SaveChanges, o Entity Framework automaticamente garante 
						que todas as alterações sejam bem-sucedidas ou falhem. 
						Se algumas alterações são feitas primeiro e, em seguida, 
						ocorre um erro, essas alterações são automaticamente 
						revertidas. 
						
						
						Consultas de não acompanhamento (No-Tracking)
						
						
						Quando um contexto de banco de dados recupera linhas de 
						uma tabela e cria objetos de entidade que os 
						representam, por padrão ele mantém um registro se as 
						entidades na memória estão sincronizadas com o que está 
						no banco de dados. Os dados na memória atuam como um 
						cache e são usados quando você atualiza uma entidade. 
						Esse armazenamento em cache é muitas vezes desnecessário 
						em um aplicativo Web porque as instâncias de contexto 
						são normalmente de curta duração (uma nova é criada e 
						disposta para cada solicitação) e o contexto que lê 
						uma entidade normalmente é descartado antes que essa 
						entidade seja usada novamente.
						
						Você pode desabilitar o rastreamento de objetos de 
						entidade na memória chamando o método AsNoTracking. 
						Isso melhora o desempenho.
						A seguir algumas 
						situações onde podemos desabilitar o rastreamento :
						
						1 - Durante a vida do contexto, você não precisa 
						atualizar quaisquer entidades e não precisa que o EF 
						carregue automaticamente propriedades de navegação com 
						entidades recuperadas por consultas separadas. 
						Frequentemente, estas condições são satisfeitas nos 
						métodos Action HttpGet do 
						controlador.
						
						2 - Você está executando uma consulta que recupera um 
						grande volume de dados e somente uma pequena parte dos 
						dados retornados serão atualizados. Pode ser mais 
						eficiente desativar o rastreamento para a consulta e 
						executar uma consulta mais tarde para as poucas 
						entidades que precisam ser atualizadas.
						
						3 - Você deseja anexar uma entidade para atualizá-la, 
						mas antes você recuperou a mesma entidade para uma 
						finalidade diferente. Como a entidade já está sendo 
						rastreada pelo contexto do banco de dados, não é 
						possível anexar a entidade que você deseja alterar. Uma 
						maneira de lidar com essa situação é chamar AsNoTracking 
						na consulta anterior.
Na próxima parte do artigo vamos continuar realizando operações de filtragem, ordenação, paginação e agrupamento.
						
						
						Eu sou a videira, vós as varas; quem está em mim, e eu 
						nele, esse dá muito fruto; porque sem mim nada podeis 
						fazer. 
						
						
						João 15:5
| 
	
    
    Veja os 
    Destaques e novidades do SUPER DVD Visual Basic 
(sempre atualizado) : clique e confira ! 
 Quer migrar para o VB .NET ? 
 Quer aprender C# ?? 
 Quer aprender os conceitos da Programação Orientada a objetos ? Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?  | 
  
Referências:
Super DVD Vídeo Aulas - Vídeo Aula sobre VB .NET, ASP .NET e C#
Entity Framework - Conceitos Básicos - Uma visão geral - Macoratti
Entity Framework - Separando as classes das entidades do ... - Macoratti
Entity Framework 6 - Aplicação em camadas - Definindo o ... - Macoratti
C# - Cadastro de Clientes com Entity Framework em ... - Macoratti
NET - Entity Framework 5 - Operações CRUD (revisitado) - Macoratti