Escolha uma Página
Como hospedar sua aplicação Asp.Net Core MVC (inclusive 2.2) fora do Azure

Como hospedar sua aplicação Asp.Net Core MVC (inclusive 2.2) fora do Azure

Por menos de U$3/mês você pode ter sua aplicação ASP.NET Core MVC 2.2 rodando num provedor realmente especializado em ASP.NET, com direito a e-mail, banco de dados MS SQL e um infinidade de recursos.

Usando Visual Studio 2017 versão comunitária (grátis) você consegue instalar sua aplicação ASP.NET Core 2.2 MVC num provedor especializado e com alta velocidade – e com um custo muito baixo. Esqueça Godaddy, LOCAWEB, e mesmo Azure (que acaba custando uma tinta quanto mais você usa).

Depois de muito pesquisar e apanhar, acabei descobrindo o provedor SmarterASP.NET..Em menos de uma hora, estava com minha aplicação Core 2.0 rodando. Aqui vai um passo a passo com dicas importantes. Detalhe importante: se você duvidar, pode ter 60 dias de uso grátis!!! Ou seja, você pode comprovar tudo o que estou falando aqui sem gastar um único centavo. Se escolher um plano pago você pode usar qualquer cartão de crédito (ou pagar com PAY-PAL). Se não gostar eles devolvem seu dinheiro.

Passo 1 – Incrição

Vá em SmarterASP.NET. NÃO se preocupe, este é o único formulário em inglês. Todo seu painel pode ser usado em português.

Escolha o plano 60 dias grátis, se não quiser pagar nada agora, Em segundos você recebe um e-mail de confirmação, podendo já acessar e publicar sua aplicação CORE MVC.

A primeira grata surpresa: você ganha um domínio prontinho, seu, para testar sua aplicação na WEB (depois você aponta um domínio seu, não se preocupe agora).

Passo 2 – Acesso

Clique no link recebido por e-mail enviado pela SmarterASP.NET. . Você já pode acessar seu painel. Escilha Português se não quiser inglês. Ao acessar, se o sistema perguntar onde quer seu servidor (USA ou Europa), defina onde quer. Eu defini USA e roda rapisíssimo, fiquei impressionado! Você vai ter de cara um domínio tipo seunomedeusuario-001-site1.htempurl.com


Passo 3 – Pegue os dados de Deploy para seu Visual Studio

Você não vai precisar usar FTP. Vai subir sua aplicação via Visual Studio e comprovar que é rapidíssimo comparada a qualquer subida via FTP. Clique em Sites e depois em Mostrar Informações Web Deploy. Não se preocupe em copiar nada.dados para visual studio

Clicando e Mostrar informações abre um painel abaixo:

Deixe VS Estado como ON (verde). Clique no botão “Se quiser publicar configuração”. Os lugares que risquei em amarelo vao ter seu nome de usuário.

Salve em seu disco local o arquivo Site1. Este é um arquivo com extensão .PubblishSettings, próprio para o Visual Studio. Seu conteúdo é este:

Passo 4 – Abra sua aplicação no Visual Studio e instale o arquivo para PublishSettings

Clique no Solution Explorer do Visual Studio, no lado direito, no nome do seu projeto (com o botão direito). Escolha a opção Publishing.

PUBLISH


Clique em Create new profile:

create new profile


Clique em Import Profile e dê OK

import profile


Escolha o Arquivo Site1 que foi gravado no seu disco local:

arquivo site1

Clique no botão Publish. O Visual Studio iniciará a subida da sua aplicação, que você notará que é bem rápida.

publish

Uma dica importante: na primeira vez, ANTES de clicar no botão PUBLISH NO VISUAL STUDIO, CLIQUE EM settings, que fica no lado direito do painel de publicação (as vez fica meio escondido na tela). Clique em VALIDAR CONEXÃO (validate connection). Se o sistema reclamar alguma coisa de certificado, diz que sim, que aceita o certificado e pronto. Clique em SALVAR (save) e agora sim, pode publicar.

As vezes o Visual Studio “esquece” que você já fez isso e começa a dar erro na publicação do site. É só repetir a operação acima e tudo volta ao normal. A partir da segunda vez que você publica a atualização é rapidíssima! É a vantagem desse provedor permitir WEB DEPLOY, que só atualiza os arquivos que efetivamente mudaram… Se for fazer pelo FTP do Visual Studio, que é burrinho, pode sair e voltar um bom tempo depois (pois ele sobe todos os arquivos de novo, sempre). É melhor – neste caso, publicar num diretório e subir com o Filezilla, parametrizando-o para só enviar arquivos com novas datas. Mas certamente você vai esquecer que existe FTP usando o WEBDEPLOY…

Outras vantagens:

Você pode criar um número ilimitado de bancos de dados, inclusive SQL. Não paga nada a mais por isso. Para as minhas aplicações isso é muito bom. O desempenho dos servidores é ótimo.

Se tiver algum problema:

  • O provedor tem uma base de conhecimento com uma série de bons artigos explicativos, agrupados por tópicos. Tem tudo ali que em geral os clientes podem ter dúvidas;
  • Escreva para o suporte. Testei e me responderam rapidinho (nem acreditei, estou acostumado com o padrão 24 horas da Locaweb). O suporte é mesmo 24 x 7! Basta abrir um tíquete de suporte na Central de Ajuda.

Obrigado pela sua leitura. Continue visitando este blog e compartilhe artigos em sua rede de relacionamento. Por favor, se quiser, registre sugestões e comentários ao final da página.

Junte-se à nossa rede de Empreendedores Builderall.

turbine seus negócios online com mais de 20 aplicativos Builderall

Integrando o MailingBoss a uma aplicação ASP.NET Core MVC com o Visual Studio 2017

Integrando o MailingBoss a uma aplicação ASP.NET Core MVC com o Visual Studio 2017

Neste artigo veremos como é possível cadastrar um usuário no seu database e, ao mesmo tempo, inserir esse usuário numa lista do MailingBoss, onde é possível automatizar uma sequência de ações de marketing via E-mails. Ou seja, você põe um “robô” digital para interagir com seus leads até conseguir efetuar uma venda… ou o que você imaginar.

O problema

Usando o MailingBoss, que é a ferramenta de e-mail marketing da BuilderAll, é possível trabalhar com Funis de Venda, capturando Leads para uma determinada lista. O fantástico dessa ferramenta é que ela permite montar WORKFLOWS com ações a serem tomadas DEPOIS que o lead se inscreve na lista. Tipicamente essas ações são envios de novos e-mails, contendo conteúdos diferentes e levando o lead a tomar uma decisão de compra. Esses e-mais são programáveis no tempo. E mais: se a pessoa clicar no link de um e-mail, o WORKFLOW permite transferir o lead de uma lista original – onde o e-mail entrou pela primeira vez – para qualquer outra lista (retirando-o, se você quiser, da lista original).

Essencialmente, toda lista é associada a um único formulário que tem em geral poucos campos. O mínimo que pode ter é o campo de e-mail, que é obrigatório. O típico pede um nome e o e-mail. O problema: assim que a pessoa submete o formulário, clicando no botão de submit, é muito complicado passar esses dados para uma outra aplicação e – até o momento em que escrevo este post – o jeito que eles recomendam fazer isso não funcionou e é ainda impraticável passar dados “hidden”, embora isso seja teoricamente possível.  

A solução

Bem, se Maomé não vai à montanha, a montanha vai a Maomé. O jeito que encontrei é cadastrar o lead na minha própria aplicação CORE MVC, num formulário simples que simplesmente injeta o LEAD no database da minha aplicação. Ao completar essa operação, eu emulo um submit para o MailingBoss, como se os dados tivessem saído de um formulário deles – e não da minha aplicação. Assim que o MailingBoss “engole” os dados, ele mesmo dispara e-mail pedindo confirmação de e-mail (se a lista é double opt in) ou um e-mail de confirmação do cadastramento.

Funciona. Se você quiser fazer um teste, pode se inscrever para ter 7 dias de acesso gratuíto ao MailingBoss e mais 17 ferramentas de marketing digital – além de diversos treinamentos sobre tudo quanto é assunto para fazer marketing na WEB.

 

A aplicação em ASP.NET CORE MVC

A ideia é ter uma aplicação muito simples que permitirá fazer um cadastro de pessoas interessadas em fazer um teste, na WEB, sem custos, do seu grau de empreendedorismo.

Assim que a pessoa se cadastra, a aplicação manda um e-mail para o e-mail cadastrado, solicitando a CONFIRMAÇÃO (da veracidade) do mesmo. PAra não complicar a programação do aplicativo, vamos deixar por conta do MailingBoss o envio do e-mail pedindo a Confirmação.

Assim que a pessoa cadastrada clica no link desse e-mail de confirmação, ocorrem duas coisas:

  • Ela é remetida para uma página tipo parabéns, está tudo certo, ‘clique no link abaixo para fazer o teste‘;
  • O sistema muda o status do cadastrado para confirmado;
  • O MailingBoss envia um e-mail de agradecimento.

Modelo de Dados

Crie uma classe de dados no diretório model, tendo como chave o e-mail (para evitar e-mails duplicados).

MODEL->Empreendedor.cs

Gerando a tabela no banco de dados – Contexto

Uma vez que seu modelo já está gravado com Empreendedor.cs, você precisa precisa acrescentar essa tabela na lista de tabelas – que no meu caso está no arquivo ContextoPrimario.cs, dentro da pasta models. Se você não tem ainda na aplicação nenhuma especificação de contexto, crie uma ContextoPrimario.cs na pasta de models e insira o seguinte conteúdo

Se você já tem seu arquivo de Contexto, por ter criado o banco de dados anteriormente (ou por estar aproveitando uma solução de teste criada anteriormente, e somente quer ter a tabela do modelo empreendedor lá dentro), simplesmente acrescente à relação de arquivos já existentes a seguinte linha (dentro de public partial class ContextoPrimario : DbContext:):

public virtual DbSet<Empreendedor> Empreendedores { get; set; } // banco de Empreendedores

Feito isso, vá no menu superior e rode BUILD->BUILD SOLUTION. Tem que dar zero erros, se não der acerte o que estiver errado, até rodar e dar zero erros.

Feito isso, precisamos agora gerar o arquivo que vai de fato implementar a tabela de EMpreendedores na sua aplicação. Na janela do Package Manager Console, embaixo, temos de adicionar essa primeira migração. Digite, trocando [nomedamigracao] por qualquer nome, como PrimeiraMigracao:

//PM> Add-Migration [nomedamigracao] -Context ContextoPrimario:

Add-Migration [nomedamigracao] -Context ContextoPrimario 

Se rodar certinho, ele vai gerar um arquivo com um númerogrande_nomedasuamigracao. No meu caso, 20190131213948_Empreendedor3

Agora temos de rodar a Migração, gerando a tabela de Empreendedores no seu banco de dados.Novamente na janela de Package Management Console (PM, embaixo), rode o seguinte comando (troque ContextoPrimario pelo nome do seu contexto, se tiver um outro):

update-database -context ContextoPrimario

Entre no seu gerenciador de banco de dados, você vai ver que a tabela está lá. No meu caso, como uso SQL, entro no MIcrosoft SQL Server Management Studio (MSSMS). Coloco a tabela no modo de edição e vejo o seguinte:

tabela empreendedores

tabela empreendedores

Nada é perfeito, suspirou a raposa. Na migração a tabela foi criada mas, a despeito de ter definido no modelo valores default para idConsultoria (4), flagEmailConfirmado (0) e quantidadeDeAvaliadores (0), estes valores não estão no Binding da tabela, quando todos os campos estão definidos como “não permitindo nulos”. Isso significa que, se deixar assim, vou ter de especificar todos esses valores no meu form, como hidden. Até aí tudo bem. Ocorre que algumas vezes uso procedures e gosto de ter a certeza que se eu não enviar algum desses campos, eles vão assumir valores padrão.

Assim, ajusto o o DEFAULT VALUE OR BINDING para os valores padrão que quero e salvo a tabela (que ainda está vazia).

Gerando o Controller as as Views, com Scaffolding

Agora precisamos criar o controlador (controller) e os formulários (views). Clicks sobre o nome do seu projeto e com o botão da direita escolha ADD-> NEW SCAFFOLDED ITEM. Escolha o template MVC COntroller with Views, using Entity Framework. Click em ADD.

Em Model Class, escolha Empreendedor. Em Data Context Class, escolha  ContextoPrimario (ou o nome do seu contexto). Deixe clicado: Generate Views, Reference Script Libraries e Use a lay-out page. Em Controller name, mude o nome de EmpreendedorsController para EmpreendedoresController. Click em ADD.

Legal. Vamos fazer um check básico. No menu superior clique em IIS EXpress. Sua aplicação vai ter um endereço básico de entrada, definido em startup;cs. No meu caso é http://localhost:50628/.

No NAVEGADOR, altere para o seu endereço localhost + Empreendedores/Create.

No meu caso fica assim: http://localhost:50628/Empreendedores/Create

Que chato, Fernandinho. O formulário fica pedindo coisas que não deveriam e.se tento gravar, dá um monte de erros, E, pior que tudo, não tem o e-mail que considero chave:

 

 

 

 

É necessário que os campos idConsultoria, quantidadeDeAvaliadores e flagEmailConfirmado fiquem em hidden e com valores pré determinados. Além disso:

  • vou mudar a cor do botão para vermelho usando “btn-danger” no lugar de “btn-default”;
  • tirar o link “Back to List”;
  • No títutlo, colocar um texto promocional com a classe “jumbotron” do bootstrap (que é usado na minha aplicação);
  • acrescentar o campo de e-mail que, por ser chave, o CORE assumiu que não precisaria ter porque seria gerado automaticamente – o que não é o nosso caso.

 

Rodando a aplicação no localhost, nossa telinha de entrada fica assim:

Fazendo um cadastramento o sistema manda para a página Index ferada pelo Core MVC:

tela index Empreendedor

Ou conferindo na base de dadosse está tudo OK:

 

Agora vamos criar um espelho dessa tabela no MailingBoss. Se você não tem uma conta na BuilderAll, crie uma conta de 7 dias grátis.

Entre no MailingBoss Autoresponder->Acessar MailingBoss e preencha os dados em CRIAR NOVA LISTA DE CONTATOS. Dê o nome à lista de EMPREENDEDOR DIGITAL. No topo, clique em LISTAS e clique na engrenagem da Lista de Empreendedor Digital Abra o seletor LINKS RÁPIDOS e escolha a opção VER CAMPOS CUSTOMIZADOS.

Aqui vamos inserir os campos que nos interessam, todos como texto, além do e-mail: senha, nome, idconsultoria e telefone (que chamei de whatsap):

campos mailingboss

Não esqueça de salvar. Depois selecione em LINKS RÁPIDOS -> formulários de interação. Copie todo contéudo numa janela de texto, para podermos retirar dela as informações que nos interessam para o controlador do core mvc,

 

Podemos ver que a lista esta vazia:

Precisamos alterar o controlador para além de submeter ao database, também “”emular” o submit para o MailingBoss.

Vejamos o código original do POST do CREATE do CORE MVC:

Acrescente ao controlador:

using System.Net.Http;
using System.Net.Http.Headers;

e altere seu código post conforme abaixo. Não se esqueça de incluir a rotina privada para inserir os pares de valores do form e de trocar – no post – o código para o código de sua lista que você guardou no arquivo texto (no meu caso, o código zw93898d6db44 (que é o “cpf” da lista).

Testando a aplicação

No formulário original aparecia o campo MBOSSLASTCLICKS, que não documentado em lugar algum. Simplesmente pedi sua inserção com valor 1:

new KeyValuePair<string, string>(“MBOSSLASTCLICKS”,”1″)

cadastro 2

Solicitei o cadastramento com email alcides2@mailinator.com.

O email entrou:

Listando os contatos do MailingBoss, cuja lista estava vazia:

cadastro não confirmado 1

Observe que ele entrou como não confirmado, o que significa que a aplicação funcionou direitinho, emulando o post para o MailingBoss. Vendo os detalhes do contato:

cadastro não confirmado 2

Olhando no Mailinator. vemos que foi remetido um e-mail de confirmação:

cadastro não confirmado 2

Abrindo e e-mail e clicando no link de confirmação:

cadastro não confirmado 2

A confirmação leva para uma tela de confirmação no MailingBoss. Não é o que queremos, mas vamos refinar isso mais adiante:

tela de confirmação mailingboss

Voltando aos contatos do MailingBoss, vemos que agora o e-mail está com status confirmado.

cadastro mailingboss confirmado

No MailingBoss você pode ajeitar o e-mail de confirmação de inscrição, colocando sua arca e acertado oo texto. No meu caso:

email de confirmação da inscrição

Se o usuário clicar no link, vai cair na página de inscrição confirmada do MailingBoss. Como eu quero que ele caiana minha aplicação, tenho de configurar isso clicando no AVANÇADO.

Página de inscrição confirmada

Aí eu defino para onde quero remeter o usuário na minha aplicação e o tempo que isso demora. Deixei com zero segundos.

avançado da página de inscrição

Não esqueça de clicar em Salvar e próximo. Volto para a aplicação e registro um novo e-mail (alcides3@mailinator.com), vou nos e-mails recebidos (mailinator.com) e clico no link de confirmação. Imediatamente vai para a MINHA tela de confirmação:

minha tela de confirmação

Agora é embelezar a tela e mudar o FlagEmailConfirmado na minha aplicação, alterando o valor de 0 para 1. Outra coisa a fazer, uma vez que sei que o e-mail é quente, é inserir o usuário na aplicação de testes, para que ele possa fazer o teste prometido usando os mesmos dados com que se cadastrou na minha lista,

Nova tela de Detalhes

nova tela de detalhes

Observe que já passo os parâmetros no link para evitar que o usuário tenha de redigitar o que acabou de cadastrar. O que é bem chato especialmente em celular,

 

Acertando o Controlador

Assim que o usuário vê a tela de detalhes o controlador atualiza o flag de confirmação e chama uma procedure para inserir a pessoa confirmada no sistema de Avaliação e cria uma autoavaliação para ela preencher.

Esta é uma ISCA DIGITAL gratuíta e dá para o interessado, sem custos, sua autoavaliação como empreendedor. Ela poderá ver os resultados em tela, na forma gráfica e em tabelas.

Se ela gostar e quiser que outras pessoas a avaliem (amigos, parentes, pessoal de networking, etc), ela poderá comprar avaliações adicionais por um preço módico. Este seria um UP SELL . Se o resultado dela for muito bom, já na autoavaliação, no MailingBoss, vou colocá-la num FUNIL para ela juntar-se à CAMPANHA DE UM MILHÃO DE EMPREENDEDORES 

 

Ajustes Finais

Quando o usuário se cadastra, o sistema está ainda mandando o usuário para a página Index, que dá a relação de todos os cadastrado Não é isso que queremos: precisamos remetê-lo a uma página dizendo “Que legal, estamos quase lá, Por favor confirme sua inscrição clicando no link recebido por e-mail (veja se não caiu no SPAM”. Assim, o controlador no final do POST do create tem de redirecionar não para Index, e sim para uma pagina diferente. Vamos nos basear na própria página de details.cshtml, criando uma chamada details_confirma_email.cshtml.

Logo após cadastrar um novo e-mail (alcides6@mailinator.com) , aparece a tela do “estamos quase lá”.

TELA ESTAMOS QUASE LÁ

O e-mail foi enviado para o Mailinator.comO EMAIL FOI ENVIADO PARA O MAILINATOR.COM

Depois de clicar no link de confirmação do E-mail, a tela para pré-acessar o teste.

tela tudo pronto

Clicando no botão “Vai para o teste”, a tela já é pré-preenchida

tela tudo pronto

Clicando em entrar a pessoa já pode responder o questionário (no caso sobre seu grau de empreendedorismo).

tela interna do sistema

Todos e-mails ficaram registrados no MailingBoss e com status confirmados.

tela com relação de emails no mailingboss

Obrigado pela sua leitura. Continue visitando este blog e compartilhe artigos em sua rede de relacionamento. Por favor, se quiser, registre sugestões e comentários ao final da página.

Junte-se à nossa rede de Empreendedores Builderall.

turbine seus negócios online com mais de 20 aplicativos Builderall

Listas DropDown em Cascata (Cascading) usando o Select Tag Helper no CORE MVC

Listas DropDown em Cascata (Cascading) usando o Select Tag Helper no CORE MVC

Neste artigo damos continuidade ao artigo de listas dropdown usando o tag helper SELECT. Fazer listas dropdown em cascata exige o uso de Jquery e chamadas Ajax.  Este artigo mostra como fazer isso, desde o setup do sistema até as rotinas do core MVC integradas ao javascript. Não é muito elementar. Mas funciona. Aqui vai o caminho das pedras.

Fazendo o setup do seu sistema CORE MVC no arquivo Startup.cs

Há muitos artigos na Internet que falam em Cascading (dropdown em cascata, um dependente do outro) – mas a maioria mostra o milagre mas não mostra o santo. Ao fazer uma chamada AJAX para o servidor, por questões de segurança, os navegadores modernos não permitem. Principalmente se a camada está em domínio diferente do domínio da sua aplicação. No meu caso, eu não conseguia acesso HTTP para a leitura de dados mesmo dentro do meu próprio ambiente de desenvolvimento. Não conseguia nem no LOCALHOST, resumindo.

Estudando os erros, vi que precisava autorizar a aplicação CORE MVC a ler o que eu queria, criando uma política de acesso para capturar dados de uma página fora da página que você está. Dei o nome de “MinhaPolitica” a essa autorização.

Então, minha primeira recomendação é preparar os serviços do Startup.cs para lerem o domínio – ou os domínios – que você quiser. Na parte de Configuração dos Serviços (ConfigureServices) inclua:

Configurando o serviço globalmente

Depois disso é preciso configurar o serviço. :

Configurando Ações (Actions) dos Controladores (Controllers)

Se quiser garantir o acesso no seu próprio controlador (Control), coloque ANTES da ação do seu Controle (controller) o EnableCors :

Configurações Diferentes para ambiente de Desenvolvimento e Produção

Pronto. Temos certeza agora que as chamadas GET, em Ajax, vão chegar. Lendo o artigo em referência, você verá que pode definir diferentes políticas, uma para ambiente de desenvolvimento, outra par ambiente de produção e, ao invés de liberar geral como fiz aqui, fazer liberações para localhost, seu domínio ou outros domínios, tendo mais de uma política. Exemplo:

Princípio Básico dos Selects em Cascata

Selects em cascata funcionam do seguinte modo. Há um select MESTRE e um select ESCRAVO. Quando um item é selecionado no Select MESTRE, todos os itens do Select ESCRAVO são filtrados por uma variável (tipo ID) da opção selecionada. Itens sem essa ID são desprezados e somente são mostrados no Select ESCRAVO os itens que apresentam o mesmo valor da ID selecionada.

Ao fazermos uma seleção no MESTRE, essa escolha pode ser monitorada pelo comando Jquerychange()“:

A rotina em Jquery para recarregar o Select ESCRAVO

Quando o  Jquerychange()” percebe uma mudança no select MESTRE (no exemplo pelo select com id=”SelectConsultoria”, a sequência de ações é a seguinte:

  1. Limpa o select escravo, cuja Id é “SelectCompetencia” (no modelo de dados, cada Consultoria opera um conjunto de Competências), com a função jquery empty(). Não use remove(), que apaga o select inteiro e não s opções…;
  2. Acrescenta uma única linha com a função append(), tipo “Selecione uma Competência”;
  3. Força um refresh (algumas versões de navegador ficam mortinhas). Você faz o append() e a tela não muda. Esse aqui é um truque, forçando o DOM a atualizar o elemento com id “SelectCompetencia”. Usa uma função local chamada “redraw”;
  4. Monta a URL usando @Context.Request.Host do CORE MVC. Muita gente usa @Url, comigo não funcionou. A Url já passa o parâmetro que foi escolhido no Select Mestre. O pulo do gato, aqui, é que esse endereço tem o CONTROLADOR (controller)e a AÇÃO (Action) que tem de ser chamada para devolver, em formato Json, os dados filtrados. Sim, ela vai buscar no controller do CORE MVC ;
  5. Faz a chamada Ajax com ‘GET’, para buscar os dados.;
  6. Com o sucesso da conexão, coloca os dados em ‘data’ e manda processar numa função à parte chamada callbackFunction;
  7. A função callbackFunction popula o Select ESCRAVO novamente, O pulo do gato, aqui, é que ela no final remove uma linha que ficaria em branco.
  8. Essas rotinas ficam no miolo do @section Scripts { }, no final da sua página .CSHTML. Não esqueça de colocar <script> e </script>, ficando @section Scripts { <script> //todas as suas rotinas </script> }

A Ação (Action) no Controlador (Controller)

Montei uma ação específica para o “GET” do Ajax, via Jquery. Antes da ação, defini que para um GET, com [HttpGet]. E também assinalei a política local [EnableCors)”MinhaPolitica”)], já mencionada.

Aqui é montada uma lista simples. Filtrada pela Id do Select mestre, passado via URL como parâmetro. Observe que todos os itens da lista são acrescentados com Selected = false, pois  o Select ESCRAVO ainda não tem nada selecionado (e foi o próprio Javascript que criou o primeiro elemento com “Selecione uma Competência” .

Se isso não tivesse sido feito lá, poderíamos fazer aqui logo depois de definir a nova lista, fazendo um Lista.Add de um único elemento ANTES de entrar no loop foreach.

Observe que o retorno é em formato Json. É isso. Tem muita coisa antiga e errada na internet. Não precisa acrescentar parâmetro nenhum. Funciona que é uma chupeta.

 

 

A View com os selects usando o Select Tag Helper

A view que foi montada tem 3 blocos:

  • Bloco 1: os selects MESTRE e ESCRAVO;
  • Bloco 2: uma seção informativa do resultado da seleção acima se o botão de filtrar é pressionado;
  • Bloco 3: um grid com o resultado da seleção.

DICA:

Habilitando Tag Helpers de forma genérica

Para que tag helpers funcionem em qualquer arquivo de visualização, a opção é habilitar no arquivo auxiliar de visualização que é sempre lido (o _ViewImports.cshtml) .

Assim, no arquivo  _ViewImports.cshtml, inclua:

Isso pode ser feito com outros tag helpers que você vier a construir (além dos padrões Microsoft já existentes no Visual Studio).

Bloco 1 – Selects Mestre e Escravo

cascading selects

O primeiro select é o MESTRE e o segundo é o ESCRAVO. O funcionamento com javascrpt já vimos. Ocorre que quando entramos na página, esses selects devem ser previamente populados.

Papa popular usamos ViewBags, seguindo a sintaxe basica do HTML TAG HELPER para selects:  os dados são carregados dinamicamente a partir de uma base de dados e no select são colocadas no tag asp-items, formato asp-items(“ViewBag.MeusDados)”. Par carregar as ViewBagas é preciso colocar  neles uma lista IEnumerable. A View, nesse ponto, é um simples formulário com dois selects com suas respectivas ViewBags.

Note que o form (formulário) é submetido para Index2, no controlador. Index captura a entrada (GET), Index2 captura o formulário quando é submetido (via POST).

Bloco 2 – Botão de Filtrar

O botão Filtra Perguntas, no código acima, é um simples botão de submit, que vai obedecer o encaminhamento para Index2, definida no topo do formulário (<form asp-action=”Index2″>).

O botão Limpa Escolhas é um link simples (<a>…</a>), não passa nenhum parâmetro. A ideia é essa, simplesmente recarregar a página sem nenhuma filtragem, mostrando as 10 últimas perguntas cadastradas sem qualquer filtragem.

cascading dropdown

Quando o botão Filtra Perguntas é clicado, na verdade ele passa para Index2 os dois Ids. Nenhum deles pode estar selecionado, somente um dos dois (ou um ou outro) ou os 2: temos 4 opções possíveis. A ação Index2 simplesmente pega as Ids do POST e “devolve a bola” para a ação Index, que alem de repopular os selects, também muda a mensagem sobre a filtragem e os contadores de Totais (de Consultorias, de Competências e de Perguntas). Além disso, filtra os dados da Grid que vai ser apresentada abaixo desse bloco, no bloco 3. Todas as informações são passadas via ViewBags que são instanciadas na Ação (action) Index do controlador.

O botão Cria Nova Pergunta leva para um formulário de inserção, já com os dois parâmetros escolhidos. Quando uma nova pergunta é gravada, o form (formulário) e perguntas cham novamente a página Index, mas já passando para ela os dois IDs que recebeu (de forma que se pode cadastrar várias perguntas de uma Consultoria, para uma mesma Competência, sem ter de ficar novamente fazendo Selects!!!).

Bloco 3 – Grid com  Listagem das últimas perguntas registrado (máximo de 10)

Aqui a coisa pegou, porque o formulário (form) uma um modelo simples (não é uma lista!) e o grid precisa de uma lista… Os arquivos de Views  não permitem, de modo direto, que você defina dois ou mais modelos no topo (cabeçalho). É um modelo somente e ponto. Ir por esse caminho implica em gerar um terceiro modelo que contenha, dentro dele, os modelos que você quer usar na View. Mas pesquisando aqui e ali, acabei descobrindo uma outra forma de fazer isso, que será mostrada mais adiante.

cascading - formulário resultante do dropdown cascading

O modelo das Perguntas é muito simples:

 

O modelo acima é definido no topo do arquivo da View:

@model Coaching.Models.PerguntasBancoGeral
@{
ViewData[“Title”] = “Banco de Perguntas – Coaching Adm”;
}

Só que, a Grid, me interessava listar o Nome da Consultoria e o Nome da Competência, que estão em outras tabelas. Montei um modelo FAKE, contendo também os nomes, para poder popular esse modelo “Lista” (IENUMERABLE) através da ação Index do controlador (controller).

E, dentro da View, na hora de LISTAR todas as perguntas, no comando foreach, chamei esse modelo fake com os nomes:
foreach (var item in ViewBag.Lista as IEnumerable<Coaching.Models.PerguntasBancoGeralComNome>)

Veja a seguir o modelo FAKE e o bloco de código HTML da VIew do bloco 3 (grid).

Populando as opções do Select através do Banco de Dados

Para fazer isso criei dois métodos PRIVADOS dentro do controlador (controller). Um para popular a ViewBag de Consultorias, outro para popular a ViewBag de Competências.

Note que o VIewBag é instanciado dentro do método PRIVADO. Outra coisa: atribui o valor zero (0) para o “Selecione uma Consultoria”, pois isso facilita muito saber se a pessoa não fez nenhuma opção no select. É melhor que trabalhar com ”nulls”.

Se houver um item selecionado, vem um valor diferente de zero, que é passado por TipoSelecionado. A linha, em dados, cuja Id “bater” com o valor de TipoSelecionado será marcada como verdadeira (Selected = true).  Assim, quando a ViewBag entregar essa lista para o Select, o mesmo estar´posicionado nesta opção. DO contrário fica na primeira opção (0), “Selecione uma Consultoria”.

Populando as Competências

O princípio desse método é exatamente igual ao do anterior. A diferença é que ele utiliza 2 parâmetros que podem ter 4 comninações possíveis:

  • os 2 não são passados (são zero), nenhum dos dois foi selecionado: mostra todas as opções existentes (todas as competências);
  • somente o Id da Competência foi passado: mostra exatamente essa competência;
  • somente o id da Consultoria foi passado: mostra todas as competências ligadas a essa consultoria
  • os 2 são passados (diferente de zero): mostra exatamente a competência específica (igual caso 2)

O resultado dessa lista é armazenado na ViewBag de Competências dentro do método privado.

A Ação (Action) Index do Controlador

Esta ação é o “coração” desta pequena aplicação. Quando se entra na pagina Index.cshtml, a ação Index é solicitada no controlador (controller) e ela executa 3 papéis importantes:

  • chama os dois métodos populadores, passando a eles os parâmetros escolhidos na View;
  • carrega uma lista para a grid do modelo fake, com as devidas filtragens dos parâmetros e a coloca também numa VIeBag;
  • calcula e define todas as demais ViewBags que serão mostradas no arquivo de visualização (View), considerando as 4 possíveis alternativas de passagem dos 2 parâmetros.

O código é mostrado a seguir.

Obrigado pela sua leitura. Continue visitando este blog e compartilhe artigos em sua rede de relacionamento. Por favor, se quiser, registre sugestões e comentários ao final da página.

Junte-se à nossa rede de Empreendedores Builderall.

turbine seus negócios online com mais de 20 aplicativos Builderall

Listas DropDown usando o Select Tag Helper no CORE MVC

Listas DropDown usando o Select Tag Helper no CORE MVC

Usando o tag helper SELECT, é muito fácil ter listas dropdown nas suas páginas de criação e edição. Só que como as funcionalidades mudaram muito desde o início do Core MVC, há muita informação na Internet que simplesmente não funciona mais. E você pode perder horas – ou mesmo dias – tentando entender o que está acontecendo até conseguir um simples select com uma lista dropdown. Este artigo mostra como fazer isso rapidamente.

Usando o Select Tag Helper

O tag help para um select é usado adicionando o atributo asp-for a um elemento selecionado. Por exemplo, considere uma classe de modelo de visão muito simples, simples contendo uma propriedade Cidade:

public class MeuModeloDeCidades
{
    public string Cidade{ get; set; }
}

Para gerar o seu select basta instanciar a propriedade Cidade no comando de select, dentro da sua página de visualização (View):

<select asp-for="Cidade"></select>

O tag helper irá gerar o seguinte código HTML:

<select name="Cidade" 
           id="Cidade">
</select>

Este é o princípio lógico. Mas fazendo isso, você não tem nenhuma lista de opções (as cidades) dentro do seu select – e ele não serve para nada. É preciso popular o select com todas opções desejadas.

Acrescentando opções no Select do Tag Helper

Se as opções são poucas – e não vem de um banco de dados, você poder fazer isso no próprio arquivo de visualização (View):

<select asp-for="Cidade">
    <option value="São Paulo">São Paulo</option>
    <option value="Rio de Janeiro">Rio de Janeiro</option>
    <option value="--">Outras Cidades</option>
</select>

Nesse caso, as opções especificadas na marcação serão incluídas no HTML gerado. A opção selecionada será determinada automaticamente com base no valor da propriedade do modelo. Por exemplo, se a propriedade Cidade estiver definida como ‘São Paulo’ no modelo, o seguinte HTML será gerado:

<select name="Cidade" id="Cidade">
    <option selected="selected" value="São Paulo">São Paulo</option>
    <option value="Rio de Janeiro">Rio de Janeiro</option>
    <option value="--">Outras Cidades</option>
</select>

Se a lista de opções for carregada dinamicamente a partir de uma base de dados, você pode usar o atributo auxiliar chamado tag asp-items. Tudo o que você precisa fazer é definir o atributo asp-items para um IEnumerable. Por exemplo, se tivéssemos uma lista de cidades disponíveis numa ViewBag, poderíamos especificar as opções de seleção da seguinte forma:

<select asp-for="Cidades" 
         asp-items="ViewBag.ListaDeCidades">
</select>

IMPORTANTE: Não se esqueça de fechar o elemento <SELECT> com </SELECT>.

Este é o princípio. Mas como popular a ViewBag??? A VIewBag é passado do controlador (controller) para o visualizador (view).

Carregando as opções do Select através do Banco de Dados

Há duas formas de fazer isso. Em seu arquivo de controle (controller), na ação (action) que vai estar associada à visualização, você popula o ViewBag. Vamos imaginar neste exemplo que a Viewbag será usada num visualizador para CRIAR um novo registro. Neste caso, o cliente poderá escolher qualquer opção (não há opção pré definida).

Jeito 1:

public IActionResult Cria()
        {
            List<SelectListItem> Lista = new List<SelectListItem>();
            Lista.Add(new SelectListItem //adiciona uma opção que convida a escolher uma das possíveis opções
            {
                Text = "Selecione uma Consultoria",
                Value = ""
            });
            foreach (var Linha in Dados)
            {
                Lista.Add(new SelectListItem()
                {
                    Value = Linha.IdRelacionamento.ToString(),
                    Text = Linha.TipoDeRelacaoPublico,


                });
            }
            ViewBag.Selecionador = Lista;
         
            return View();
    }

Ou seja, em meu banco de dados tenho um arquivo de Consultorias, que faz parte do contexto (_context).  Quando registro um novo consultor no sistema, tenho de associá-lo a uma consultoria (já cadastrada). A tabela de pessoas vai guardar a Id da Consultoria no cadastramento da pessoa “consultora”. Por isso preciso do select, que chama a tabela de consultorias, mostra as mesmas pelo seu nome, mas ao gravar o arquivo o registro que é passado leva não o nome da consultoria – e sim a ID da consultoria.

Note que no comando foreach posso ordenar o select pelo nome das consultorias (.OrderBy(x=>x.NomeConsultoria). Também poderia incluir algum tipo de filtro acrescentado  .(Where x=>x.NomedoCampo == Uma string  ou um inteiro, conforme o campo).

Note também que ao passar valores inteiros (como uma id, é feita a conversãopara strign (z.IdConsultoria.ToString(), no caso).

A chamada, dentro do arquivo de visualização (View) fica da seguinte forma:

<div class="form-group">
            <label asp-for="IdConsultoria" class="col-md-2 control-label"></label>
            <div class="col-md-10">
             
                <select asp-for="IdConsultoria" class="form-control" asp-items="ViewBag.idConsultoria"></select>
                <span asp-validation-for="IdConsultoria" class="text-danger"></span>
              
            </div>
</div>

Jeito 2

Esta forma tem o “defeito” de não permitir a passagem, pelo controlador (controller) de uma opção tipo “Selecione uma das Consultorias”. Mas funciona.

public IActionResult Cria()
        {
           
         var Lista2 = from x in _context.Consultorias
                         orderby x.NomeConsultoria
                         select x;
            ViewBag.IdConsultoria2 = new SelectList(Lista2.AsNoTracking(), "IdConsultoria", "NomeConsultoria", "");

            return View();
    }

Mas, como no caso anterior, é possível ordenar e filtrar a seleção de opções usando clausulas where e orderby.

Para contornar esta limitação, você pode incluir no visualizador (vView) a seguinte linha, dentro da tag helper do select:

  <option value="">Selecione uma Consultoria</option>

Ficando assim:

<div class="form-group">
            <label asp-for="IdConsultoria" class="col-md-2 control-label"></label>
            <div class="col-md-10">
              <select asp-for="IdConsultoria" class="form-control" asp-items="ViewBag.idConsultoria2">
                    <option value="">Selecione uma Consultoria</option>
                </select>
                <span asp-validation-for="IdConsultoria" class="text-danger"></span>
   
            </div>
</div>

Habilitando Tag Helpers de forma genérica

Para que tag helpers funcionem em qualquer arquivo de visualização, a opção é habilitar no arquivo auxiliar de visualização que é sempre lido (o _ViewImports.cshtml) .

Assim, no arquivo  _ViewImports.cshtml, inclua:

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

Isso pode ser feito com outros tag helpers que você vier a construir (além dos padrões Microsoft já existentes no Visual Studio).

Criando Funções para Popular os Tag Helper de Selects nos controladores de criação e edição

Ocorre que você pode precisar do mesmo “dropdown” não só em em páginas de criação, mas também páginas de edição.

No caso de edição, já havia uma opção pré escolhida..

.Não é justo fazer com que o usuário tenha que, novamente, definir uma opção que já tinha definido antes. Ele pode mudar de ideia, mas sempre a partir da ideia que já teve antes.

Então, o conceito aqui é gerar o conteúdo da ViewBag considerando que pode ter havido uma opção anterior. É criada uma função privada, que sempre é chamada pelos controladores (controllers) de criação ou edição. Exemplos:

Lendo dados do arquivo Users (quando o sistema tem login)

//FUNCOES POPULA LOOKUP PARA EDICAO
        //PopulateAvaliado(id1); -> chadado pelo controlador
        //PopulateAvaliador(id2);
        //PopulateTipoDeAvaliador(id3);
        private void PopulateAvaliado(object PessoaSelecionada = null)
        {
            var pessoaQuery = from d in _userManager.Users
                              orderby d.UserName
                              select d;

            ViewBag.PessoaAvaliadoId = new SelectList(pessoaQuery.AsNoTracking(), "Id", "Email", PessoaSelecionada);
            return;
         }

Uma forma que funciona muito bem e apresenta o Selecionador somente nos caos de criação (em que não existe opção pré selecionada:

private void PopulateTipoDeAvaliador(object TipoSelecionado = null)
        {
            List<SelectListItem> Lista = new List<SelectListItem>();
            if (TipoSelecionado == null)
            {
                Lista.Add(new SelectListItem()
                { Value = null, Text = "<<Selecione Tipo de Relacionamento>>", Selected = true });
            }

            var Dados = from r in _context.Relacionamento orderby r.TipoDeRelacaoPublico select r;
            foreach (var Linha in Dados)
            {
                bool valor = false;
                if (Linha.IdRelacionamento == Convert.ToInt32(TipoSelecionado))
                {
                    valor = true;
                }
                Lista.Add(new SelectListItem()
                {
                    Value = Linha.IdRelacionamento.ToString(),
                    Text = Linha.TipoDeRelacaoPublico,
                    Selected = valor

                });
            }
            ViewBag.Selecionador = Lista;
        }

Lendo dados de um arquivo de Contexto (_context)

private void PopulateTipoDeAvaliador(object TipoSelecionado = null)
        {
            var relacaoQuery = from e in _context.TipoDeAvaliador
                               orderby e.TextoTipoDeAvaliador
                               select e;
            ViewBag.TipoDeRelacaoId = new SelectList(relacaoQuery.AsNoTracking(), "TipoDeAvaliadorID", "TextoTipoDeAvaliador", TipoSelecionado);
            return;
        }

Chamando os “lookups” dos selects do controlador de Criação:

// GET: Avaliacoes/Create
        public IActionResult Create()
        {
            //var id1 = avaliacoes.AvaliadoId;
            //var id2 = avaliacoes.AvaliadorId;
            // var id3 = avaliacoes.TipoDeAvaliadorID;

            PopulateAvaliado();
            PopulateAvaliador();
            PopulateTipoDeAvaliador();
            return View();
        }

        // POST: Avaliacoes/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("AvaliacaoId,AvaliadoId,AvaliadorId,DataCadastroAvaliacao,DataConviteAvaliacao,DataFimAvaliacao,DataUltimaAlteracao,DataUltimoLembrete,FlagFim,MediaDasRespostas,PerguntasRespondidas,TipoDeAvaliadorID,TotalDePerguntas")] Avaliacoes avaliacoes)
        {

            if (ModelState.IsValid)
            {
                _context.Add(avaliacoes);
                await _context.SaveChangesAsync();
                return RedirectToAction("Index");
            }
            var id1 = avaliacoes.AvaliadoId;
            var id2 = avaliacoes.AvaliadorId;
            var id3 = avaliacoes.TipoDeAvaliadorID;

            PopulateAvaliado(id1);
            PopulateAvaliador(id2);
            PopulateTipoDeAvaliador(id3);
            return View(avaliacoes);
        }

Chamando os “lookups” dos selects do controlador de Edição:

// GET: Avaliacoes/Edit/5
        public async Task<IActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var avaliacoes = await _context.Avaliacoes.SingleOrDefaultAsync(m => m.AvaliacaoId == id);
            if (avaliacoes == null)
            {
                return NotFound();
            }
            var id1 = avaliacoes.AvaliadoId;
            var id2 = avaliacoes.AvaliadorId;
            var id3 = avaliacoes.TipoDeAvaliadorID;

            PopulateAvaliado(id1);
            PopulateAvaliador(id2);
            PopulateTipoDeAvaliador(id3);
            return View(avaliacoes);
        }

        // POST: Avaliacoes/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, [Bind("AvaliacaoId,AvaliadoId,AvaliadorId,DataCadastroAvaliacao,DataConviteAvaliacao,DataFimAvaliacao,DataUltimaAlteracao,DataUltimoLembrete,FlagFim,MediaDasRespostas,PerguntasRespondidas,TipoDeAvaliadorID,TotalDePerguntas")] Avaliacoes avaliacoes)
        {
            if (id != avaliacoes.AvaliacaoId)
            {
                return NotFound();
            }

            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(avaliacoes);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!AvaliacoesExists(avaliacoes.AvaliacaoId))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction("Index");
            }
            var id1 = avaliacoes.AvaliadoId;
            var id2 = avaliacoes.AvaliadorId;
            var id3 = avaliacoes.TipoDeAvaliadorID;

            PopulateAvaliado(id1);
            PopulateAvaliador(id2);
            PopulateTipoDeAvaliador(id3);
            return View(avaliacoes);
        }

É isso. Se tiver sugestões, contribua.

Obrigado pela sua leitura. Continue visitando este blog e compartilhe artigos em sua rede de relacionamento. Por favor, se quiser, registre sugestões e comentários ao final da página.

Junte-se à nossa rede de Empreendedores Builderall.

turbine seus negócios online com mais de 20 aplicativos Builderall

Diferença de datas (datediff) no Core MVC

Diferença de datas (datediff) no Core MVC

O cálculo de diferença de datas (datediff) no Core é muito simples (mas há muita confusão n Internet sobre o assunto.

Um exemplo dentro de uma View, que tira a diferença de datas entre o dia corrente e uma data advinda do modelo da própria view:

@{var hoje = DateTime.Today;
        var limite = Model.DataFim;
        var DiasFaltantes = (limite - hoje).Value.Days;

    }

A variável hoje é um inteiro, tendo valores negativos se hoje for maior que a data na variável limite (capturada no modelo da View como Model.DataFim.

Isso ajuda muito, dentro do view, a tomar decisões de mostrar layouts diferentes (texto e ou dados) para cada uma das situações. Exemplo:

@{var hoje = DateTime.Today;
        var limite = Model.DataFim;
        var DiasFaltantes = (limite - hoje).Value.Days;

    }
    <div class="col-sm-4">
        @*/////botão edita questionário*@

        @if (DiasFaltantes >= 0) //ainda pode preencher avaliação
        {

            @*preenche avaliação*@
        if (Model.FlagFim == true) // preencheu completamente a avaliação, só pode editar
        {
            <a href="@Url.Action("Action", "Controller", new { @idAvaliacao = Model.IdAvaliacao })" class="btn btn-xs btn-success">
                Edite a Avaliação  @*Preenche Avaliação*@
                <span class="glyphicon glyphicon-list" aria-hidden="true"></span>
            </a>
        }
        else
        {
            @*edita avaliação*@
            <a href="@Url.Action("Action", "Controller", new { @idAvaliacao = Model.IdAvaliacao })" class="btn btn-xs btn-success">
                Preencha/Edite a Avaliação  @*Preenche Avaliação*@
                <span class="glyphicon glyphicon-list" aria-hidden="true"></span>
            </a>
            }

        }
        else//nao pode mais preencher avaliação
        {
            <h4 class="alert-danger text-center">Avaliação encerrada</h4>
        }
        }
    </div>

 

 

Obrigado pela sua leitura. Continue visitando este blog e compartilhe artigos em sua rede de relacionamento. Por favor, se quiser, registre sugestões e comentários ao final da página.

Junte-se à nossa rede de Empreendedores Builderall.

turbine seus negócios online com mais de 20 aplicativos Builderall

Formatando datas nas views do CORE MCV

Formatando datas nas views do CORE MCV

Para  ver as datas formatadas em qualquer view que utilize um campo de data, basta alterar o model que essa view utilza. A partir daí todas as views que usarem o mesmo model ficarão com as datas ajustadas.

Deverá ser acrescentada a linha (no caso para formatar datas no formato brasileiro, dia, mês e ano, sem hora:

[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]

Cada campo data do modelo ficará assim:

using System;
using System.ComponentModel.DataAnnotations;

namespace Coaching.Models
{
    public partial class Avaliacoes
    {
        public int IdAvaliacao { get; set; }
        public int IdAvaliado { get; set; }
        public int IdAvaliador { get; set; }
        public string TipoDeAvaliador { get; set; }
        public int IdTipoquestionario { get; set; }
        public bool? FlagFim { get; set; }
        public string Comentario { get; set; }
        public bool? FlagComentario { get; set; }
        [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
        public DateTime? DataFim { get; set; }
        public int? NumRespostas { get; set; }
        [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
        public DateTime? DataAltera { get; set; }
        [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
        public DateTime? DataConvite { get; set; }
        [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
        public DateTime? DataLembrete { get; set; }
        public int? NumPerguntas { get; set; }
        [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
        public DateTime? DataCadastro { get; set; }
    }
}

Em cada arquivo de modelo onde fizer isso, deverá sempre acrescentar no topo:

using System.ComponentModel.DataAnnotations;

Essas alterações valem para toda aplicação. Onder que que essas datas entrarem, nas views, eles estarão formatadas.

Obrigado pela sua leitura. Continue visitando este blog e compartilhe artigos em sua rede de relacionamento. Por favor, se quiser, registre sugestões e comentários ao final da página.

Junte-se à nossa rede de Empreendedores Builderall.

turbine seus negócios online com mais de 20 aplicativos Builderall