Este artigo é útil para desenvolvedores que precisam aprender o funcionamento e como implementar uma aplicação distribuída através do uso dos recursos da plataforma .NET.
A mesma fornece as tecnologias necessárias para o desenvolvimento de sistemas distribuídos com uso de web services baseado em REST, aplicações desktop, tecnologias para web sites e aplicações para mobilidade.
Este artigo será dividido em três partes. Será desenvolvida uma aplicação distribuída completa para força de vendas. Nesta primeira parte do artigo focaremos na estrutura de web services com base no REST.
O serviço irá expor uma interface comum para todas as entidades envolvidas seguindo as funcionalidades e operações CRUD para o SGDB SQL Server 2014. Será criado um cadastro de clientes, produtos e pedidos, sendo que a implementação será feita diretamente com a ASP.NET Web API.
Desenvolver sistemas distribuídos não é uma tarefa fácil, principalmente na hora de escolher qual tecnologia usar para realizar a integração entre aplicações diversas que estarão rodando em plataformas distintas.
Até pouco tempo atrás os Web Services baseados em SOAP eram a melhor forma para estabelecer comunicação entre aplicações, porém existe uma série de fatos que tornaram esta implementação complexa e com custo bastante elevado, devido às características que o SOAP foi ganhando com o passar dos tempos.
Boa parte dos fornecedores de tecnologias para desenvolvimento de aplicações não conseguiam acompanhar a evolução do SOAP, desta forma era preciso escolher com bastante calma e atenção quais soluções e tecnologias utilizar para integrar sistemas distribuídos utilizando como meio de comunicação padrão o SOAP.
O SOAP ainda está presente na maior parte das soluções para sistemas distribuídos. O fator que auxilia este número é a segurança, pois este protocolo proporciona um excelente nível de segurança para troca de informações entre aplicações.
Outro ponto a destacar sobre os sistemas distribuídos é o novo meio de troca de dados entre aplicações rodando em qualquer plataforma. O REST é o novo padrão arquitetural que proporciona a troca de dados entre qualquer aplicação que faça uso do protocolo HTTP, ou seja, o REST tem como base a estrutura do protocolo HTTP para enviar e receber dados entre o servidor e uma aplicação cliente.
Esta nova forma de implementação de Web Service vem se consolidando no mercado e existem vários fatores que levam o REST a ser hoje umas das melhores alternativas para solução de troca de dados entre sistemas distribuídos. Veja a seguir algumas características do REST.
-
O primeiro ponto a destacar sobre o sucesso do REST é o fato do mesmo ter como base para comunicação o protocolo HTTP, desta forma qualquer aplicação com acesso à internet pode estabelecer comunicação com um Web Service implementado seguindo o esse padrão arquitetural.
-
A implementação do REST é bem simplificada, ou seja, tanto no lado do servidor como também do lado da aplicação cliente. Isso se deve ao fato do REST usar alguns verbos do HTTP: GET, POST, PUT e DELETE. Podemos até fazer uma breve comparação com as operações de transações em um banco de dados, o famoso CRUD, acrônimo de CREATE, READ, UPDATE e DELETE.
-
Por fim, mas não parando por aí, o REST possibilita a troca de dados em vários formatos, os mais utilizados são o JSON e XML.
Tabela cliente: os campos da tabela cliente são auto descritivos, porém perceba que a coluna cpf_cnpj é do tipo varchar, pois se utilizássemos um int não caberia o dado e nem poderíamos armazenar valores iniciados em zero.
Com uso do varchar é possível até mesmo gravar no campo os números do CPF ou CNPJ com máscara, apesar de essa não ser uma boa prática, já que existem componentes que fazem o trabalho de adicionar máscaras em campos.
-
Tabela produto: na tabela produto também não existem segredos, porém temos um problema: o campo preço de compra não pode ser apresentado no dispositivo móvel com Windows Phone, pois o vendedor não pode saber este preço.
Assim sendo, temos duas soluções: ocultar este campo na implementação mobile, ou criar um DTO exclusivo para consumo do serviço pela aplicação mobile. Em nosso cenário o melhor e menos dispendioso seria fazer isso no lado mobile, que é o que faremos.
-
Tabela pedido cabeçalho: a ideia desta tabela é armazenar as informações sobre o pedido como um todo, ou seja, qual cliente, valor total da venda, quantidade de itens, data da venda e status (que pode ser Finalizada, Cancelada, dentre outros status possíveis).
-
Tabela itens pedido: esta tabela também é auto descritiva, porém é importante notar as chaves estrangeiras que representam os relacionamentos com as tabelas produto e pedido cabeçalho.
-
Linha 11: o primeiro ponto a destacar é o controller Values que herda de ApiController.
Em uma aplicação ASP.NET MVC o controller herda da classe Controller, então para que possamos implementar a Web API é necessário herdar da classe ApiController que já tem encapsulado todos os recursos necessários referentes à implementação de serviços com base no REST.
-
Linhas 14, 20, 26, 30 e 35: nestas linhas estão implementados os métodos que expõem ao cliente a interface de comunicação através da definição de rotas, ou seja, o endereço que aponta para algum recurso no servidor. Perceba que cada método faz referência a um verbo do protocolo HTTP, sendo: Get para solicitar uma representação de um recurso no servidor; Post para criar um no recurso; Put para alterar um recurso existente; e Delete para excluir um determinado recurso no servidor.
É muito importante você fazer uma associação com as operações de CRUD, pois o objetivo deste projeto com uso da Web API é fornecer uma interface comum para os outros dois projetos.
Os projetos que irão consumir o serviço serão criados nas próximas partes deste artigo.
- Empty EF Designer Model: utilizando esta forma de trabalho você irá criar um modelo visualmente através do EF Designer. A partir deste modelo visual será criado o banco de dados e suas classes models, sendo assim, a estrutura de ORM do Entity Framework permite facilmente a inclusão e manutenção de dados no banco de dados;
-
Empty Code First model: esta opção cria um modelo vazio Code First a partir da linha de código, ou seja, você irá codificar seu modelo manualmente e em seguida poderá gerar o banco de dados a partir deste código. Por este motivo que se chama Code First, primeiro o modelo e depois a geração do banco de dados.
-
Code First from database: selecionando esta opção será criado um modelo Code First com base em um banco de dados já existente, bastando apenas você selecionar as tabelas necessárias para o modelo da aplicação.
-
EF Designer from database: aqui será criado o modelo no EF Designer (EDMX) baseado em banco de dados já existente, dessa forma você precisa apenas informar a conexão com banco de dados, selecionar as tabelas e as views se necessário, em seguida será criado um arquivo EDMX.
O EF Designer é um diagrama que representa as tabelas do banco de dados transformadas em classes para seu modelo.
Esta abordagem é interessante para nosso contexto de sistema distribuído, pois não temos uma dependência do projeto Web API para com o banco de dados. Este cenário é o contrário da abordagem Code First, porém em outros cenários o code first é mais apropriado, tudo depende do contexto e domínio de negócio onde o projeto é baseado.
Então será esta abordagem que iremos usar no projeto com a Web API, clique no botão Next para avançar para próxima tela.
-
Linha 18: aqui é declarado e inicializado um objeto privado que representa o DbContext para acesso ao banco de dados através do Entity Framework.
Neste caso foi inicializado o objeto a partir da classe BD_FORCA_VENDAEntities. Veja na Listagem 5 a implementação da classe BD_FORCA_VENDAEntities que herda de DbContext. Em seguida são definidas quatro propriedades através do DbSet para representar as tabelas do banco de dados.
-
Linhas 20 a 24: neste bloco de código temos a implementação da primeira action do controller. O importante é entender que este método será solicitado via Get através da rota http://localhost:1622/api/Clientes, sendo que você deve alterar a porta de acordo com o IIS de sua máquina. Após realizada a requisição será retornado um IQueryable de cliente com lista de clientes no formato JSON.
Anteriormente foi definido no arquivo WebApiConfig.cs o formato JSON como padrão para retorno de dados ao cliente. Veja na Figura 10 o resultado da listagem de clientes no formato JSON após acessar a rota “http://localhost:1622/api/Clientes” pelo browser.
-
Linhas 26 a 37: neste bloco de código temos a implementação do método Getcliente(int id) para obter um cliente especifico através de uma requisição via GET, passando como parâmetro o id do cliente.
Note que na linha 27 o método foi anotado com o ResponseType informando que o tipo de resposta é um objeto tipo cliente, sendo que na linha 28 foi definido o retorno como sendo do tipo IHttpActionResult de forma assíncrona.
Na linha 30 é realizada uma busca pelo cliente chamando o método FindAsync, que é um método assíncrono do Entity Framework. Na sequência é passando como parâmetro o id do cliente a ser pesquisado.
Por fim, nas linhas 33 e 36 é verificado se foi encontrado um cliente na base de dados, caso o objeto cliente seja null, então retorna NotFound, caso contrário retorna o cliente através da instrução return Ok(cliente).
A interface IHttpActionResult se responsabiliza pela mensagem de retorno do protocolo HTTP a ser retornada ao cliente solicitante.
-
Linha 41: é declarado o método “Putcliente(int id, cliente cliente)” que recebe como parâmetro o id do cliente a ser alterado e também um model cliente constando os dados a serem atualizados em um cliente especifico na base de dados, identificado pelo id informado.
-
Linha 43: esta linha é muito importante na implementação, pois temos uma condição if que verifica se o modelo é valido, ou seja, se realmente o ASP.NET MVC conseguiu recuperar os dados da mensagem que vindas no corpo da requisição do protocolo HTTP 1.1. O ASP.NET transforma os dados em um objeto do tipo cliente, então caso tenha algum problema de conversão de dados, como uma string para um int, será disparado um BadRequest com ModelState inválido, que retorna um erro, tratado na linha 44.
-
Linha 48: nesta linha é verificado se o id recebido como parâmetro é diferente do id do objeto cliente. Caso seja diferente, ele vai disparar uma mensagem de erro HTTP 400 – BadRequest para informar ao solicitante da requisição que ocorreu algum erro nos dados informados no corpo da mensagem enviado ao serviço da Web API.
-
Linha 53: nesta linha é chamado o método Entry através context, passando como parâmetro o objeto a ser modificado. Perceba que foi atribuída à propriedade State o EntityState.Modified para informar o estado do objeto a ser modificado.
-
Linha 57: aqui é chamado o método SaveChangesAsync para salvar as alterações do cliente no banco de dados.
-
Linha 70: é retornado o código de status informando o sucesso da operação através do HttpStatusCode.NoContent.
-
Linha 72: nesta linha temos a definição do método Postcliente, que recebe como parâmetro um objeto do tipo cliente para cadastrar no banco. É importante mencionar que o ASP.NET MVC se responsabiliza por transformar os dados do cliente que chega ao servidor junto ao corpo da mensagem via requisição HTTP. Estes dados são transformados em um objeto válido do tipo cliente para que depois possa ser manipulado no código.
-
Linha 78: verifica se o modelo é válido, como na action Putcliente.
-
Linha 83: adiciona o cliente à base de dados através do método Add presente no model cliente do DbContext.
-
Linha 84: Salva o cliente no banco, ou seja, realiza um commit para efetuação a inclusão de dados permanente na base.
-
Linha 86: cria uma rota através do CreatedAtRoute para retornar ao cliente da requisição, desta forma o mesmo pode realizar uma nova consulta ao novo recurso disponível no servidor.
-
Linha 91: definição do método que recebe como parâmetro o id do cliente a ser deletado da base de dados.
-
Linha 93: realiza a busca do cliente a ser excluído através do método FindAsync, passando o id do cliente a ser retornado para a exclusão.
-
Linhas 94 e 96: verifica se foi encontrado algum cliente a ser excluído da base, caso contrário retorna uma mensagem de erro NotFound.
-
Linha 99 e 100: deleta o cliente da base de dados através da chamada do método Remove, em seguida efetua um commit da exclusão com o método SaveChangesAsync.
-
Linha 102: nesta linha é retornada uma mensagem informando o sucesso da exclusão do cliente na base de dados.
-
Linhas 01 e 02: Neste bloco de código é informado o arquivo “angular.min.js”, que é núcleo do AngularJS e será usado no front-end para acessamos os recursos do serviço Web API.
-
Linhas 04 a 07: Aqui são informados alguns controller pertencentes ao AngularJS. Será nestes arquivos que iremos codificar o acesso ao serviço da Web API e também manipular os elementos do HTML a serem exibidos ao usuário.
Perceba que os arquivos clienteController.js, produtoController.js e pedidoCabecalhoController.js ainda não existem no projeto, pois iremos criá-los à medida que avançamos no artigo.
-
Linha 2: Nesta linha temos a abertura da tag html, na qual foi definido o atributo “data-ng-app” do AngularJS, que é o ponto inicial para inicializar uma aplicação com AngularJS.
Perceba que o data-ng-app recebeu como valor o “app”, ou seja, é a forma de dar nome à aplicação. Entenda como sendo uma forma de instância do AngularJS para que possamos ter vários escopos de acordo com a necessidade.
-
Linha 5: Esta linha tem uma configuração muito importante para uso pelo Bootstrap. Nela definimos a meta tag viewport para que se possa capturar informações sobre o dispositivo que está renderizando a página, como o tamanho da tela e escala.
-
Linhas 7 e 8: Aqui são utilizados o @Styles e @Scripts para renderizar os arquivos CSS e JavaScript que definimos dentro do arquivo BundleConfig.cs anteriormente.
-
Linha 11 a 29: Este bloco de código refere-se à estrutura de menu de navegação da aplicação. Aqui foram utilizadas várias classes CSS do Boostrap para construção do menu.
Perceba ainda que no intervalo das linhas 23 a 25 foram definidos alguns ActionLinks que apontam para os controllers da aplicação, controller estes que serão criados posteriormente.
-
Linha 32: Nesta linha consta o ponto chave de um arquivo de layout, o comando @RenderBody(), que é responsável por carregar o conteúdo das páginas que herdam de _Layout.cshtml.
-
Linhas 39 a 42: Aqui é utilizado novamente o @Scripts para renderizar os arquivos JavaScript da aplicação. Perceba que foi informado o path virtual referente aos bundles.
Veja ainda que são carregados a jQuery, o Bootstrap, o AngularJS e alguns Controllers que iremos utilizar na aplicação referentes ao AngularJS.
-
Linha 2: Nesta linha está o ponto de partida para se trabalhar com o AngularJS. Aqui é criado o escopo da aplicação, através de um objeto denominado app, que é instanciado através da chamada da função module do Angular.
Perceba que foi passado como parâmetro o nome do objeto “app”, que é o nome que iremos usar para adicionar novos objetos à aplicação instanciada, que neste caso iremos inserir no contexto os controllers.
Uma boa prática em aplicações complexas seria criar um ou mais arquivo module para inicializar todos os módulos da aplicação.
-
Linha 4: Aqui é utilizada a função chamada controller do Angular para criar o controller clienteController'. Também são passados como parâmetro os objetos $scope, $http e o nome do novo controller. O $scope é utilizado para criar variáveis e objetos a serem acessadas pela view da aplicação. Quanto ao $http, será utilizado para podemos acessar o serviço da Web API.
-
Linhas 10 a 12: Nestas linhas são definidas algumas variáveis booleanas que são utilizadas para exibir alguns conjuntos de elementos HTML da view, neste caso os formulários para cadastro e alteração de clientes e também a tabela com a listagem de clientes cadastrados na base de dados. Perceba que para criar estas variáveis não foi utilizado o “var”, ou seja, elas foram adicionadas diretamente ao escopo da aplicação app do Angular através do uso do $scope.
-
Linhas 15 a 17: Neste bloco de código é criada a função funcformNovoCliente, que altera os valores das variáveis booleanas para que seja exibido o form de cadastro de clientes ao usuário e depois seja ocultada a tabela de listagem dos clientes cadastros.
-
Linhas 21 a 23: Aqui é criada outra função chamada de funcCancelarCadastro, que realiza o papel inverso à função funcformNovoCliente criada anteriormente.
-
Linhas 27 a 39: Este bloco de código corresponde à função cadastrarCliente, que tem como objetivo receber os dados do novo cliente a ser cadastrado.
Para isso é criada uma variável chamada novocliente, que é vinculada pelo Angular aos inputs da view que recebe os dados do cliente informados pelo usuário.
Perceba que na linha 30 é realizada uma requisição via POST ao recurso “api/Clientes/Postcliente”. Caso ocorra algum erro, o mesmo será apresentado na view através da variável chamada error, presente na linha 37.
-
Linhas 42 a 47: Aqui é utilizado o $http para realizar uma requisição GET ao recurso “api/Clientes/Getcliente” para obter a listagem de clientes e popular a tabela na view.
-
Linhas 50 a 55: Aqui é criada a função funcformEditarCliente para editar cadastro do cliente. Perceba que é definido na linha 51 o cliente que é selecionado para edição na tabela. As demais linhas exibem e ocultam alguns conjuntos de elementos HTML na página.
-
Linha 58 a 62: Aqui temos a função funcCancelarEdicao, que é chamada caso o usuário clique no botão Cancelar na edição do cadastro do cliente.
-
Linhas 65 a 74: Neste bloco de código temos a função editarCliente, que tem o papel de acesso ao recurso “/api/Clientes/Putcliente” através de uma requisição PUT, passando o objeto com os dados do cliente a ser alterado no banco de dados.
-
Linhas 78 a 91: Para finalizar o controller cliente, foi criada uma função deletarCliente que recebe o id do cliente ser excluído da base de dados. Perceba que é realizada uma requisição com o verbo DELETE do HTTP ao recurso “api/Clientes/Deletecliente/”.
-
Linha 2: Nesta linha é criada uma div e definido o atributo data-ng-controller do AngularJS recebendo o valor “clienteController", que é justamente o nome do controller criado anteriormente na pasta “AngularJSApp”.
O uso do data-ng-controller passando o nome do controller informa ao AngularJS que tudo que estiver dentro desta div faz parte do escopo deste controller. Assim sendo, teremos acesso na view às variáveis, objetos e funções criadas no clienteController. É desta forma que iremos passar os dados do controller para a view e vice-versa.
-
Linha 9: Perceba que nesta linha foi feita uma referência à variável error do controller cliente, inserindo {{ error }} juntamente ao código HTML. Esta é forma com que se acessa e apresenta ao usuário o valor contido em uma variável ou objeto presente em um controller do AngularJS. Desta forma fica simples mostrar na página um erro, caso ocorra.
-
Linha 10: Aqui é criado um form referente ao cadastro de um novo cliente, que recebe uma propriedade data-ng-show do Angular cujo objetivo é exibir ou ocultar o formulário de acordo com os valores booleanos recebidos. Por este motivo que esse atributo é vinculado à variável exibeformNovoCliente.
-
Linhas 11 a 24: Neste bloco de código temos várias labels e inputs para entrada de dados com as informações do cliente a ser cadastrado pelo usuário.
Perceba que está sendo inserido nos inputs o atributo data-ng-model do Angular. Desta forma vinculamos um objeto e suas propriedades entre o controller e a view, assim, tudo que o usuário digitar nestes inputs automaticamente é refletido no objeto “novocliente” em cada uma de suas propriedades.
Observe que uma parte do código foi omitida para simplificar a listagem, porém o código fonte deste artigo encontra-se disponível para download no site da revista.
-
Linhas 27 e 28: Aqui são criados dois botões com referências às funções cadastrarCliente() e funcCancelarCadastro() do clienteController. Perceba que para realizar esta vinculação foi utilizado o atributo data-ng-click do AngularJS.
-
Linha 40: Aqui é criado outro botão e vinculado à função funcformNovoCliente() através do atributo data-ng-click do Angular, desta forma podemos facilmente exibir o form para cadastro de um cliente.
-
Linhas 41 a 57: Neste bloco de código é criada uma tabela com algumas classes do Bootstrap para podemos exibir a listagem clientes.
Na linha 49 temos o código principal para fazer loop na listagem de clientes: o atributo data-ng-repeat do Angular. Para esse atributo passamos a expressão "cliente in clientes” para buscar os dados de todos clientes. Nas linhas 50 a 55 acessamos e exibimos as propriedades de cada cliente.
-
Linhas 64 a 89: Neste intervalo é criado outro from, porém agora para realizar a alteração do cadastro de cliente. Perceba que nos inputs são utilizados atributos data-ng-model do Angular para fazer referência ao "clienteAlterar” e assim poder passar estes novos dados através da chamada da função editarCliente(), presente no botão da linha 84.
Após esta breve introdução sobre sistemas distribuídos agora podemos entender e conhecer um pouco sobre a plataforma .NET e o que a mesma oferece como solução para esta estrutura. A Microsoft vem atualizando a plataforma .NET de acordo como os novos padrões que vão surgindo.
Se tratando de web service a plataforma .NET até certo tempo atrás tinha como principal uma tecnologia denominada WCF (Windows Comunication Foundation), que incorporava várias formas de comunicação para sistemas distribuídos e também o uso de web services ASMX.
Estas tecnologias utilizam como base para comunicação o protocolo SOAP, então com o surgimento do REST a Microsoft inovou e criou uma nova tecnologia que implementa esse padrão, a ASP.NET Web API.
Porém esta tecnologia não para por aí, ou seja, a mesma tem como base o ASP.NET MVC, então toda a implementação de web services com base na Web API segue o padrão MVC.
A Web API é parte do framework ASP.NET MVC, desta forma você pode utilizar toda a estrutura de um website ASP.NET MVC na Web API e vice-versa. A Web API pode ser hospedada em um servidor IIS normalmente como um site com ASP.NET MVC, pois sua implementação tem como base o REST e protocolo HTTP.
Criando o DER do projeto para o banco de dados
Como iremos criar uma aplicação distribuída, é interessante ter o máximo de documentação possível sobre o software a ser desenvolvido.
A documentação serve para consulta pela equipe de desenvolvimento, sendo que uma software house com processos bem definidos pode ter equipes especializadas em determinada tecnologia e plataforma.
Pensando neste cenário, iremos criar um DER (Diagrama Entidade Relacionamento) que representa a estrutura de tabelas do banco de dados de nossa aplicação referente às entidades do mundo real que fazem parte do levantamento de requisitos funcionais do software a ser desenvolvido.
Para evitar o acoplamento do DER a um determinado SGDB, iremos utilizar uma ferramenta free denominada DBDesigner que gera script SQL para vários SGDBs, como o SQL Server, MySQL, Oracle, PostgreSQL, Firebird, SQLite, dentre outros. Você pode baixar o DBDesigner no link presente na seção Links deste artigo.
O banco de dados da aplicação terá quatro tabelas: cliente, produto, cabeçalho do pedido e itens do pedido. O DER do banco de dados com todas as tabelas necessárias para o sistema força de vendas pode ser visualizado na Figura 1, a mesma apresenta as tabelas juntamente aos relacionamentos necessários entre elas.
Analisando esse DER, o mesmo merece algumas observações a serem destacadas, conforme a seguir.
Com DER do banco de dados pronto, agora é hora de criar o banco propriamente dito. Iremos utilizar o SGDB SQL Server 2014 Express e Management Studio 2014 para obter uma interface para administração do SQL Server.
Abra o Management Studio 2014 e crie um novo banco de dados chamado de BD_FORCA_VENDA, logo após é necessário obter o script SQL para criar as tabelas. Abra o DBDesigner e vá até menu File> Export> SQL Create Script. Na sequência irá abrir uma nova janela conforme mostra a Figura 2, nela selecione SQL Server em Target Data Base e depois clique no botão Copy Script to Clipboard para copiar para memória o script SQL para gerar as tabelas.
Realizado este procedimento, volte ao Management Studio, abra uma nova query para o banco BD_FORCA_VENDA recém-criado e cole o script SQL (presente na Listagem 1).
Execute o script e terá uma estrutura conforme apresentado na Figura 3, com todas as tabelas definidas no DER do banco de dados.
CREATE TABLE produto (
idproduto INTEGER NOT NULL IDENTITY ,
descricao VARCHAR(100) ,
preco_compra DECIMAL(6,2) ,
preco_venda DECIMAL(6,2) ,
qtde_estoque INTEGER ,
data_cadastro DATE ,
status_produto VARCHAR(20) ,
PRIMARY KEY(idproduto));
GO
CREATE TABLE cliente (
idcliente INTEGER NOT NULL IDENTITY ,
nome VARCHAR(100) ,
cpf_cnpj VARCHAR(18) ,
endereco VARCHAR(200) ,
bairro VARCHAR(40) ,
cidade VARCHAR(40) ,
cep VARCHAR(10) ,
email VARCHAR(50) ,
telefone VARCHAR(15) ,
status_cliente VARCHAR(20) ,
data_cadastro DATE ,
PRIMARY KEY(idcliente));
GO
CREATE TABLE pedido_cabecalho (
idpedido_cabecalho INTEGER NOT NULL IDENTITY ,
idcliente INTEGER NOT NULL ,
valor_total_pedido DECIMAL(6,2) ,
qtde_itens INTEGER ,
status_pedido VARCHAR(20) ,
data_pedido DATE ,
PRIMARY KEY(idpedido_cabecalho) ,
FOREIGN KEY(idcliente)
REFERENCES cliente(idcliente));
GO
CREATE INDEX pedido_cabecalho_FKIndex1 ON pedido_cabecalho (idcliente);
GO
CREATE INDEX IFK_Rel_Cliente__Ped_Cab ON pedido_cabecalho (idcliente);
GO
CREATE TABLE pedido_itens (
idpedido_itens INTEGER NOT NULL IDENTITY ,
idpedido_cabecalho INTEGER NOT NULL ,
idproduto INTEGER NOT NULL ,
quantidade INTEGER ,
valor_unitario DECIMAL(6,2) ,
sub_total DECIMAL(6,2) ,
status_item VARCHAR(20) ,
PRIMARY KEY(idpedido_itens) ,
FOREIGN KEY(idpedido_cabecalho)
REFERENCES pedido_cabecalho(idpedido_cabecalho),
FOREIGN KEY(idproduto)
REFERENCES produto(idproduto));
GO
CREATE INDEX pedido_itens_FKIndex1 ON pedido_itens (idpedido_cabecalho);
GO
CREATE INDEX pedido_itens_FKIndex2 ON pedido_itens (idproduto);
GO
CREATE INDEX IFK_Rel_PedCab__Ped_Itens ON pedido_itens (idpedido_cabecalho);
GO
CREATE INDEX IFK_Rel_Produto__Ped_Itens ON pedido_itens (idproduto);
GO
Com isso, o banco de dados da aplicação já está pronto para receber os dados.
Criando o projeto ASP.NET Web API com Visual Studio
O primeiro projeto a ser desenvolvido será a Web API que funcionará como centro do sistema, pois tanto a aplicação ASP.NET MVC quanto a do Windows Phone irão consumir dados a partir deste serviço.
Abra o Visual Studio 2013 e a partir do menu File> New> Project escolha, dentro da categoria Web, o template ASP.NET Web Application. Selecione a versão do .NET Framework (aqui será usada a 4.5.1) e nomeie o projeto como ForcaVendasWebAPI.
Em seguida clique em OK. Depois disso será aberta outra janela onde você deverá selecionar o template Web API e pressionar OK novamente para que o Visual Studio possa criar toda a estrutura de arquivos e pastas do projeto para Web API utilizando o padrão MVC.
Após o projeto ser criado pelo Visual Studio, teremos a estrutura do projeto “ForcaVendaWebAPI” conforme apresentado na Figura 4. Aparentemente parece um projeto ASP.NET MVC comum, mas não se engane, pois este projeto já vem com um serviço Web API configurado e pronto para ser executado.
Você pode ver esta implementação no arquivo ValuesController.cs, que se encontra dentro da pasta Controllers. Veja a Listagem 2 que mostra a implementação de um serviço Web API seguindo o padrão REST.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace ForcaVendaWebAPI.Controllers
{
[Authorize]
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
// POST api/values
public void Post([FromBody]string value)
{
}
// PUT api/values/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
}
}
Esta listagem merece alguns comentários para que você possa entender como trabalhar com a Web API seguindo o padrão arquitetural REST, que por sua vez tem como base de comunicação o protocolo HTTP. Veja a seguir mais detalhes sobre a codificação.
Integrando o Entity Framework ao projeto
Para realizar a conexão entre o serviço com a Web API e banco de dados SQL Server iremos utilizar o ORM Entity Framework que é nativo da plataforma .NET. O mesmo é responsável por realizar o mapeamento objeto-relacional entre as tabelas do banco de dados e as classes model da aplicação.
Também é possível optar para que o Entity crie todas as classes para o modelo de dados. Seguindo este contexto o BDA pode realizar atualizações na estrutura do schema do banco de dados e a partir da aplicação Web API podemos facilmente atualizar o modelo na aplicação através do EDMX do Entity framework.
Para adicionar o Entity Framework ao projeto você deve selecionar a pasta Models no Solution Explorer. A mesma faz parte da estrutura do padrão MVC. Com a pastas Models selecionada, clique nela com botão direito e depois na opção Add> New item. Abrirá uma nova janela onde deverá ser selecionada a categoria Data e o template ADO.NET Entity Data Model (Entity Framework). Informe o nome “ModelForcaDeVenda” e por fim clique em Add.
Após clicar em Add será apresentada uma nova janela, conforme a Figura 5. Esta janela é muito importante, pois é aqui onde você deve selecionar a forma que vai trabalhar com Entity Framework, ou seja, você irá escolher como vai gerar seu modelo de dados. Também deve definir como será a criação do banco de dados, se vai gerar o banco a partir de seu modelo ou se vai gerar o modelo a partir do banco de dados. Assim sendo, é possível selecionar as seguintes opções:
Na próxima tela você deve criar uma nova conexão com banco de dados clicando no botão New Connection. Realizado este procedimento, marque a opção “Yes, include the sensitive data in the connection string”, logo após clique em Next.
A Figura 6 mostra a nova janela onde devem ser selecionadas as tabelas necessárias do banco de dados para o serviço, em seguida clique em Finish. Perceba que nesta tela também é possível selecionar Views e Stored Procedures caso existam no banco de dados e sejam necessárias na aplicação.
Após finalizada a inclusão do Entity Framework ao projeto, será aberto o arquivo EDMX contendo todas as classes models que representam as tabelas do banco de dados, conforme a Figura 7, ou seja, o mapeamento objeto relacional propriamente dito. Perceba que na Solution Explorer, dentro da pasta Models, é criado várias classes com extensão .cs para cada entidade do mapeamento objeto relacional realizado com a base de dados.
Criando o Controller Cliente para realizar operações CRUD com EF
A partir deste ponto focaremos na criação das funcionalidades do serviço da Web API para expor os métodos para acesso às funcionalidades de CRUD para as entidades cliente, produto, cabeçalho do pedido e itens do pedido.
O acesso será realizado via requisições HTTP com uso de seus verbos, sendo que cada recurso terá uma rota especificada em forma de URI que identifica o caminho para as requisições de cada operação como a inclusão, leitura, alteração e exclusão de recursos no servidor.
Como a Web API roda em cima do ASP.NET MVC, precisaremos criar um controller para cada model que irá expor alguns métodos. Começaremos então criando o controller para clientes.
Para adicionar um novo controller para cliente, clique com o botão direito na pasta Controllers localizada no Solution Explorer, em seguida clique na opção Add> Controller. Com misso será aberta uma nova janela conforme mostra a Figura 8.
Selecione a opção “Web API 2 Controller with actions, using Entity Framework“, assim o Visual Studio irá auxiliar na criação do controller Cliente com base no model do Entity Framework. Desta forma serão gerados os métodos que precisaremos para as operações de CRUD no banco de dados. Este procedimento é conhecido como Scaffold.
Essa figura apresenta várias possibilidades de uso do scaffold, que nada mais é que um assistente para gerar um controller automaticamente com base na Web API ou ASP.NET MVC, desta forma ele nos poupa de um grande trabalho manual de codificação. Nosso contexto utiliza o Entity Framework, assim sendo o scaffold gera automaticamente todos os métodos para manutenção de dados.
Após selecionado o scaffold correto para o contexto do projeto e clicado em Add, será aberta uma nova janela conforme apresentado na Figura 9. Nesta janela precisamos informar o Model, o Data context e o nome do novo controller para nosso serviço. Siga os passos descritos a seguir:
1. No campo Model class você deve selecionar a classe cliente que representa o model para o Entity Framework.
2. O próximo passo é selecionar a classe que representa o Data Context do nosso modelo criado anteriormente. Quando foi definido o nome da connection string para acesso a dados com Entity Framework, este nome também foi usado no Data Context, sendo assim você deve selecionar a opção “BD_FORCA_VENDAEntities” que está localizado dentro da pasta Models.
3. Marque a opção “Use async controller actions”, pois na próxima parte faremos o acesso aos serviços através do AngularJS e essa opção fará com que a aplicação tenha um melhor rendimento através do uso de actions assíncronas.
4. O último passo é apenas informar o nome do controller, que neste caso será ClientesController. Perceba que devemos manter o sufixo Controller para que o ASP.NET possa identificar os controladores. Clique em Add para que o Visual Studio possa gerar o controller cliente e as actions automaticamente.
Ao clicar em Add o Visual Studio cria um arquivo chamado “ClientesController.cs” dentro na pasta Controllers, este arquivo por sua vez já contém toda a implementação necessária para expor as funcionalidades de CRUD aos clientes consumidores do serviço da Web API.
Antes de detalhar a implementação do código e testar o serviço da Web API com o novo controller Clientes, devemos realizar uma pequena configuração no arquivo WebApiConfig.cs, que está localizado dentro da pasta App_Start. Caso não façamos esta configuração teremos um erro ao tentar consumir o serviço devido a estarmos manipulando atributos do tipo Date dentro do model cliente.
Por padrão, a Web API retorna os dados no formato XML, assim sendo é necessário tratar o retorno de dados do tipo Date. Vamos alterar o arquivo WebApiConfig.cs, que é o arquivo de configuração padrão da Web API.
Devemos definir o formato JSON como padrão ao invés do XML. Veja na Listagem 3 toda a configuração necessária para retornar dados no formato JSON como padrão, o que deve ser feito no método Register.
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Na Listagem 4 temos a implementação gerada pelo Visual Studio para o Controller Clientes.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Description;
using ForcaVendaWebAPI.Models;
namespace ForcaVendaWebAPI.Controllers
{
public class ClientesController : ApiController
{
private BD_FORCA_VENDAEntities db = new BD_FORCA_VENDAEntities();
// GET: api/Clientes
public IQueryable<cliente> Getcliente()
{
return db.cliente;
}
// GET: api/Clientes/5
[ResponseType(typeof(cliente))]
public async Task<IHttpActionResult> Getcliente(int id)
{
cliente cliente = await db.cliente.FindAsync(id);
if (cliente == null)
{
return NotFound();
}
return Ok(cliente);
}
// PUT: api/Clientes/5
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> Putcliente(int id, cliente cliente)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != cliente.idcliente)
{
return BadRequest();
}
db.Entry(cliente).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!clienteExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
// POST: api/Clientes
[ResponseType(typeof(cliente))]
public async Task<IHttpActionResult> Postcliente(cliente cliente)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.cliente.Add(cliente);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = cliente.idcliente }, cliente);
}
// DELETE: api/Clientes/5
[ResponseType(typeof(cliente))]
public async Task<IHttpActionResult> Deletecliente(int id)
{
cliente cliente = await db.cliente.FindAsync(id);
if (cliente == null)
{
return NotFound();
}
db.cliente.Remove(cliente);
await db.SaveChangesAsync();
return Ok(cliente);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool clienteExists(int id)
{
return db.cliente.Count(e => e.idcliente == id) > 0;
}
}
}
O código implementado nessa listagem contém alguns detalhes que merecem ser comentados para melhor entendimento sobre seu funcionamento e como será o processo de requisição e resposta entre o cliente e o servidor.
Detalhes da action Getcliente que retorna uma listagem de clientes da base de dados:
Detalhes da action Getcliente que retorna um cliente especifico:
Detalhes da action Putcliente usada para atualizar um cliente no banco:
Detalhes da action Postcliente, que possibilita cadastro de cliente através de uma requisição via verbo POST.
Detalhes da action Deletecliente para excluir um cliente especifico do banco de dados.
namespace ForcaVendaWebAPI.Models
{
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
public partial class BD_FORCA_VENDAEntities : DbContext
{
public BD_FORCA_VENDAEntities()
: base("name=BD_FORCA_VENDAEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<cliente> cliente { get; set; }
public virtual DbSet<pedido_cabecalho> pedido_cabecalho { get; set; }
public virtual DbSet<pedido_itens> pedido_itens { get; set; }
public virtual DbSet<produto> produto { get; set; }
}
}
Criando o Controller Produtos
O próximo passo na construção do serviço é criar o controller para o cadastro de produtos e possibilitar a realização de todas as operações de CRUD conforme foi feito com o cadastro de clientes.
O procedimento para criar o controller de produtos é praticamente igual ao realizado anteriormente com o controller Clientes, porém no campo Model class devemos selecionar o model produto e nomear o controller como Produtos.
A Listagem 6 a seguir apresenta a implementação do ProdutosController, perceba que é bem similar a codificação do ClientesController, ou seja, o que foi explicado anteriormente no controlador cliente também é valido para o controlador de produtos.
namespace ForcaVendaWebAPI.Controllers
{
public class ProdutosController : ApiController
{
private BD_FORCA_VENDAEntities db = new BD_FORCA_VENDAEntities();
// GET: api/Produtos
public IQueryable<produto> Getproduto()
{
return db.produto;
}
// GET: api/Produtos/5
[ResponseType(typeof(produto))]
public async Task<IHttpActionResult> Getproduto(int id)
{
produto produto = await db.produto.FindAsync(id);
if (produto == null)
{
return NotFound();
}
return Ok(produto);
}
// PUT: api/Produtos/5
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> Putproduto(int id, produto produto)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != produto.idproduto)
{
return BadRequest();
}
db.Entry(produto).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!produtoExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
// POST: api/Produtos
[ResponseType(typeof(produto))]
public async Task<IHttpActionResult> Postproduto(produto produto)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.produto.Add(produto);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = produto.idproduto }, produto);
}
// DELETE: api/Produtos/5
[ResponseType(typeof(produto))]
public async Task<IHttpActionResult> Deleteproduto(int id)
{
produto produto = await db.produto.FindAsync(id);
if (produto == null)
{
return NotFound();
}
db.produto.Remove(produto);
await db.SaveChangesAsync();
return Ok(produto);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool produtoExists(int id)
{
return db.produto.Count(e => e.idproduto == id) > 0;
}
}
}
No código dessa listagem não consta nenhuma mudança significativa na implementação para ser comentada. Execute o projeto e especifique a rota para realizar uma requisição no browser via GET para obter a listagem de produtos, conforme ilustra a Figura 11.
Alterando o roteamento da Web API
Sempre quando for criado um novo serviço a ser consumido por vários clientes distintos é importante descrever o objetivo do mesmo na própria URL de acesso ao recurso. Desta forma o cliente terá uma ideia do se trata o recurso e o que pode obter a partir do método (action) definido através do conjunto (URL + Roteamento).
Quando é criado um novo projeto ASP.NET MVC, o Visual Studio por padrão não implementa o nome da action no roteamento, ou seja, não é necessário informar o nome da action (método) do controller na URL de acesso ao recurso no servidor.
Faremos uma alteração no roteamento do projeto para possibilitar informar o nome da action na URL para ter uma melhor descrição e acesso ao recurso solicitado no servidor.
Para alterar o roteamento da Web API é necessário abrir o arquivo WebApiConfig.cs que está localizado dentro da pasta App_Start. Veja o trecho de código presente na Figura 12, que mostra a rota padrão para se ter acesso aos recursos disponíveis no servidor.
Altere o trecho de código mostrado nessa figura, presente no arquivo WebApiConfig.cs, para o trecho de código presente na Listagem 7. Desta forma agora também pode ser informada a action do controller na URL de acesso aos recursos do servidor.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{Action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Para finalizar e conferir se a alteração no roteamento foi realizada corretamente, veja a comparação da URL de acesso à listagem de produtos. Perceba que na parte superior da Figura 13 não foi informada a action. Logo na parte inferior agora temos a presença do nome da action do controller na URL.
Criando o controller de cabeçalho do pedido
Já foram criados os controllers para os cadastros básicos da aplicação, o cadastro de clientes e produtos, porém agora é necessário criar os controllers para as entidades de movimentação do sistema: cabeçalho do pedido e itens do pedido.
Começaremos pelo controller do cabeçalho do pedido e suas actions. Repita o procedimento para adição de um novo controller, mas dessa vez nomeie como PedidoCabecalho e em Model class selecione o model pedido_cabecalho.
A Listagem 8 apresenta toda a implementação necessária para as operações de CRUD do novo controller e suas respectivas actions. Esta listagem também não apresenta nenhuma novidade para ser comentada, assim sendo, considere como sendo a mesma estrutura de recursos utilizados e explicados nos controllers anteriores.
public class PedidoCabecalhoController : ApiController
{
private BD_FORCA_VENDAEntities db = new BD_FORCA_VENDAEntities();
// GET: api/PedidoCabecalho
public IQueryable<pedido_cabecalho> Getpedido_cabecalho()
{
return db.pedido_cabecalho;
}
// GET: api/PedidoCabecalho/5
[ResponseType(typeof(pedido_cabecalho))]
public async Task<IHttpActionResult> Getpedido_cabecalho(int id)
{
pedido_cabecalho pedido_cabecalho = await db.pedido_cabecalho.FindAsync(id);
if (pedido_cabecalho == null)
{
return NotFound();
}
return Ok(pedido_cabecalho);
}
// PUT: api/PedidoCabecalho/5
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> Putpedido_cabecalho(int id, pedido_cabecalho pedido_cabecalho)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != pedido_cabecalho.idpedido_cabecalho)
{
return BadRequest();
}
db.Entry(pedido_cabecalho).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!pedido_cabecalhoExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
// POST: api/PedidoCabecalho
[ResponseType(typeof(pedido_cabecalho))]
public async Task<IHttpActionResult> Postpedido_cabecalho(pedido_cabecalho pedido_cabecalho)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.pedido_cabecalho.Add(pedido_cabecalho);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = pedido_cabecalho.idpedido_cabecalho }, pedido_cabecalho);
}
// DELETE: api/PedidoCabecalho/5
[ResponseType(typeof(pedido_cabecalho))]
public async Task<IHttpActionResult> Deletepedido_cabecalho(int id)
{
pedido_cabecalho pedido_cabecalho = await db.pedido_cabecalho.FindAsync(id);
if (pedido_cabecalho == null)
{
return NotFound();
}
db.pedido_cabecalho.Remove(pedido_cabecalho);
await db.SaveChangesAsync();
return Ok(pedido_cabecalho);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool pedido_cabecalhoExists(int id)
{
return db.pedido_cabecalho.Count(e => e.idpedido_cabecalho == id) > 0;
}
}
O único ponto importante a destacar é o fato do model pedido_cabecalho ter relacionamentos com os models cliente e pedidos_itens, assim sendo, existe uma certa preocupação no consumo do serviço exposto pelo controlador PedidoCabecalhoController.
No ato da criação de um novo pedido você terá que passar junto com objeto pedido_cabecalho o cliente a que ele pertence, conforme pode ser visto na propriedade cliente presente na linha 29 da Listagem 9. Veja o detalhe também na linha 30, onde temos uma ICollection de pedido_itens.
namespace ForcaVendaWebAPI.Models
{
using System;
using System.Collections.Generic;
public partial class pedido_cabecalho
{
public pedido_cabecalho()
{
this.pedido_itens = new HashSet<pedido_itens>();
}
public int idpedido_cabecalho { get; set; }
public int idcliente { get; set; }
public Nullable<decimal> valor_total_pedido { get; set; }
public Nullable<int> qtde_itens { get; set; }
public string status_pedido { get; set; }
public Nullable<System.DateTime> data_pedido { get; set; }
public virtual cliente cliente { get; set; }
public virtual ICollection<pedido_itens> pedido_itens { get; set; }
}
}
Criando o controller itens do pedido
Para finalizar o projeto, falta apenas criar o controller para os itens do pedido. Assim sendo, repita o processo e crie um novo controller, nomeando-o como PedidoItens e selecionando o model pedido_itens no campo Model class.
A Listagem 10 mostra como deve ficar o controller criado.
public class PedidoItensController : ApiController
{
private BD_FORCA_VENDAEntities db = new BD_FORCA_VENDAEntities();
// GET: api/PedidoItens
public IQueryable<pedido_itens> Getpedido_itens()
{
return db.pedido_itens;
}
// GET: api/PedidoItens/5
[ResponseType(typeof(pedido_itens))]
public async Task<IHttpActionResult> Getpedido_itens(int id)
{
pedido_itens pedido_itens = await db.pedido_itens.FindAsync(id);
if (pedido_itens == null)
{
return NotFound();
}
return Ok(pedido_itens);
}
// PUT: api/PedidoItens/5
[ResponseType(typeof(void))]
public async Task<IHttpActionResult> Putpedido_itens(int id, pedido_itens pedido_itens)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != pedido_itens.idpedido_itens)
{
return BadRequest();
}
db.Entry(pedido_itens).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!pedido_itensExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
// POST: api/PedidoItens
[ResponseType(typeof(pedido_itens))]
public async Task<IHttpActionResult> Postpedido_itens(pedido_itens pedido_itens)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.pedido_itens.Add(pedido_itens);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = pedido_itens.idpedido_itens }, pedido_itens);
}
// DELETE: api/PedidoItens/5
[ResponseType(typeof(pedido_itens))]
public async Task<IHttpActionResult> Deletepedido_itens(int id)
{
pedido_itens pedido_itens = await db.pedido_itens.FindAsync(id);
if (pedido_itens == null)
{
return NotFound();
}
db.pedido_itens.Remove(pedido_itens);
await db.SaveChangesAsync();
return Ok(pedido_itens);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool pedido_itensExists(int id)
{
return db.pedido_itens.Count(e => e.idpedido_itens == id) > 0;
}
}
Com isso temos o nosso serviço Web API pronto para ser consumido pelas demais aplicações, a serem desenvolvidas nas próximas partes desta série de artigos.
O modelo REST permite que clientes distintos, implementados mesmo com tecnologias diferentes, possam consumir os dados do serviço de igual forma, bastando que haja suporte a requisições HTTP.
Esse tipo de arquitetura vem ganhando espaço no desenvolvimento de grandes aplicações, pois modulariza o sistema, deixando-o mais flexível e facilitando sua expansão, inclusive para outras plataformas, característica que é foco deste artigo e será explorada nas próximas edições.
Integrando aplicações com Web API – Parte 2
Este artigoé útil para desenvolvedores front-end que precisam conhecer ou aprofundar seus conhecimentos em Bootstrap, AngularJS e ASP.NET MVC através do desenvolvimento de uma aplicação real para Força de Vendas.
Nesta segunda parte do artigo iremos realizar a implementação da aplicação web para suporte à Força de Vendas, que irá dispor de cadastro cliente, produto e também uma página onde seja possível realizar o gerenciamento dos pedidos enviados pela aplicação Windows Phone que será desenvolvida na terceira parte do artigo.
O desenvolvimento de aplicações web se tornou um mercado totalmente dinâmico, pois a maioria das empresas hoje necessitam de um website na internet, seja para apresentar seus produtos aos clientes, comunicação com parceiros, fornecedores e principalmente para tornar-se competitiva no mercado.
Analisando este cenário, boa parte das aplicações que antes rodavam apenas no desktop hoje estão sendo migradas para web, em forma de website ou mesmo através de um serviço na nuvem para que vários tipos de aplicações cliente possam consumi-lo.
Com o crescimento da web têm surgido vários frameworks para rodar no front-end da aplicação, boa parte deles focado no uso da linguagem JavaScript, que roda no próprio browser do usuário.
Seguindo este crescimento temos a biblioteca jQuery como sendo uma das mais utilizadas, porém hoje existem outros frameworks front-end como opção ao jQuery ou até mesmo para utilizar em conjunto ao jQuery.
O AngularJS é um framework front-end poderoso que dispõe de vários recursos interessantes para implementar e dar vida ao front-end de um website.
Introdução ao AngularJS
O framework AngularJS foi criado pela Google e tem como base o JavaScript, o mesmo pode ser considerado como sendo uma expansão do HTML.
Com uso desta poderosa biblioteca podemos ter novos parâmetros e interagir de forma dinâmica com vários elementos do HTML, sendo possível adicionar novos atributos do AngularJS às tags do HTML, criando um vínculo entre os elementos do HTML e os objetos e variáveis do AngularJS, recurso este conhecido como Data Binding.
O AngularJS conta com vários recursos para melhor organizar a implementação da aplicação front-end, para isso o mesmo dispõe de recursos como módulos, controller, serviços, escopo de aplicação, databind, loops em objetos complexos, rotas URI, requisições Ajax dentre vários outros recursos poderosos presentes nesta magnífica biblioteca.
Criando o projeto no Visual Studio
Para iniciar o desenvolvimento da aplicação web que irá dar suporte à aplicação Força de Vendas Mobile, iremos criar um projeto com Visual Studio 2013. Crie um novo projeto do tipo ASP.NET Web Application (aqui será utilizado o .NET Framework 4.5.1) e nomeie como “ForcaDeVendaAppWeb”.
Na próxima tela que surge clique no botão Change Authentication e marque a opção ‘No Authentication’, pois não iremos usar o sistema de autenticação padrão do Asp.net MVC. Logo após selecione o template MVC e clique em OK.
Baixando o Bootstrap e AngularJS
Para dar prosseguimento ao projeto será necessário baixar dois os frameworks que farão o trabalho pesado no front-end da aplicação. Será baixado o Bootstrap para trabalhamos a parte estrutural e visual das páginas HTML e por fim será usado o AngularJS para realizamos a comunicação com a aplicação ASP.NET Web API e mostrar os dados na interface do usuário.
Para baixar os frameworks podemos utilizar o NuGet no Visual Studio, que é uma ferramenta que auxilia o desenvolvedor na obtenção das dependências do projeto. Estas dependências podem ser frameworks, plug-ins dentre outros.
Para acessar o gerenciador do NuGet basta clicar com botão direito no projeto e depois na opção Manage NuGet Packages, assim será apresentada uma nova janela, conforme a Figura 1.
Com o NuGet aberto podemos pesquisar o nome do componente que desejamos adicionar ao projeto, logo serão listados todos os componentes que contenham o termo pesquisado. Basta clicar no botão install para adicionar o componente ao projeto.
Nesta figura é apresentado o gerenciador do NuGet com a pesquisa pelo framework do AngularJS, perceba que após instalar, o mesmo fica com um ícone verde informando que o mesmo já foi adicionado ao projeto.
Deve ser realizado o mesmo procedimento da instalação do AngularJS para instalar o Bootstrap, pois embora o Visual Studio já adicione por padrão o Bootstrap no projeto, aqui iremos utilizar a versão mais recente.
Definindo carregamento de arquivos CSS e JavaScript
Aplicações web necessitam de arquivos auxiliares com código CSS e JavaScript para construção e definição das funcionalidades front-end da aplicação, ou seja, não é uma boa pratica inserir código CSS ou JavaScript juntamente ao código de marcação HTML.
Assim sendo, sempre que for necessário aplicar formatações na página ou mesmo criar algum código JavaScript, será necessário criar arquivos auxiliares e realizar uma referência aos arquivos na página principal da aplicação.
O ASP.NET MVC tem uma forma bem simples e organizada para carregar arquivos CSS e JavaScript durante a renderização da página principal no browser. Existe uma classe chamada
BundleConfig que se encarrega de unificar diversos arquivos CSS e JavaScript em um só, desta forma teremos uma carga através de um único arquivo, reduzindo a assim o tamanho do arquivo a ser carregado, a quantidade e também dificultando o acesso direto aos arquivos originais.
A classe BundleConfig está presente dentro da pasta App_Start da estrutura de um projeto ASP.NET MVC. Abrindo este arquivo veremos um método chamado RegisterBundles, que recebe por parâmetro uma coleção de BundleCollection. É através do abjeto bundles que iremos informar ao ASP.NET quais arquivos devem ser unificados no ato do carregamento da página do browser do usuário.
Perceba que na estrutura de pastas do projeto ASP.NET MVC existe uma pasta chamada de “Content” para inserirmos os arquivos CSS do projeto. Também há outra pasta chamada Scripts, que é o local onde devem ser salvos os arquivos JavaScript pertencentes ao projeto.
De posse destes detalhes sobre como definir a carga de arquivos CSS e JavaScript no projeto, agora devemos definir dentro arquivo BundleConfig.cs quais arquivos CSS e JavaScript devem ser unificados para carga de arquivos, ou seja, informar os arquivos CSS e JavaScript referentes aos frameworks que iremos utilizar, neste caso o Bootstrap e o AngularJS.
Veja na Listagem 1 as linhas de código que precisam ser inseridas no arquivo BundleConfig para carregar os arquivos do AngularJS. Os arquivos do Bootstrap e demais scripts e estilos, como da jQuery, já devem constar neste artigo por padrão.
bundles.Add(new ScriptBundle("~/bundles/angularjs").Include(
"~/Scripts/angular.min.js"));
bundles.Add(new ScriptBundle("~/bundles/angularjsControllers").Include(
"~/Scripts/AngularJSApp/clienteController.js",
"~/Scripts/AngularJSApp/produtoController.js",
"~/Scripts/AngularJSApp/pedidoCabecalhoController.js"));
}
Boa parte da carga de arquivos informada no arquivo BundleConfig.cs foi realizada automaticamente pelo Visual Studio no ato da criação do projeto, porém é importante destacar outros que foram incluídos posteriormente. Vejamos a seguir alguns detalhes sobre essa listagem.
Estruturando Layout com Bootstrap
Para que seja possível estruturar um layout de páginas de um website de forma organizada com Bootstrap e poder compartilhar esta estrutura com as demais páginas do site, a melhor alternativa é usar um arquivo de layout.
Quando se cria um projeto ASP.NET MVC, ele automaticamente cria um arquivo de layout chamado de _Layout.cshtml, presente dentro da pasta Views>Shared, algo parecido com as Master Pages do ASP.NET Web Forms.
É neste arquivo de layout que iremos definir o menu de navegação da aplicação Força de Vendas de forma simples e objetiva com uso do menu de navegação do Bootstrap.
Para entender como deve ser construído um arquivo de layout com ASP.NET MVC com uso do Bootstrap, analise o código presente na Listagem 2.
<!DOCTYPE html>
<html data-ng-app=”app”>
<head>
<meta charset=”utf-8” />
<meta name=”viewport” content=”width=device-width, initial-scale=1.0”>
<title>@ViewBag.Title – Força de Vendas</title>
@Styles.Render(“~/Content/css”)
@Scripts.Render(“~/bundles/modernizr”)
</head>
<body>
<div class=”navbar navbar-inverse navbar-fixed-top”>
<div class=”container”>
<div class=”navbar-header”>
<button type=”button” class=”navbar-toggle” data-toggle=”collapse” data-target=”.navbar-collapse”>
<span class=”icon-bar”></span>
<span class=”icon-bar”></span>
<span class=”icon-bar”></span>
</button>
@Html.ActionLink(“App Força de Vendas”, “Index”, “Home”, new { area = “” }, new { @class = “navbar-brand” })
</div>
<div class=”navbar-collapse collapse”>
<ul class=”nav navbar-nav”>
<li>@Html.ActionLink(“Clientes”, “Index”, “Cliente”)</li>
<li>@Html.ActionLink(“Produtos”, “Index”, “Produto”)</li>
<li>@Html.ActionLink(“Pedidos”, “Index”, “PedidoCabecalho”)</li>
</ul>
</div>
</div>
</div>
<br /><br /><br />
<div class=”container body-content”>
@RenderBody()
<hr />
<footer>
<p>© @DateTime.Now.Year – App Força de Vendas</p>
</footer>
</div>
@Scripts.Render(“~/bundles/jquery”)
@Scripts.Render(“~/bundles/bootstrap”)
@Scripts.Render(“~/bundles/angularjs”)
@Scripts.Render(“~/bundles/angularjsControllers”)
@RenderSection(“scripts”, required: false)
</body>
</html>
Essa listagem tem o menu de navegação da aplicação, porém existem outros detalhes que precisam de um melhor esclarecimento. Veja a seguir os detalhes da listagem.
Criando um Controller com AngularJS para o cadastro de clientes
O AngularJS é um framework que trabalha no front-end da aplicação e para organizar o código o mesmo dispõe de uma estrutura de módulos, serviços, controllers, dentre outros recursos. Assim podemos dividir as responsabilidades e funcionalidades da aplicação de forma clara, objetiva e manutenível.
O primeiro controller a ser criado será para o cadastro de clientes, para isso crie uma pasta chamada “AngularJSApp” dentro da pasta Scripts, que já consta na estrutura do projeto.
É na pasta AngularJSApp que iremos criar todos os arquivos JavaScript que irão representar os controllers do AngularJS na aplicação Força de Vendas.
Clique com botão direito na pasta AngularJSApp e na opção Add>New Item, logo após será aberta uma nova janela onde deve ser selecionado o tipo de arquivo JavaScript File. Nomeie de clienteController.js e pressione Add para que o arquivo possa ser criado.
O arquivo clienteController.js irá conter todo o código necessário para dar vida à view de cadastro de cliente e também acessar o serviço do ASP.NET Web API para realizar as operações de CRUD. Para melhor entendimento de como deve ficar a implementação do Controller Cliente com AngularJS, veja o código presente na Listagem 3.
//Seta o módulo para o AngularJS
var app = angular.module('app', []);
//Cria o controller cliente
app.controller('clienteController', ['$scope', '$http', clienteController]);
//Método para o controller cliente do AngujarJS
function clienteController($scope, $http) {
//Declaração de variáveis
$scope.exibeformNovoCliente = false;
$scope.ocultaTabelaListagemClientes = false;
$scope.exibeformAlterarCliente = false;
//Função para exibir o form para cadastro do novo cliente
$scope.funcformNovoCliente = function () {
$scope.exibeformNovoCliente = true;
$scope.ocultaTabelaListagemClientes = true;
};
//Ocultar o form de novo cliente e exibir a listagem
$scope.funcCancelarCadastro = function () {
$scope.exibeformNovoCliente = false;
$scope.ocultaTabelaListagemClientes = false;
};
//Cadastrar cliente
$scope.cadastrarCliente = function () {
var dataAtual = new Date();
this.novocliente.data_cadastro = dataAtual;
$http.post('http://localhost/ForcaVendaWebAPI/api/Clientes/Postcliente/', this.novocliente).success(function (data) {
alert("Cliente cadastrado com sucesso.");
$scope.exibeformNovoCliente = false;
$scope.ocultaTabelaListagemClientes = false;
$scope.clientes.push(data);
$scope.novocliente = null;
}).error(function (data) {
$scope.error = "Erro ao cadastrar o cliente! " + data;
});
};
//Buscar listagem de clientes
$http.get('http://localhost/ForcaVendaWebAPI/api/Clientes/Getcliente/').success(function (data) {
$scope.clientes = data;
})
.error(function () {
$scope.error = "Erro ao carregar a listagem de clientes!";
});
//Função para exibir o form para cadastro do novo cliente
$scope.funcformEditarCliente = function () {
$scope.clienteAlterar = this.cliente;
$scope.exibeformNovoCliente = false;
$scope.ocultaTabelaListagemClientes = true;
$scope.exibeformAlterarCliente = true;
};
//Ocultar o form de novo cliente e exibir a listagem
$scope.funcCancelarEdicao = function () {
$scope.exibeformNovoCliente = false;
$scope.ocultaTabelaListagemClientes = false;
$scope.exibeformAlterarCliente = false;
};
//Editar cliente
$scope.editarCliente = function () {
var cli = this.clienteAlterar;
$http.put('http://localhost/ForcaVendaWebAPI/api/Clientes/Putcliente/' + cli.idcliente, cli).success(function (data) {
alert("Cliente alterado com sucesso.");
$scope.exibeformNovoCliente = false;
$scope.ocultaTabelaListagemClientes = false;
$scope.exibeformAlterarCliente = false;
}).error(function (data) {
$scope.error = "Erro ao alterar o cliente! " + data;
});
};
//Deletar cliente
$scope.deletarCliente = function () {
var id = this.cliente.idcliente;
$http.delete('http://localhost/ForcaVendaWebAPI/api/Clientes/Deletecliente/' + id).success(function (data) {
alert("Cliente deletado com sucesso.");
$.each($scope.clientes, function (i) {
if ($scope.clientes[i].idcliente === id) {
$scope.clientes.splice(i, 1);
return false;
}
});
}).error(function (data) {
$scope.error = "Erro ao deletar o cliente." + data;
});
};
}
Essa listagem já contém vários comentários básicos sobre as funcionalidades a serem implementadas na view de cadastro de clientes, que é o próximo passo, porém veja a seguir algumas explicações sobre a estrutura e recursos do AngularJs utilizados nesse controller.
Desenvolvendo a página cadastro de clientes
Para criar a página de cadastro de clientes no ASP.NET MVC é necessário criar um controle seguindo de uma view que corresponde à página com marcação HTML. Assim sendo, o controller recebe a solicitação do cliente via protocolo HTTP e em seguida o controller verifica a URL do recurso solicitado, bem como qual verbo do protocolo HTTP foi utilizado na requisição.
Após o controller analisar a requisição, então ele retorna a view correspondente para o usuário, ou seja, é devolvida uma resposta ao usuário em forma de HTML, podendo também ser retornados outros tipos de dados como o JSON, o que será feito nas requisições realizadas na aplicação ASP.NET Web API que dispõe de recursos de acesso a dados.
O primeiro passo para criar a página de cadastro de cliente é criar o controller, para isso clique com o botão direito em cima da pasta Controllers e depois na opção Add>Controller. Será apresentada uma nova janela onde você deve selecionar a opção “MVC 5 Controller – Empty” e clicar em Add.
Por fim, na próxima tela que surge, defina o nome do controller para “Cliente”e clique Add novamente. Após realizado este procedimento será criado um arquivo chamado ClienteController.cs dentro da pasta Controllers do projeto, que traz por padrão a action Index que retorna a view de mesmo nome.
Após criado o controller é necessário criar a view referente à action Index. Para realizar este procedimento devemos clicar com botão direito em cima do nome da action (Index) e escolher a opção Add View.
Logo em seguida será apresentada uma nova janela onde podemos alterar o nome da view ou selecionar um dos templates automáticos oferecidos pelo IDE. Neste caso, mantenha o template Empty (without model) e clique em Add.
O Visual Studio criou o arquivo Index.cshtml dentro da pasta Views>Cliente. Neste arquivo ainda não consta nenhum código, desta forma precisamos implementar as funções de CRUD na view, vinculando aos recursos criados no controller cliente com o AngularJS. Para realizar este procedimento, a view Index deve ser codificada conforme código presente na Listagem 4.
@{ ViewBag.Title = "Clientes"; }
<div data-ng-controller="clienteController">
<div class="panel panel-default">
<div class="panel-heading">Controle de clientes</div>
<div class="panel-body">
<div class="row">
<!--row form novoCliente-->
<div class="col-md-12">
<strong class="error">{{ error }}</strong>
<form name = "novoCliente" data-ng-show="exibeformNovoCliente">
<div class="form-group">
<div class="col-sm-12">
<label for="cadastro" class="col-sm-12 control-label col-md-offset-1">::CADASTRAR NOVO CLIENTE::</label>
</div>
<label for="nome" class="col-sm-2 control-label">Nome:</label>
<div class="col-sm-10">
<input type = "text" class="form-control" id="nome" data-ng-model="novocliente.nome" required />
</div>
<label for="cpf" class="col-sm-2 control-label">CPF/CNPJ:</label>
<div class="col-sm-10">
<input type = "number" class="form-control" id="cpf_cnpj" data-ng-model="novocliente.cpf_cnpj" required />
</div>
...demais campos do cadastro do cliente
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<input type = "submit" value="Cadastrar" data-ng-click="cadastrarCliente()" class="btn btn-primary" />
<input type = "button" value="Cancelar" data-ng-click="funcCancelarCadastro()" class="btn btn-primary" />
</div>
</div>
</div>
</form>
</div>
</div><!-- fim row form novoCliente-->
<div class="row">
<!--row table listagem de clientes-->
<div class="col-md-12">
<div class="table-responsive" data-ng-hide="ocultaTabelaListagemClientes">
<p><a data-ng-click="funcformNovoCliente()" class="btn btn-primary">Novo cliente</a></p>
<table class="table table-bordered table-hover">
<tr>
<td>ID</td>
<th>Nome</th>
<th>CPF/CNPJ</th>
... cabeçalho das demais colunas
<th></th>
</tr>
<tr data-ng-repeat= "cliente in clientes" >
<td>{ { cliente.idcliente } }</td>
<td>{{ cliente.nome }}</td>
<td>{{ cliente.cpf_cnpj }}</td>
... demais colunas
<td> <a data-ng-click="funcformEditarCliente(cliente)" href="javascript:;">Editar</a>
| <a data-ng-click="deletarCliente(cliente)" href="javascript:;">Deletar</a> </td>
</tr>
</table>
</div><!--fim div class="table-responsive"-->
</div>
</div><!--fim row table listagem de clientes-->
<div class="row">
<!--row form alterarCliente-->
<div class="col-md-12">
<form name = "alterarCliente" data-ng-show="exibeformAlterarCliente">
<div class="form-group">
<div class="col-sm-12">
<label for="alterar" class="col-sm-12 control-label col-md-offset-1">::ALTERAR CADASTRO CLIENTE::</label>
</div>
<label for="idcliente" class="col-sm-2 control-label">ID:</label>
<div class="col-sm-10">
<input type = "number" class="form-control" id="idcliente" data-ng-model="clienteAlterar.idcliente" required />
</div>
<label for="nome" class="col-sm-2 control-label">Nome:</label>
<div class="col-sm-10">
<input type = "text" class="form-control" id="nome" data-ng-model="clienteAlterar.nome" required />
</div>
<label for="cpf" class="col-sm-2 control-label">CPF/CNPJ:</label>
<div class="col-sm-10">
<input type = "text" class="form-control" id="cpf_cnpj" data-ng-model="clienteAlterar.cpf_cnpj" required />
</div>
... demais campos do cadastro do cliente
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<input type = "submit" value="Salvar alterações" data-ng-click="editarCliente()" class="btn btn-primary" />
<input type = "button" value="Cancelar" data-ng-click="funcCancelarEdicao()" class="btn btn-primary" />
</div>
</div>
</div>
</form>
</div>
</div><!-- fim row form alterarCliente-->
</div><!--class="panel-body"-->
</div><!--Fim class="panel panel-default"-->
</div><!--Fim data-ng-controller="clienteController"-->
Boa parte do código dessa listagem faz apenas referência às classes CSS do Bootstrap, porém é necessária uma melhor explicação quanto à vinculação dos elementos HTML com o código do controller Cliente do AngularJS. Veja a seguir mais alguns comentários sobre esse código.
O cadastro de cliente já se encontra totalmente funcional, o interessante agora é testar a aplicação e verificar o seu funcionamento, desta forma iremos precisar do serviço do ASP.NET Web API rodando na mesma porta da aplicação Web.
Para realizar tal procedimento podemos importar o projeto da Web API para a solution da aplicação Web e alterar as propriedades dos projetos para rodar no servidor IIS Local ao invés do IIS do Visual Studio.
Você precisará inicializar o Visual Studio como administrador para que o mesmo possa criar os diretórios virtuais no IIS Local através do clique do botão Create Virtual Directory localizado na propriedade do projeto guia Web.
Agora rode a aplicação cadastre alguns clientes e veja o resultado, que deve ser semelhante ao que é apresentado nas Figuras 2 a 4.
Criando o Controller do AngularJS para o cadastro de produtos
Para criar o cadastro de produtos faremos os mesmos passos usados no cadastro de clientes. Assim sendo, agora é necessário criar um controller para o AngularJS, então clique com botão direito na pasta AngularJSApp e crie um novo arquivo JavaScript chamado de produtoController.js.
Este arquivo irá ter todas as funcionalidades necessárias para as operações de CRUD referente ao cadastro de produtos.
Após realizado o passo anterior, devemos implementar o produtoController conforme código presente na Listagem 5. Este código já está comentado e é bem parecido com o código do clienteController, o que muda é somente o contexto para o cadastro de produto, então não teremos dificuldades para compreender esta implementação.
//Cria o controller produto
app.controller('produtoController', ['$scope', '$http', produtoController]);
//Método para o controller produto do AngujarJS
function produtoController($scope, $http) {
//Declaração de variáveis
$scope.exibeformNovoProduto = false;
$scope.ocultaTabelaListagemProdutos = false;
$scope.exibeformAlterarProduto = false;
//Função para exibir o form para cadastro do novo produto
$scope.funcformNovoProduto = function () {
$scope.exibeformNovoProduto = true;
$scope.ocultaTabelaListagemProdutos = true;
};
//Ocultar o form de novo produto e exibir a listagem
$scope.funcCancelarCadastro = function () {
$scope.exibeformNovoProduto = false;
$scope.ocultaTabelaListagemProdutos = false;
};
//Função cadastrar produto
$scope.cadastrarProduto = function () {
var dataAtual = new Date();
this.novoproduto.data_cadastro = dataAtual;
$http.post('http://localhost/ForcaVendaWebAPI/api/Produtos/Postproduto/', this.novoproduto).success(function (data) {
alert("Produto cadastrado com sucesso.");
$scope.exibeformNovoProduto = false;
$scope.ocultaTabelaListagemProdutos = false;
$scope.produtos.push(data);
$scope.novoproduto = null;
}).error(function (data) {
$scope.error = "Erro ao cadastrar o produto! " + data;
});
};
//Buscar listagem de produtos
$http.get('http://localhost/ForcaVendaWebAPI/api/Produtos/Getproduto/').success(function (data) {
$scope.produtos = data;
})
.error(function () {
$scope.error = "Erro ao carregar a listagem de produtos!";
});
//Função para exibir o form para cadastro do novo produto
$scope.funcformEditarProduto = function () {
$scope.produtoAlterar = this.produto;
$scope.exibeformNovoProduto = false;
$scope.ocultaTabelaListagemProdutos = true;
$scope.exibeformAlterarProduto = true;
};
//Ocultar o form de novo produto e exibir a listagem
$scope.funcCancelarEdicao = function () {
$scope.exibeformNovoProduto = false;
$scope.ocultaTabelaListagemProdutos = false;
$scope.exibeformAlterarProduto = false;
};
//Editar produto
$scope.editarProduto = function () {
var prod = this.produtoAlterar;
$http.put('http://localhost/ForcaVendaWebAPI/api/Produtos/Putproduto/' + prod.idproduto, prod).success(function (data) {
alert("Produto alterado com sucesso.");
$scope.exibeformNovoProduto = false;
$scope.ocultaTabelaListagemProdutos = false;
$scope.exibeformAlterarProduto = false;
}).error(function (data) {
$scope.error = "Erro ao alterar o produto! " + data;
});
};
//Deletar produto
$scope.deletarProduto = function () {
var id = this.produto.idproduto;
$http.delete('http://localhost/ForcaVendaWebAPI/api/Produtos/Deleteproduto/' + id).success(function (data) {
alert("Produto deletado com sucesso.");
$.each($scope.produtos, function (i) {
if ($scope.produtos[i].idproduto === id) {
$scope.produtos.splice(i, 1);
return false;
}
});
}).error(function (data) {
$scope.error = "Erro ao deletar o produto." + data;
});
};
}
Criando o Controller e View ASP.NET para o cadastro de produto
Para criarmos a view para o CRUD de produtos, primeiramente precisaremos criar o controller do ASP.NET MVC, então repita os passos do controller de clientes, mas nomeie agora de ProdutoController.
O próximo passo é criar a view correspondente ao ProdutoController, para isso basta repetir o procedimento executado para criar a view Index do cadastro de cliente. Dessa vez a view será criada na pasta Produto.
A nova view criada também não terá nenhuma implementação nova em comparação à view do cadastro de clientes, o que muda é apenas contexto para considerar a entidade produto. A view deve ser implementada conforme código presente na Listagem 6.
@{ ViewBag.Title = "Produtos"; }
<div data-ng-controller="produtoController">
<div class="panel panel-default">
<div class="panel-heading">Controle de Produtos</div>
<div class="panel-body">
<div class="row"> <!--row form novoproduto-->
<div class="col-md-12">
<strong class="error">{{ error }}</strong>
<form name = "novoproduto" data-ng-show="exibeformNovoProduto">
<div class="form-group">
<div class="col-sm-12">
<label for="cadastro" class="col-sm-12 control-label col-md-offset-1">::CADASTRAR NOVO PRODUTO::</label>
</div>
<label for="descricao" class="col-sm-2 control-label">Descrição:</label>
<div class="col-sm-10">
<input type = "text" class="form-control" id="descricao" data-ng-model="novoproduto.descricao" required />
</div>
<label for="preco_compra" class="col-sm-2 control-label">Preço de compra:</label>
<div class="col-sm-10">
<input type = "number" class="form-control" id="preco_compra" data-ng-model="novoproduto.preco_compra" required />
</div>
... demais campos
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<input type = "submit" value="Cadastrar" data-ng-click="cadastrarProduto()" class="btn btn-primary" />
<input type = "button" value="Cancelar" data-ng-click="funcCancelarCadastro()" class="btn btn-primary" />
</div>
</div>
</div>
</form>
</div>
</div><!-- fim row form novoproduto-->
<div class="row"> <!--row table listagem de produtos-->
<div class="col-md-12">
<div class="table-responsive" data-ng-hide="ocultaTabelaListagemProdutos">
<p><a data-ng-click="funcformNovoProduto()" class="btn btn-primary">Novo produto</a></p>
<table class="table table-bordered table-hover">
<tr>
<td>ID</td>
<th>Descrição</th>
<th>Preço Compra</th>
...demais campos
<th></th>
</tr>
<tr data-ng-repeat= "produto in produtos" >
< td >{ { produto.idproduto } }</td>
<td>{{ produto.descricao }}</td>
<td>{{ produto.preco_compra }}</td>
...demais campos
<td> <a data-ng-click="funcformEditarProduto(produto)" href="javascript:;">Editar</a>
| <a data-ng-click="deletarProduto(produto)" href="javascript:;">Deletar</a> </td>
</tr>
</table>
</div><!--fim div class="table-responsive"-->
</div>
</div><!--fim row table listagem de produtos-->
<div class="row"> <!--row form alterarproduto-->
<div class="col-md-12">
<form name = "alterarproduto" data-ng-show="exibeformAlterarProduto">
<div class="form-group">
<div class="col-sm-12">
<label for="alterar" class="col-sm-12 control-label col-md-offset-1">::ALTERAR CADASTRO produto::</label>
</div>
<label for="idproduto" class="col-sm-2 control-label">ID:</label>
<div class="col-sm-10">
<input type = "number" class="form-control" id="idproduto" data-ng-model="produtoAlterar.idproduto" required />
</div>
<label for="descricao" class="col-sm-2 control-label">Descrição:</label>
<div class="col-sm-10">
<input type = "text" class="form-control" id="descricao" data-ng-model="produtoAlterar.descricao" required />
</div>
<label for="preco_compra" class="col-sm-2 control-label">Preço de Compra:</label>
<div class="col-sm-10">
<input type = "text" class="form-control" id="preco_compra" data-ng-model="produtoAlterar.preco_compra" required />
</div>
... demais campos
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<input type = "submit" value="Salvar alterações" data-ng-click="editarProduto()" class="btn btn-primary" />
<input type = "button" value="Cancelar" data-ng-click="funcCancelarEdicao()" class="btn btn-primary" />
</div>
</div>
</div>
</form>
</div>
</div><!-- fim row form alterarproduto-->
</div><!--class="panel-body"-->
</div><!--Fim class="panel panel-default"-->
</div><!--Fim data-ng-controller="produtoController"-->
Execute o projeto para verificar o resultado e realizar testes nas operações de CRUD, conforme pode ser visto na Figura 5.
Criando o Controller do AngularJS para gerenciamento de pedidos
A aplicação Web também terá a funcionalidade de gerenciamento de pedidos enviados pelos vendedores via app Windows Phone, assim sendo, todos os pedidos enviados ficarão com status de bloqueado para que o responsável pelo faturamento possa analisar os pedidos e realizar a liberação caso seja necessário, ou seja, o mesmo deve alterar o status do pedido. O administrador também poderá excluir pedidos via gerenciador.
Crie um novo arquivo na pasta AngularJSApp chamado de “pedidoCabecalhoController.js”, é neste arquivo que iremos implementar o código da Listagem 7 que irá ficar responsável por conter as funcionalidades do gerenciamento de pedidos.
//Cria o controller pedido cabeçalho
app.controller('pedidoCabecalhoController', ['$scope', '$http', pedidoCabecalhoController]);
//Método para o controller pedido cabeçalho
function pedidoCabecalhoController($scope, $http) {
//Declaração de variáveis
$scope.ocultaTabelaListagempedidoCabecalhos = false;
$scope.exibeformAlterarpedidoCabecalho = false;
//Buscar listagem de pedido cabeçalho
$http.get('http://localhost/ForcaVendaWebAPI/api/PedidoCabecalho/Getpedido_cabecalho/').success(function (data) {
$scope.pedidoCabecalhos = data;
})
.error(function () {
$scope.error = "Erro ao carregar a listagem de pedidos!";
});
//Função para exibir o form para editar pedido
$scope.funcformEditarpedidoCabecalho = function () {
$scope.pedidoCabecalhoAlterar = this.pedidoCabecalho;
$scope.ocultaTabelaListagempedidoCabecalhos = true;
$scope.exibeformAlterarpedidoCabecalho = true;
};
//Ocultar o form de edição de pedido e exibir a listagem
$scope.funcCancelarEdicao = function () {
$scope.ocultaTabelaListagempedidoCabecalhos = false;
$scope.exibeformAlterarpedidoCabecalho = false;
};
//Editar o status do pedido cabeçalho
$scope.editarpedidoCabecalho = function () {
var ped = this.pedidoCabecalhoAlterar;
$http.put('http://localhost/ForcaVendaWebAPI/api/PedidoCabecalho/Putpedido_cabecalho/' + ped.idpedido_cabecalho, ped).success(function (data) {
alert("Pedido alterado com sucesso.");
$scope.ocultaTabelaListagempedidoCabecalhos = false;
$scope.exibeformAlterarpedidoCabecalho = false;
}).error(function (data) {
$scope.error = "Erro ao alterar o pedido! " + data;
});
};
//Deletar pedido
$scope.deletarpedidoCabecalho = function () {
var id = this.pedidoCabecalho.idpedido_cabecalho;
$http.delete('http://localhost/ForcaVendaWebAPI/api/PedidoCabecalho/Deletepedido_cabecalho/' + id).success(function (data) {
alert("Pedido deletado com sucesso.");
$.each($scope.pedidoCabecalhos, function (i) {
if ($scope.pedidoCabecalhos[i].idpedidoCabecalho === id) {
$scope.pedidoCabecalhos.splice(i, 1);
return false;
}
});
}).error(function (data) {
$scope.error = "Erro ao deletar o pedido." + data;
});
};
}
O código dessa listagem já se encontra comentado e é bem similar às implementações dos controllers do cadastro de clientes e produtos.
Controller e View para o gerenciamento de pedidos
O último passo na implementação da aplicação Força de Vendas é criar o controller e view para o gerenciamento dos pedidos emitidos. Assim sendo, crie o novo controller clicando na pasta Controllers e na opção Add>Controller. Nomeie o controller como PedidoCabecalhoController e por clique em Add.
Com o controller criado, agora será necessário criar a view para construção da tela de gerenciamento de pedidos. Repita o processo já realizado anteriormente e no arquivo Index.cshtml gerado dentro da pasta Views> PedidoCabecalho, implemente o código presente na Listagem 8.
@{ ViewBag.Title = "pedidoCabecalhos"; }
<div data-ng-controller="pedidoCabecalhoController">
<div class="panel panel-default">
<div class="panel-heading">Controle de Pedidos</div>
<div class="panel-body">
<div class="row"> <!--row table listagem de pedidoCabecalhos-->
<div class="col-md-12">
<div class="table-responsive" data-ng-hide="ocultaTabelaListagempedidoCabecalhos">
<!--<p><a data-ng-click="funcformNovopedidoCabecalho()" class="btn btn-primary">Novo pedidoCabecalho</a></p>-->
<table class="table table-bordered table-hover">
<tr>
<td>Id Pedido</td>
<th>Id Cliente</th>
<th>Nome cliente</th>
<th>Valor total pedid16o</th>
<th>Qtde itens</th>
<th>Status</th>
<th>Data pedido</th>
<th></th>
</tr>
<tr data-ng-repeat="pedidoCabecalho in pedidoCabecalhos">
<td>{{ pedidoCabecalho.idpedido_cabecalho }}</td>
<td>{{ pedidoCabecalho.idcliente }}</td>
<td>{{ pedidoCabecalho.cliente.nome }}</td>
<td>{{ pedidoCabecalho.valor_total_pedido }}</td>
<td>{{ pedidoCabecalho.qtde_itens }}</td>
<td>{{ pedidoCabecalho.status_pedido }}</td>
<td>{{ pedidoCabecalho.data_pedido }}</td>
<td>
<a data-ng-click="funcformEditarpedidoCabecalho(pedidoCabecalho)" href="javascript:;">Editar</a>
| <a data-ng-click="deletarpedidoCabecalho(pedidoCabecalho)" href="javascript:;">Deletar</a>
</td>
</tr>
</table>
</div><!--fim div class="table-responsive"-->
</div>
</div><!--fim row table listagem de pedidoCabecalhos-->
<div class="row">
<!--row form alterarpedidoCabecalho-->
<div class="col-md-12">
<form name="alterarpedidoCabecalho" data-ng-show="exibeformAlterarpedidoCabecalho">
<div class="form-group">
<div class="col-sm-12">
<label for="alterar" class="col-sm-12 control-label col-md-offset-1">:::ALTERAR STATUS PEDIDO:::</label>
</div>
<label for="idpedido_cabecalho" class="col-sm-2 control-label">Id pedido:</label>
<div class="col-sm-10">
<input type="number" class="form-control" id="idpedido_cabecalho" data-ng-model="pedidoCabecalhoAlterar.idpedido_cabecalho" required disabled />
</div>
<label for="idcliente" class="col-sm-2 control-label">Id cliente:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="idcliente" data-ng-model="pedidoCabecalhoAlterar.idcliente" required disabled />
</div>
<label for="cliente.nome" class="col-sm-2 control-label">Nome cliente:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="cliente.nome" data-ng-model="pedidoCabecalhoAlterar.cliente.nome" required disabled />
</div>
<label for="valor_total_pedido" class="col-sm-2 control-label">Valor total pedido:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="valor_total_pedido" data-ng-model="pedidoCabecalhoAlterar.valor_total_pedido" required disabled />
</div>
<label for="qtde_itens" class="col-sm-2 control-label">Qdte Itens:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="qtde_itens" data-ng-model="pedidoCabecalhoAlterar.qtde_itens" required disabled />
</div>
<label for="status_pedido" class="col-sm-2 control-label">Status:</label>
<div class="col-sm-10">
<select data-ng-model="pedidoCabecalhoAlterar.status_pedido" class="form-control">
<option value="{{ pedidoCabecalho.status_pedido }}">{{ pedidoCabecalho.status_pedido }}</option>
<option value="cancelado">cancelado</option>
<option value="em aberto">em aberto</option>
<option value="bloqueado">bloqueado</option>
<option value="entregue">entregue</option>
<option value="em transito">em transito</option>
</select>
</div>
<label for="data_pedido" class="col-sm-2 control-label">Data Cadastro:</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="data_pedido" data-ng-model="pedidoCabecalhoAlterar.data_pedido" required disabled />
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<input type="submit" value="Salvar alterações" data-ng-click="editarpedidoCabecalho()" class="btn btn-primary" />
<input type="button" value="Cancelar" data-ng-click="funcCancelarEdicao()" class="btn btn-primary" />
</div>
</div>
</div>
</form>
</div>
</div><!-- fim row form alterarpedidoCabecalho-->
</div><!--class="panel-body"-->
</div><!--Fim class="panel panel-default"-->
</div><!--Fim data-ng-controller="pedidoCabecalhoController"-->
Antes de executar o projeto é interessante cadastrar alguns pedidos para verificar a listagem dos mesmos no gerenciador e também testar a função de alteração e exclusão de pedidos via gerenciador.
Ao executar o projeto podemos observar o resultado, que deve ser semelhante às Figuras 6 e 7.
Note que não utilizamos aqui os helpers do Razor para criar os elementos HTML e ligá-los às propriedades dos models. Tudo isso foi feito utilizando os atributos do AngularJS, que faz com que as views estejam ainda mais desacopladas da linguagem server-side utilizada no projeto, neste caso C# (que na verdade nem foi utilizada como geralmente vemos em projetos ASP.NET MVC).
Com um serviço RESTful robusto atuando como back-end principal da nossa aplicação de Força de Vendas, a criação de novos módulos se torna bastante simples, inclusive se for necessário utilizar várias linguagens e plataformas, bastando haver suporte para o consumo de APIs REST através de requisições HTTP.
Integrando aplicações com Web API – Parte 3
Este artigo é útil para desenvolvedores que visam expandir o número de usuários de suas aplicações, desenvolvendo soluções para o sistema operacional Windows Phone, bem como entender na prática como aplicar o padrão de desenvolvimento MVVM associado ao padrão Proxy para consumo de um serviço externo.
Nesta terceira parte da série, desenvolveremos uma aplicação que funcionará como módulo mobile da aplicação e Força de Vendas e se comunicará com o serviço RESTful criado na primeira parte.
Aplicações orientadas a serviços têm ganhado bastante espaço no mercado de software, devido a garantirem maior flexibilidade e escalabilidade, fatores cruciais para qualquer aplicação atualmente.
Como a que estamos desenvolvendo nessa série, boa parte da lógica de negócio e do armazenamento de dados fica contida em um ou mais serviços na internet, os quais são consumidos por aplicações “front-end”, sejam elas mobile, web ou desktop.
Serviços RESTful se mostram atualmente como a melhor opção nesse tipo de arquitetura, uma vez que a comunicação com esse tipo de aplicação se dá baseada no protocolo HTTP, com requisições e respostas nas quais o conteúdo é enviado e recebido através de métodos GET, POST, PUT, DELETE, etc.
Para consumir esse tipo de serviço é preciso basicamente que a tecnologia a ser utilizada ofereça suporte a requisições HTTP, o que atualmente é feito pela maioria das linguagens e plataformas de programação.
Na parte 2 dessa série vimos a aplicação prática desse conceito, onde criamos uma aplicação com AngularJS que consumia nosso serviço Web API.
Nesta terceira e última parte seguiremos a mesma lógica para o desenvolvimento de uma aplicação Windows Phone que representará mais um módulo da aplicação, aquele que será usado pelos vendedores que atendem a seus clientes e emitem pedidos a partir de seu smartphone.
Regras de negócio
Este módulo da aplicação terá algumas limitações, se comparado ao módulo web. Esta aplicação terá por objetivo funcionar como ferramenta de informatização do processo de vendas a domicílio, ou seja, aquelas que são feitas pelo vendedor ao visitar seus clientes.
O vendedor deverá ser capaz de visualizar os clientes cadastrados, cadastrar novos clientes, editar ou excluir seu cadastro, porém só poderá visualizar o cadastro de produtos, sem inserir ou editar registros, pois isso será feito unicamente pelo administrador no módulo web.
A aplicação também permitirá ao vendedor emitir novos pedidos, selecionando o cliente que está atendendo e os produtos que serão vendidos. As informações de total do pedido e quantidade de itens serão calculadas automaticamente, mantendo assim a integridade das informações e reduzindo a probabilidade de falha humana.
A partir do que será desenvolvido neste artigo o leitor poderá expandir a aplicação, implementando novas regras de negócio e funcionalidades adicionais.
O padrão MVVM
O padrão MVVM (Model-View-ViewModel), assim como o MVC (Model-View-Controller) e MVP (Model-View-Presenter), do qual deriva, tem por objetivo dividir os componentes da aplicação de acordo com suas responsabilidades.
Nesse padrão, o Model representa as classes de domínio da aplicação, ou seja, aquelas que modelam o negócio e estão diretamente relacionadas ao banco de dados, quando existir. As Views formam a camada superior, aquela que é visualizada pelo usuário final para exibição e entrada de dados.
Em aplicações Universais e WPF as views são formadas por código XAML. Já os ViewModels representam o componente intermediário nessa arquitetura, responsável por prover dados às views com base nos models.
Esse padrão foi desenvolvido especificamente para funcionar com aplicações que usam XAML (WPF e Silverlight na época), portanto sua implementação eficiente está diretamente relacionada aos recursos oferecidos pelo .NET Framework e pela linguagem XAML, como a implementação do padrão Observer (BOX 1) e os mecanismos de Data Binding.
O Oberver, também chamado de Publisher-Subscriber, é um padrão de projeto que define uma relação de dependência entre objetos, de forma que quando um dado objeto (Observable) é alterado, todos aqueles que dependem dele (Observers) são automaticamente notificados sobre essa mudança.
Para o desenvolvimento da aplicação neste artigo serão utilizados o Visual Studio 2015 e a versão 6.0 da linguagem C#, bem como o .NET Framework 4.6, porém, versões anteriores podem ser utilizadas sem grandes dificuldades, bastando algumas adaptações em pontos onde utilizaremos recursos próprios do # 6.0.
Começaremos então criando uma nova aplicação do tipo Blank App (Windows Phone) para Windows Phone 8.1, como mostra a Figura 1, já que no momento da publicação deste artigo, esta é a atual versão estável do sistema operacional mobile da Microsoft. Smartphones com Windows 10 Mobile também poderão executar as aplicações desenvolvidas para a versão 8.1, portanto com essa escolha, nesse momento, conseguimos atingir um maior número de usuários.
Com o projeto criado, podemos criar as pastas que representarão cada componente da aplicação e facilitarão a organização e leitura do projeto.
Adicione então as pastas Proxy, Models, ViewModels e Views clicando sobre o projeto no Solution Explorer e então em Add > New Folder, deixando a estrutura do projeto como a que é vista na Figura 2.
Models
As primeiras classes a serem criadas serão aquelas que modelam o domínio da aplicação, ou seja, as classes model. Para facilitar a conversão dos dados que serão enviados e recebidos para o serviço Web API, essas classes terão basicamente a mesma estrutura das que foram geradas pelo Entity Framework na parte 1 dessa série. Essa não é, de fato, uma regra, porém para esse contexto atende bem a nossa necessidade.
Na Listagem 1 temos o código da classe Cliente, que se comparada à que foi gerada pelo mapeamento da tabela do banco de dados, é praticamente igual, mas aqui estamos utilizando o recurso de Property Initializer do C# 6.0 nas linhas 12 e 13.
public class Cliente
{
public int IDCliente { get; set; }
public string Nome { get; set; }
public string CPF_CNPJ { get; set; }
public string Endereco { get; set; }
public string Bairro { get; set; }
public string Cidade { get; set; }
public string CEP { get; set; }
public string Email { get; set; }
public string Telefone { get; set; }
public string Status_Cliente { get; set; } = "Ativo";
public DateTime Data_Cadastro { get; set; } = DateTime.Today;
}
Como os nomes das propriedades são iguais aos utilizados na parte 1 da série, exceto pelo fato de aqui estarem em maiúsculo, a conversão dos dados no formato JSON se dará de forma automática, sem a necessidade de adaptação ou uso de objetos DTO (BOX 2).
DTO (Data Transfer Object) é um tipo objeto utilizado no tráfego de dados entre processos ou aplicações. São objetos POCO (Plain Old CLR Object) e possuem estrutura simples, de forma que seu envio e recebimento consuma o mínimo de recursos (principalmente de rede, quando trafegados entre web services) possível.
A segunda classe modelo, a classe Produto, pode ser vista na Listagem 2.
public class Produto
{
public int IDProduto { get; set; }
public string Descricao { get; set; }
public decimal Preco_Compra { get; set; }
public decimal Preco_Venda { get; set; }
public int Qtde_Estoque { get; set; }
public DateTime Data_Cadastro { get; set; } = DateTime.Today;
public string Status_Produto { get; set; } = "Ativo";
}
Nas Listagens 3 e 4 temos o código das classes PedidoCabecalho e PedidoItem, respectivamente.
public class PedidoCabecalho
{
public int IDPedido_Cabecalho { get; set; }
public int IDCliente { get; set; }
public decimal Valor_Total_Pedido { get; set; }
public int Qtde_Itens { get; set; }
public string Status_Pedido { get; set; } = "Pendente";
public DateTime Data_Pedido { get; set; } = DateTime.Today;
}
public class PedidoItem
{
public int IDPedido_Itens { get; set; }
public int IDPedido_cabecalho { get; set; }
public int IDProduto { get; set; }
public int Quantidade { get; set; }
public decimal Valor_Unitario { get; set; }
public decimal Sub_Total { get { return Quantidade * Valor_Unitario; }}
public string Status_Item { get; set; } = "Pendente";
}
Por fim, na Listagem 5 temos a classe ItemProduto, que servirá para simplificar a exibição dos itens do pedido durante a emissão. Essa classe conterá características tanto do Produto quanto do PedidoItem.
public class ItemProduto
{
public int IDProduto { get; set; }
public string Descricao { get; set; }
public int Quantidade { get; set; }
public decimal Subtotal { get; set; }
}
Proxy
Em uma aplicação no padrão MVVM que utilizasse, por exemplo, um banco de dados local, poderíamos utilizar o padrão Repository para centralizar as coleções de dados e essa classe poderia se comunicar diretamente com o banco de dados (usando Entity Framework Code First, por exemplo), ficando dentro da camada Model.
Porém, neste caso os dados são recebidos e enviados através de requisições a um serviço externo, e manter em uma única classe a responsabilidade de consumir todos os métodos da API geraria um código extenso e, no mínimo, um Bad Smell chamado God Class, onde uma classe possui muitas responsabilidades. Isso também iria de encontro ao Princípio da Responsabilidade Única (Single Responsibility Principle - o S dos padrões SOLID).
Para contornar essa situação, trabalharemos aqui com uma camada adicional chamada Proxy (BOX 3), que será responsável por realizar as chamadas ao serviço Web API e prover dados para as demais camadas em um formato conhecido, ou seja, utilizando as classes model.
O padrão de projeto estrutural Proxy define um objeto cujo objetivo é funcionar como uma interface entre dois componentes, visando simplificar a comunicação entre eles. Neste caso, o proxy está agindo como uma interface de acesso ao serviço HTTP.
Os controllers da Web API possuem basicamente métodos para realizar as operações de CRUD nas tabelas do banco de dados, então sabemos que precisaremos em nossa camada proxy consumir esses métodos de acordo com sua assinatura.
Sendo assim, criaremos uma interface que irá servir como modelo para as demais classes proxy. Essa interface, cujo código pode ser visualizado na Listagem 6, será do tipo genérica, para que assim possamos relacionar cada classe proxy com uma classe model.
public interface IServiceProxy<T> where T : class
{
List<T> ListarTodos();
T ObterPeloID(object id);
bool Adicionar(T objeto);
bool Alterar(T objeto);
bool Excluir(T objeto);
}
A intenção aqui é que cada um desses métodos realize uma chamada a um dos métodos do serviço, mantendo a seguinte relação: ListarTodos (GET), ObterPeloID (GET/{id}), Adicionar (POST), Alterar (PUT), Excluir (DELETE).
Para facilitar a comunicação com o serviço e o tráfego de dados no formato JSON, instalaremos uma extensão através do gerenciador de pacotes NuGet executando a seguinte linha no Package Manager Console:
Install-Package
Microsoft.AspNet.WebApi.Client
Esse pacote nos auxiliará na realização de requisições assíncronas no formato JSON, oferecendo uma sintaxe simplificada para isso.
Criemos então a classe concreta ClienteProxy, que implementará a interface IServiceProxy e será responsável por invocar os métodos do controller Clientes da API. O código desta classe encontra-se na Listagem 7.
public class ClienteProxy : IServiceProxy<Cliente>
{
private HttpClient client;
public ClienteProxy()
{
client = new HttpClient();
client.BaseAddress = new Uri(Windows.Storage.ApplicationData.Current.LocalSettings.Values["EnderecoWebAPI"].ToString());
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public bool Adicionar(Cliente objeto)
{
HttpResponseMessage resposta = client.PostAsJsonAsync("api/Clientes/Postcliente", objeto).Result;
return resposta.IsSuccessStatusCode;
}
public bool Alterar(Cliente objeto)
{
HttpResponseMessage resposta = client.PutAsJsonAsync($"api/Clientes/Putcliente/{objeto.IDCliente}", objeto).Result;
return resposta.IsSuccessStatusCode;
}
public bool Excluir(Cliente objeto)
{
HttpResponseMessage resposta = client.DeleteAsync($"api/Clientes/Deletecliente/{objeto.IDCliente}").Result;
return resposta.IsSuccessStatusCode;
}
public List<Cliente> ListarTodos()
{
HttpResponseMessage resposta = client.GetAsync("api/Clientes/Getcliente").Result;
return resposta.Content.ReadAsAsync<List<Cliente>>().Result;
}
public Cliente ObterPeloID(object id)
{
HttpResponseMessage resposta = client.GetAsync($"/api/Clientes/Getcliente/{id}").Result;
return resposta.Content.ReadAsAsync<Cliente>().Result;
}
}
Aqui estamos utilizando a classe HttpClient para realizar as requisições HTTP ao serviço a partir do endpoint que será definido nas configurações locais da aplicação.
Nesta classe também estamos utilizando um dos novos recursos do C# 6.0 chamado String Interpolation, que pode ser visto, por exemplo, na linha 21, onde inserimos uma propriedade de um objeto diretamente dentro de uma string sem precisar concatená-la.
Também é necessário referenciar os namespaces System.Net.Http, System.Net.Http.Headers e ForcaVendas.WindowsPhone.Models para que possamos acessar todas as classes utilizadas nessa listagem.
Como implementam a mesma interface, as classes ProdutoProxy e PedidoItemProxy possuem implementação extremamente semelhante à ClienteProxy, portanto serão omitidas deste artigo, estando disponíveis no código fonte.
Apenas a classe PedidoCabecalhoProxy merece destaque aqui, pois seu método Adicionar utiliza uma das informações retornadas pelo serviço para preencher o ID do cabeçalho do pedido que acabou de ser inserido. Como vemos na Listagem 8, após enviar o cabeçalho para o serviço, o atributo Location contido nos Headers da resposta é retornado com a seguinte estrutura: <URL base>/api/PedidoCabecalho/Postpedido_cabecalho/{id}.
Precisamos então pegar a quarta parte dessa URL, o ID do pedido inserido, e atribuir à propriedade IDPedido_Cabecalho do parâmetro objeto. Como se trata de uma classe, ou seja, um tipo referência, ao alterarmos o parâmetro, estamos alterando o objeto original que foi passado para o método.
public class PedidoCabecalhoProxy : IServiceProxy<PedidoCabecalho>
{
private HttpClient client;
public PedidoCabecalhoProxy()
{
client = new HttpClient();
client.BaseAddress = new Uri(Windows.Storage.ApplicationData.Current.LocalSettings.Values["EnderecoWebAPI"].ToString());
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
public bool Adicionar(PedidoCabecalho objeto)
{
HttpResponseMessage resposta = client.PostAsJsonAsync("api/PedidoCabecalho/Postpedido_cabecalho", objeto).Result;
if (resposta.IsSuccessStatusCode)
{
objeto.IDPedido_Cabecalho = Convert.ToInt32(resposta.Headers.Location.Segments[4]);
return true;
}
return false;
}
public bool Alterar(PedidoCabecalho objeto)
{
HttpResponseMessage resposta = client.PutAsJsonAsync($"api/PedidoCabecalho/Putpedido_cabecalho/{objeto.IDPedido_Cabecalho}", objeto).Result;
return resposta.IsSuccessStatusCode;
}
public bool Excluir(PedidoCabecalho objeto)
{
HttpResponseMessage resposta = client.DeleteAsync($"api/PedidoCabecalho/Deletepedido_cabecalho/{objeto.IDPedido_Cabecalho}").Result;
return resposta.IsSuccessStatusCode;
}
public List<PedidoCabecalho> ListarTodos()
{
HttpResponseMessage resposta = client.GetAsync("api/PedidoCabecalho/Getpedido_cabecalho").Result;
return resposta.Content.ReadAsAsync<List<PedidoCabecalho>>().Result;
}
public PedidoCabecalho ObterPeloID(object id)
{
HttpResponseMessage resposta = client.GetAsync($"/api/PedidoCabecalho/Getpedido_cabecalho/{id}").Result;
return resposta.Content.ReadAsAsync<PedidoCabecalho>().Result;
}
}
Para que as classes proxy possam acessar o endereço da API nas configurações locais da aplicação, devemos abrir o arquivo App.xaml.cs e no construtor da classe adicionar a seguinte linha, alterando apenas o endereço onde o serviço está sendo executado:
Windows.Storage.ApplicationData.Current.LocalSettings.Values["EnderecoWebAPI"] = "http://localhost:1622/";
ViewModels
Os ViewModels, aqui, terão a responsabilidade de receber os dados de um proxy e repassá-los às views, permitindo assim a visualização e interação por parte do usuário. ViewModels podem ter estrutura e funcionamento visando atender uma determinada situação, como o cadastro de clientes. Aqui começaremos criando o ClientesViewModel, que será utilizando tanto para listagem quanto para inserção, exclusão e alteração de registros. Seu código pode ser visto na Listagem 9.
public class ClientesViewModel
{
private ClienteProxy proxy;
public ObservableCollection<Cliente> Clientes { get; set; }
public Cliente ClienteSelecionado { get; set; }
public ClientesViewModel()
{
proxy = new ClienteProxy();
Clientes = new ObservableCollection<Cliente>(proxy.ListarTodos());
}
public bool AdicionarSelecionado()
{
return proxy.Adicionar(ClienteSelecionado);
}
public bool AlterarSelecionado()
{
return proxy.Alterar(ClienteSelecionado);
}
public bool ExcluirSelecionado()
{
if (proxy.Excluir(ClienteSelecionado))
{
Clientes.Remove(ClienteSelecionado);
return true;
}
return false;
}
}
A classe ObservableCollection representa o componente Observable da implementação do padrão Observer. Essa classe notifica automaticamente todos os seus observadores (neste caso, a interface gráfica) quando sofre alguma modificação, desta forma não precisamos atualizar a fonte de dados dos componentes quando um novo item for inserido, alterado ou excluído.
A propriedade ClienteSelecionado também será ligado a um componente da interface gráfica e será atualizado automaticamente quando o usuário selecionar um registro diferente. Ela é utilizada para adicionar, alterar e excluir um registro utilizando a classe ClienteProxy nos métodos AdicionarSelecionado, AlterarSelecionado e ExcluirSelecionado.
O ViewModel para o cadastro de produtos é semelhante, porém, como foi definido nas regras de negócio, ele não permitirá adicionar, alterar ou excluir produtos, contendo apenas uma lista de objetos a serem exibidos na interface gráfica, como vemos na Listagem 10.
public class ProdutosViewModel
{
private ProdutoProxy proxy;
public ObservableCollection<Produto> Produtos { get; set; }
public Produto ProdutoSelecionado { get; set; }
public ProdutosViewModel()
{
proxy = new ProdutoProxy();
Produtos = new ObservableCollection<Produto>(proxy.ListarTodos());
}
}
Assim como na classe ClientesViewModel, a lista de produtos é iniciada no construtor da classe a partir da lista que é recebida pelo método ListarTodos da classe ProdutoProxy.
O terceiro ViewModel, e mais complexo, será o NovoPedidoViewModel, que será responsável por controlar a emissão de um novo pedido e para isso precisará conter uma lista de clientes e produtos a serem exibidos, bem como que está selecionado. Também haverá um campo para quantidade do produto selecionado a ser inserido e os itens do pedido propriamente ditos, conforme vemos na Listagem 11.
public class NovoPedidoViewModel
{
public ObservableCollection<Cliente> Clientes { get; set; }
public ObservableCollection<Produto> Produtos { get; set; }
public ObservableCollection<ItemProduto> Itens { get; set; } = new ObservableCollection<ItemProduto>();
public Cliente ClienteSelecionado { get; set; }
public Produto ProdutoSelecionado { get; set; }
public ItemProduto ItemSelecionado { get; set; }
public int QuantidadeSelecionada { get; set; } = 1;
private ClienteProxy proxyCliente;
private ProdutoProxy proxyProduto;
private PedidoCabecalhoProxy proxyCabecalho;
private PedidoItemProxy proxyItens;
public NovoPedidoViewModel()
{
proxyCliente = new ClienteProxy();
proxyProduto = new ProdutoProxy();
proxyCabecalho = new PedidoCabecalhoProxy();
proxyItens = new PedidoItemProxy();
Clientes = new ObservableCollection<Cliente>(proxyCliente.ListarTodos());
Produtos = new ObservableCollection<Produto>(proxyProduto.ListarTodos());
}
public void AdicionarProdutoSelecionado()
{
ItemProduto item = new ItemProduto()
{
IDProduto = ProdutoSelecionado.IDProduto,
Descricao = ProdutoSelecionado.Descricao,
Quantidade = QuantidadeSelecionada,
Subtotal = ProdutoSelecionado.Preco_Venda * QuantidadeSelecionada
};
Itens.Add(item);
}
public void ExcluirItemSelecionado()
{
Itens.Remove(ItemSelecionado);
}
public bool EmitirPedido()
{
PedidoCabecalho cabecalho = new PedidoCabecalho()
{
IDCliente = ClienteSelecionado.IDCliente,
Qtde_Itens = Itens.Count,
Valor_Total_Pedido = Itens.Sum(i => i.Subtotal)
};
if (proxyCabecalho.Adicionar(cabecalho))
{
foreach (var item in Itens)
{
PedidoItem p = new PedidoItem()
{
IDPedido_cabecalho = cabecalho.IDPedido_Cabecalho,
IDProduto = item.IDProduto,
Quantidade = item.Quantidade,
Valor_Unitario = item.Subtotal / item.Quantidade
};
proxyItens.Adicionar(p);
}
return true;
}
return false;
}
}
Para emitir o pedido, utilizamos os campos que estão selecionados na tela e geramos um Pedido_Cabecalho, classe a ser utilizada na requisição HTTP ao serviço através da classe PedidoCabecalhoProxy.
Em seguida, criamos um PedidoItem para cada ItemProduto contido na lista Itens e enviamos para o serviço através da classe PedidoItemProxy, responsável por fazer as requisições ao controller PedidoItens da API.
Views
Para melhor organizar as views da aplicação, criaremos dentro da pasta Views as pastas Clientes, Produtos e Pedidos, separando assim os arquivos XAML de acordo com seu uso.
Antes de criar as views para cada funcionalidade, vamos trabalhar na MainPage.xaml, que dará acesso às demais páginas da aplicação a partir de botões simples. O código dessa página pode ser visto na Listagem 12, onde vemos o componente Grid central que já vem adicionado por padrão nas páginas.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Height" Value="80"/>
</Style>
</Grid.Resources>
<TextBlock FontSize="40" Text="Força de Vendas" Margin="10,0,10,0" Grid.Row="0"/>
<StackPanel Margin="10,0,10,0" Grid.Row="1">
<Button Name="btnClientes" Content="Clientes" />
<Button Name="btnProdutos" Content="Produtos"/>
<Button Name="btnPedido" Content="Novo Pedido"/>
</StackPanel>
</Grid>
Isso gerará o layout que vemos na Figura 3.
Em seguida, vamos criar a página ListarClientes.xaml dentro da pasta Views/Clientes. Essa página será composta por uma ListBox e alguns botões na barra inferior, e seu código está contido na Listagem 13.
<Page.BottomAppBar>
<CommandBar>
<AppBarButton Name="btnAdicionar" Icon="Add" Label="novo" Click="btnAdicionar_Click"/>
<AppBarButton Name="btnAlterar" Icon="Edit" Label="alterar" Click="btnAlterar_Click"/>
<AppBarButton Name="btnExcluir" Icon="Delete" Label="excluir" Click="btnExcluir_Click"/>
</CommandBar>
</Page.BottomAppBar>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock FontSize="40" Text="Clientes" Margin="10,0,10,0" Grid.Row="0"/>
<ScrollViewer Margin="10,0,10,0" Grid.Row="1">
<ListBox ItemsSource="{Binding Clientes}" SelectedItem="{Binding ClienteSelecionado, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Nome}" FontWeight="Bold"/>
<TextBlock Text="{Binding CPF_CNPJ}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
</Grid>
A implementação do evento Click dos botões, bem como o evento Loaded da página pode ser visto na Listagem 14.
private ClientesViewModel viewModel;
private void Page_Loaded(object sender, RoutedEventArgs e)
{
viewModel = new ClientesViewModel();
this.DataContext = viewModel;
}
private void btnAdicionar_Click(object sender, RoutedEventArgs e)
{
this.Frame.Navigate(typeof(AdicionarCliente));
}
private void btnAlterar_Click(object sender, RoutedEventArgs e)
{
this.Frame.Navigate(typeof(AlterarCliente), viewModel.ClienteSelecionado);
}
private async void btnExcluir_Click(object sender, RoutedEventArgs e)
{
var mensagem = new MessageDialog("Deseja realmente excluir esse cliente?");
UICommand comandoSim = new UICommand("Sim", (c) => {
if (viewModel.ExcluirSelecionado())
new MessageDialog("Cliente excluído.").ShowAsync();
});
UICommand comandoNao = new UICommand("Não");
mensagem.Commands.Add(comandoSim);
mensagem.Commands.Add(comandoNao);
await mensagem.ShowAsync();
}
Na linha 1 declaramos um objeto do tipo ClientesViewModel que será usado para preencher a ListBox na linha 6, onde o atribuímos à propriedade DataContext da página. Como foi definido através de data binding, a ListBox irá exibir a lista Clientes do viewmodel.
Ao clicar nos botões novo e alterar o usuário será levado a outra página (que veremos a seguir). Já no botão excluir, exibiremos uma mensagem (MessageDialog) solicitando a confirmação da exclusão e então utilizaremos o método ExcluirSelecionado do ViewModel para remover o registro.
Para poder testar a aplicação sem erros, precisamos criar as páginas AdicionarCliente.xaml e AlterarCliente.xaml.
A página AdicionarCliente.xaml irá conter vários campos para que o usuário insira os dados do novo cliente e apenas um botão para salvar o registro, conforme consta na Listagem 15. Ligamos os campos às propriedades das classes através de data binding, dessa vez utilizando o Mode=TwoWay que faz com que alterações na interface sejam refletidas nos objetos e vice-versa.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock FontSize="40" Text="Adicionar cliente" Margin="10,0,10,0" Grid.Row="0"/>
<ScrollViewer Margin="10,0,10,0" Grid.Row="1">
<StackPanel DataContext="{Binding ClienteSelecionado}">
<TextBlock Text="Descrição"/>
<TextBox Text="{Binding Descricao, Mode=TwoWay}"/>
<TextBlock Text="CPF/CNPJ"/>
<TextBox Text="{Binding Preco_Venda, Mode=TwoWay}"/>
<TextBlock Text="Endereco"/>
<TextBox Text="{Binding Endereco, Mode=TwoWay}"/>
<TextBlock Text="Bairro"/>
<TextBox Text="{Binding Bairro, Mode=TwoWay}"/>
<TextBlock Text="Cidade"/>
<TextBox Text="{Binding Cidade, Mode=TwoWay}"/>
<TextBlock Text="CEP"/>
<TextBox Text="{Binding CEP, Mode=TwoWay}"/>
<TextBlock Text="Email"/>
<TextBox Text="{Binding Email, Mode=TwoWay}"/>
<TextBlock Text="Telefone"/>
<TextBox Text="{Binding Telefone, Mode=TwoWay}"/>
<TextBlock Text="Status"/>
<TextBox Text="{Binding Status, Mode=TwoWay}"/>
</StackPanel>
</ScrollViewer>
</Grid>
Como utilizaremos a mesma classe ClientesViewModel, ligamos o StackPanel à propriedade ClienteSelecionado para que os controles em seu interior pudessem acessar as propriedades da classe Cliente.
Assim como na página ListarClientes, precisamos declarar um objeto privado do tipo ClientesViewModel e instanciá-lo no evento Loaded da página. Além disso, também precisamos instanciar a propriedade ClienteSelecionado, para que a digitação nos TextBoxes reflita diretamente em suas propriedades. Na Listagem 16 está o código onde fazemos essa implementação.
private ClientesViewModel viewModel;
private async void btnSalvar_Click(object sender, RoutedEventArgs e)
{
if (viewModel.AdicionarSelecionado())
{
await new MessageDialog("Cliente adicionado com sucesso.").ShowAsync();
this.Frame.Navigate(typeof(ListarClientes));
}
else
{
await new MessageDialog("O cliente não foi adicionado.").ShowAsync();
}
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
viewModel = new ClientesViewModel();
viewModel.ClienteSelecionado = new Models.Cliente();
this.DataContext = viewModel;
}
Observe que por estarmos utilizando o recurso de data binding da linguagem XAML, não precisamos preencher o objeto com o conteúdo dos TextBoxes. Outro ponto importante é que o método btnSalvar_Click precisou ser marcado como assíncrono, para que pudéssemos utilizar o operador await na chamada ao método ShowAsync da classe MessageDialog.
A página AlterarCliente.xaml é basicamente igual à AdicionarCliente.xaml, alterando apenas seu código C#, que podemos ver na Listagem 17.
private ClientesViewModel viewModel;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
viewModel = new ClientesViewModel();
viewModel.ClienteSelecionado = e.Parameter as Cliente;
this.DataContext = viewModel;
}
private async void btnSalvar_Click(object sender, RoutedEventArgs e)
{
if (viewModel.AlterarSelecionado())
{
await new MessageDialog("Cliente alterado com sucesso.").ShowAsync();
this.Frame.Navigate(typeof(ListarClientes));
}
else
{
await new MessageDialog("O cliente não foi alterado.").ShowAsync();
}
}
Desta vez, ao invés de instanciarmos a propriedade ClienteSelecionado, atribuímos a ela o valor recebido por parâmetro na navegação vindo da página ListarClientes, que é o objeto selecionado na ListBox.
Agora devemos ir à MainPage e no evento Click do btnClientes navegar para a página ListarClientes, da seguinte forma:
this.Frame.Navigate(typeof(Views.Clientes.ListarClientes));
Executando a aplicação agora, podemos clicar no botão Clientes na MainPage e de lá navegaremos para as páginas de listagem, adição, edição e exclusão de clientes, como vemos na Figura 4.
A próxima página a ser criada é a ListarProdutos.xaml, dentro da pasta Views/Produtos. A estrutura dessa página é semelhante à de listar clientes, apenas não tem os botões de ação na parte inferior. Seu código pode ser visto na Listagem 18.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock FontSize="40" Text="Produtos" Margin="10,0,10,0" Grid.Row="0"/>
<ScrollViewer Margin="10,0,10,0" Grid.Row="1">
<ListBox ItemsSource="{Binding Produtos}" SelectedItem="{Binding ProdutoSelecionado, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Descricao}" FontWeight="Bold"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Preço: " Grid.Column="0"/>
<TextBlock Text="{Binding Preco_Venda}" Grid.Column="1"/>
<TextBlock Text="Estoque: " Grid.Column="2"/>
<TextBlock Text="{Binding Qtde_Estoque}" Grid.Column="3"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
</Grid>
Nesta página utilizaremos o viewmodel ProdutosViewModel, por isso ligamos a propriedade ItemsSource da ListBox à lista Produtos e o SelectedItem ao ProdutoSelecionado. Basta então instanciar um ProdutosViewModel e atribuí-lo ao DataContext da página no evento Loaded, como é feito na Listagem 19.
private ProdutosViewModel viewModel;
private void Page_Loaded(object sender, RoutedEventArgs e)
{
viewModel = new ProdutosViewModel();
this.DataContext = viewModel;
}
Voltando à MainPage, devemos navegar para a página ListarProdutos no evento Click do btnProdutos, como fizemos para os clientes:
this.Frame.Navigate(typeof(Views.Produtos.ListarProdutos));
Com isso, podemos executar a aplicação e clicar no botão Produtos, visualizando assim a listagem de produtos cadastrados, como mostra a Figura 5.
Comas páginas de clientes e produtos prontas, podemos então desenvolver a tela de emissão de pedidos. Dentro da pasta Views/Pedidos, vamos adicionar a página NovoPedido.xaml, que conterá os campos para selecionar o cliente e adicionar os produtos do pedido, como vemos no código da Listagem 20.
<Page.BottomAppBar>
<CommandBar>
<AppBarButton Label="emitir" Name="btnEmitir" Icon="Accept" Click="btnEmitir_Click"/>
<AppBarButton Label="excluir item" Name="btnExcluirItem" Icon="Delete" Click="btnExcluirItem_Click"/>
</CommandBar>
</Page.BottomAppBar>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock FontSize="40" Text="Novo pedido" Margin="10,0,10,0" Grid.Row="0"/>
<StackPanel Grid.Row="1" Margin="10,0,10,0">
<ComboBox ItemsSource="{Binding Clientes}" SelectedItem="{Binding ClienteSelecionado, Mode=TwoWay}" Name="comboCliente" PlaceholderText="selecione o cliente" Header="cliente">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Nome}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox ItemsSource="{Binding Produtos}" SelectedItem="{Binding ProdutoSelecionado, Mode=TwoWay}" Name="comboProdutos" Header="Produtos" PlaceholderText="selecione um produto">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Descricao}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="120"/>
</Grid.ColumnDefinitions>
<TextBox HorizontalAlignment="Stretch" Grid.Column="0" Text="{Binding QuantidadeSelecionada, Mode=TwoWay}"/>
<Button Name="btnInserir" Content="Inserir" Grid.Column="1" HorizontalAlignment="Stretch" Click="btnInserir_Click"/>
</Grid>
</StackPanel>
<ScrollViewer Grid.Row="2" Margin="10,0,10,0">
<ListBox ItemsSource="{Binding Itens}" SelectedItem="{Binding ItemSelecionado, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Descricao}" FontWeight="Bold"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Quantidade" Grid.Column="0"/>
<TextBlock Text="{Binding Quantidade}" Grid.Column="1"/>
<TextBlock Text="Subotal" Grid.Column="2"/>
<TextBlock Text="{Binding Subtotal}" Grid.Column="3"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
</Grid>
Nesta view utilizamos o controle ComboBox para listar os clientes e produtos cadastrados e permitir que o cliente selecione apenas um por vez. O botão Inserir utilizará a quantidade digitada no TextBox e o produto selecionado para gerar um objeto do tipo ItemProduto e adicioná-lo à lista Itens do viewmodel usando o método AdicionarProdutoSelecionado.
Na CommandBar temos os botões “emitir”, que irá chamar o método EmitirPedido do viewmodel, e “excluir item”, que irá remover da lista o item selecionado na ListBox.
Para testar essa página, voltamos à MainPage e no evento Click do botão btnPedido navegamos para a página NovoPedido.xaml, da seguinte forma:
this.Frame.Navigate(typeof(Views.Pedidos.NovoPedido));
Com isso, podemos executar a aplicação e emitir um novo pedido, como ilustra a Figura 6.
A partir daí podemos visualizar os pedidos emitidos através do módulo gerencial desenvolvido como uma web app com AngularJS. De acordo com a necessidade, podemos adicionar novas funcionalidades, além de implementar regras de validação mais rigorosas e trabalhar melhor o layout da aplicação.
Tomando como base a arquitetura de aplicação e recursos que foram utilizados nesse artigo, o projeto poderia ser facilmente migrado ou adaptado para uma aplicação universal do Windows 8.1 ou 10, bem como para WPF, tipos de projetos que também utilizam a linguagem XAML em sua camada de interface visual e tiram proveito direto do padrão MVVM.