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

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) , ou também a versão comunitária do Visual Studio 2019, também 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.2 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.

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:

Aquivo para publish - SITE1
<publishData>
   <publishProfile 
   publishUrl="https://seunomedeusuario-001-site1.htempurl.com:8172/msdeploy.axd?site=seunomedeusuario-001-site1" 
   msdeploySite="seunomedeusuario-001-site1" 
   destinationAppUrl="http://seunomedeusuario-001-site1.htempurl.com/" 
   profileName="Default Settings" 
   publishMethod="MSDeploy" userName="ifcseunomedeusuario-001" 
   AllowUntrustedCertificate="True"
   /> 
</publishData>

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.


Clique em Create new profile:


Clique em Import Profile e dê OK


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

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

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.

Ajustando os parâmetros de senha no CORE MVC

Ajustando os parâmetros de senha no CORE MVC

Ajustando os parâmetros de senha no CORE MVC

Se você define usar o módulo de Autenticação / Identidade, saiba que pode definir com clareza todos os parâmetros para sua aplicação

Você pode definir todos os padrões no arquivo STARTUP.CS. Por padrão, Identity requer que as senhas contenham um caractere maiúsculo, caractere minúsculo, um dígito e um caractere não alfanumérico. As senhas devem ter pelo menos seis caracteres. PasswordOptions pode ser definido em Startup.ConfigureServices.

ARQUIVO STARTUP.CS - NO METODO public void ConfigureServices(IServiceCollection services)//

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            var cultureInfo = new CultureInfo("pt-BR");
            cultureInfo.NumberFormat.CurrencySymbol = "R$";

            CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
            CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;



            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));


            //services.AddDefaultIdentity<IdentityUser>()
            //    .AddDefaultUI(UIFramework.Bootstrap4)
            //    .AddEntityFrameworkStores<ApplicationDbContext>();
            // https://medium.com/@scottkuhl/extending-asp-net-core-2-2-identity-management-c3cc657cc448
            services.AddIdentity<AppUser, AppRole>()
             .AddDefaultUI(UIFramework.Bootstrap4)
             .AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();

//- comece aqui 

            //https://www.c-sharpcorner.com/article/asp-net-core-2-0-user-role-base-menu-management-using-dependency-injection/
            //Password Strength Setting  
            services.Configure<IdentityOptions>(options =>
            {
                // Password settings  
                options.Password.RequireDigit = true;
                options.Password.RequiredLength = 8;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = true;
                options.Password.RequireLowercase = false;
                options.Password.RequiredUniqueChars = 6;

                // Lockout settings  
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(300);
                options.Lockout.MaxFailedAccessAttempts = 10;
                options.Lockout.AllowedForNewUsers = true;

                // User settings  
                options.User.RequireUniqueEmail = true;
            });

//- termine aqui 

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddViewLocalization();
        }

IdentityOptions.Password especifica o PasswordOptions com as propriedades mostradas na tabela. 

Propriedade Descrição Padrão
RequireDigit Requer um número entre 0-9 na senha true
RequiredLength O comprimento mínimo da senha 6
RequireLowercase Requer um caractere minúsculo na senha true
RequireNonAlphanumeric Requer um caractere não alfanumérico na senha. true
RequiredUniqueChars Aplica-se somente ao ASP.NET Core 2.0 ou posterior. Requer o número de caracteres distintos na senha 1
RequireUppercase Requer um caractere maiúsculo na senha true

 

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.

Modificando e ampliando o Gerenciamento de Identidades do ASP.NET Core 2.2 e implementar Componentes Synfusion

Modificando e ampliando o Gerenciamento de Identidades do ASP.NET Core 2.2 e implementar Componentes Synfusion

Vimos no artigo “Como instalar Identidade e Autorização da Microsoft criar um projeto novo com o Syncfusion“, com todos componentes que deseja usar –  e depois como acrescentar Autenticação e Identidade da Microsoft.

Neste artigo veremos como modificar e ampliar o arquivo de “usuários” (“AspNetUsers”) – personalizando-o para ter mais campos e também para usar uma identidade (ID) que seja numérica – e não string.

Passo 1: Instale a sua aplicação Core MVC 2.2

Estou Assumindo que você já tenha um aplicativo da Web com a autenticação do usuário ativada, montada pelo próprio template do Visual Studio 2017 ou 2019. Na geração da aplicação você pode configurá-lo selecionando “Contas de usuário individuais” como o tipo de autenticação ao criar seu projeto.

Ou você pode adicioná-lo manualmente a um aplicativo seguindo as instruções do artigo:

Como instalar Identidade e Autorização da Microsoft criar um projeto novo com o Syncfusion

 

Passo 2: O que pode ser alterado no arquivo de Identidade?

Há duas coisas que não gosto quando a Microsoft instala seu Microsoft Identity Manager – mas devo reconhecer que essa funcionalidade torna realmente fácil adicionar usuários ao seu site ASP.NET Core.

Há sempre duas coisas que quero sempre mudar imediatamente.

A primeira é pedir o nome de um usuário (para inclusive poder usá-lo depois na barra de navegação com um “Olá NOME DE USUÁRIO” (no lugar de “Ola EMAIL DO USUÁRIO”. A segunda é alterar aquela detestável chave primária do usuário em “string”, substituindo-a por valores “inteiros”. – tornando muito mais fácil integrar a tabela de usuários (“ASPNETUSERS”) com as demais tabelas da aplicação. Nos próximos passos deste artigo vamos ver como fazer isso.

No exemplo, ao registra um usuário vou perguntar seu NOME COMPLETO. Na sua implementação você pode optar por perguntar separadamente o nome e sobrenome (e criar, no modelo, um campo só de leitura que junta o nome com o sobrenome, criando o nome completo automaticamente (a vantagem de fazer isso é que a pessoa é sempre obrigada a digitar os 2 campos… Quando você pede somente o nome completo, ela pode desconsiderar e digitar somente o primeiro nome).

Você pode acrescentar quantos campos quiser no padrão de gerenciamento de identidade padrão da Microsoft, que por default utiliza apenas de endereços de e-mail.

Finalmente, como já falei,  gosto de mudar a chave primária de uma string para um inteiro para corresponder ao resto do meu modelo de dados. Costumo preferir números inteiros em GUIDs, e não strings, difíceis de conferir. O pressuposto aqui é que você já tenha um aplicativo da Web com a autenticação do usuário ativada. Ao criá-lo, você pode configurá-lo selecionando Contas de usuário individuais como sendo o tipo de autenticação do projeto. Ou você pode adicioná-lo manualmente a um aplicativo seguindo nossas instruções no artigo do Passo 1.

 

Passo 3: Adicionando novos modelos para Autorização / Identidade

Vamos começar adicionando dois novos modelos ao nosso aplicativo. Nas suas pastas Modelos (Models), crie uma nova classe chamada AppRole que estende o IdentityRole. Isso pode ser usado para atribuir funções especiais a usuários como “admin” e outras categorias de usuários, com papéis diferentes na sua aplicação.

Script para roles a ser colocado na pasta Models
using Microsoft.AspNetCore.Identity;
 
namespace IdentityExample.Models
{
    public class AppRole : IdentityRole<int>
    {
        public AppRole() { }
 
        public AppRole(string name)
        {
            Name = name;
        }
    }
}

Em seguida, adicione uma classe AppUser que amplia o IdentityUser para que possamos coletar mais dados sobre eles (você pode acrescentar mais campos se quiser). Note que o nome completo é montado a partir de nome sobrenome, neste exemplo.

Script para AppUser a ser colocado na pasta Models
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;
 
namespace IdentityExample.Models
{
    public class AppUser : IdentityUser<int>
    {
        [PersonalData, Required, StringLength(20)]
        public string FirstName { get; set; }
 
        [PersonalData, Required, StringLength(20)]
        public string LastName { get; set; }
 
        public string FullName { get { return $"{FirstName} {LastName}"; } }
    }
}

Note que as duas classes são marcadas com <int>.

Isso informa ao Gerenciamento de Identidades que queremos usar chaves primárias baseadas em números inteiros.

O AppUser marca suas propriedades com algumas anotações de dados padrão para tornar o primeiro e o último nome necessários com um comprimento máximo de 20 caracteres. Essas propriedades também são marcadas com PersonalData, ficando disponíveis automaticamente para download e exclusão.

Tornar os dados capazes de serem baixados e excluídos ajuda a atender aos requisitos GDPR. Também foi uma propriedade FullName para formatar de forma consistente o nome completo de um usuário em todo o aplicativo.

Passo 4 – Preparando o banco de dados

Muito provavelmente sua aplicação vai rodar em um servidor externo MYSQL ou MSSQL.

Crie o database em vazio (sem tabelas ainda) e pegue a definição da string de conexão. Exemplo:

“Data Source=EndereçoDoServidor;Initial Catalog=NomeDOBancoDeDados;User Id=NomeDoUsuario;Password=SenhaDoUsuario;”

No arquivo appsetings,json TROQUE a string de conexão gerada na criação do seu aplicativo pela sua string de conexão.

Passo 5 – Atualizando o Context Database (diretório Data, arquivo ApplicationDbContext.cs

O contexto do banco de dados estende IdentityDbContext, precisamos atualizá-lo para informá-lo sobre nossas novas classes e estratégia de chave primária.

 

Arquivo ApplicationDbContext.cs no folder DATA
public class ApplicationDbContext : IdentityDbContext<AppUser, AppRole, int>

Nós também queremos adicionar ou atualizar OnModelCreating para ignorar a propriedade FullName que adicionamos. É uma propriedade computada que não queremos armazenar no banco de dados.

ignorando fullName em ApplicatioBbContext
protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);
    builder.Entity<AppUser>().Ignore(e => e.FullName);
}

Nossa classe de inicialização está fazendo referência à identidade padrão. Precisamos atualizá-lo para nossa identidade personalizada. Antes, no arquivo startup.cs:

Antes em STARTUP.CS
services.AddDefaultIdentity<IdentityUser>()
    .AddDefaultUI(UIFramework.Bootstrap4)
    .AddEntityFrameworkStores<ApplicationDbContext>();

Depois:

Depois em STARTUP.CS
services.AddIdentity<AppUser, AppRole>()
    .AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();

Passo 6 – Atualizando _loginPartial.cshtml

O controle de login na barra de ferramentas também possui referências que precisam ser atualizadas. (Não se esqueça de adicionar uma declaração usando à sua pasta de modelos aqui.)

 

Lembre-se de mudar IdentityExample para o nome so seu NameSpace
@using Microsoft.AspNetCore.Identity
@using IdentityExample.Models
@inject SignInManager<AppUser> SignInManager
@inject UserManager<AppUser> UserManager

Lembre-se não vamos mais mostrar ao usuário seu endereço de e-mail. Em vez disso, vamos mostrar seu nome.

Antes:

Linha que originalmente mostra e-mail no menu superior
Hello @User.Identity.Name!

Depois:

Linha que agora mostra e-mail no menu superior. Poderia ser nome completo (fullname)
Hello @((await UserManager.FindByNameAsync(User.Identity.Name)).FirstName)!

Possível Problema com caracteres alienígenas

Se a linha do navegador apresentar caracteres internacionais com símbolos alienígena, é porque o Visual Studio salvou o arquivo _loginPartial.cshtml em ANSI – e não em UTF-8.

O artigo aqui explica isso. Abra o arquivo _loginPartial.cshtml  com o notepad++  (instale esse editor gratuito que é fantástico, dá de 100 a 0 no notepad da Microsoft). Salve o arquivo com ENCODING UTF-8 e rode a aplicação para ver como “sumiram os caracteres alienígenas”.

 

Passo 7 – Substituir páginas de identidade padrão (Default Identity Pages)

  1. Clique com o botão direito no nome do projeto.
  2. Selecione ADD> New Scaffolded Item.
  3. Selecione Identity (Identidade) no menu à esquerda.
  4. Selecione ADD.
  5. Escolha Account/Manage/Index e também a opção Account/Register.
  6. Selecione sua classe de contexto do banco de dados existente.

Isso criará novas páginas para o registro do usuário e a página do perfil do usuário, onde podemos solicitar o primeiro e o último nome.
Nota> se quiser usar o primeiro nome, o ultimo no ou ainda o nome completo, em outras páginas, escolha essas páginas já no item 5, acima,

 

Passo 8 – Arquivo _ValidationScriptsPartial.cshtml

Copie o HTML do seu arquivo atual, localizado na pasta Pages/Shared, para o novo na pasta Areas/Identity/Pages, para ter certeza de que são os mesmos.

 

Passo 9 – Arquivo Register.cshtml.cs

Adicione propriedades de primeiro e último nome à classe InputModel. Se não encontrar o arquivo, é porque se esqueceu de fazer o ADD NEW SCAFFOLDED do passo 7. Este arquivo Fica na pasta Areas;Identity/Pages/Account e dentro de Register.cshtml

 

Acrescentando primeiro e ultimo nome na classe INPUT MODEL
[Required, DataType(DataType.Text), Display(Name = "First Name")]
public string FirstName { get; set; }
 
[Required, DataType(DataType.Text), Display(Name = "Last Name")]
public string LastName { get; set; }

Adicione esses valores de propriedade à declaração de variável do usuário em OnPostAsync

valores de propriedade na declaracao OnPostAsync
var user = new AppUser { UserName = Input.Email, Email = Input.Email, FirstName = Input.FirstName, LastName = Input.LastName };

Passo 10 – Arquivo Register.cshtml

Adicione os campos de entrada para primeiro e último nome, logo abaixo do controle asp-validation-summary.

 

definindo nome e sobrenome para tela de registro
<div class="form-group">
    <label asp-for="Input.FirstName"></label>
    <input asp-for="Input.FirstName" class="form-control" />
    <span asp-validation-for="Input.FirstName" class="text-danger"></span>
</div>
<div class="form-group">
    <label asp-for="Input.LastName"></label>
    <input asp-for="Input.LastName" class="form-control" />
    <span asp-validation-for="Input.LastName" class="text-danger"></span>
</div>

Passo 11 – Arquivo Index.cshtml.cs

Esta é uma classe de fica no folder Areas/Identity/Pages/Account/Manage

Adicione novamente as propriedade first e last name na classe InputModel.

definindo propriedades nome e sobrenome para arquivo index
[Required, DataType(DataType.Text), Display(Name = "First Name")]
public string FirstName { get; set; }
 
[Required, DataType(DataType.Text), Display(Name = "Last Name")]
public string LastName { get; set; }

Atualize a variável Input e OnGetAsync para popular essas propriedades.

atualiza a variavel Input em OnGetAsync
Input = new InputModel
{
    Email = email,
    PhoneNumber = phoneNumber,
    FirstName = user.FirstName,
    LastName = user.LastName
};

Salve as propriedades em OnPostAsync que fica próxima ao final do método.

Mudando OnPostAsync
if (Input.FirstName != user.FirstName) user.FirstName = Input.FirstName;
if (Input.LastName != user.LastName) user.LastName = Input.LastName;
await _userManager.UpdateAsync(user);
 
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "Your profile has been updated";
return RedirectToPage();

Passo 12 – Arquivo Index.cshtml

Adicione os campos de entrada para nome e sobrenome abaixo da entrada de e-mail e acima do número de telefone.

Colocando nome e sobrenome no arquivo index
<div class="form-group">
    <label asp-for="Input.FirstName"></label>
    <input asp-for="Input.FirstName" class="form-control" />
    <span asp-validation-for="Input.FirstName" class="text-danger"></span>
</div>
<div class="form-group">
    <label asp-for="Input.LastName"></label>
    <input asp-for="Input.LastName" class="form-control" />
    <span asp-validation-for="Input.LastName" class="text-danger"></span>
</div>

Passo 13 – Atualizando Migrações (Update Migrations)

O próximo passo lógico é atualizar migrações, para criar no banco de dados todas as tabelas de autorização e de identidade (ji usando números inteiros para o ID de pessoas (Users) e regras (Roles). 

Se o seu banco de dados está criado mas está vazio (sem tabelas)

Neste caso minha primeira recomendação é criar um template do seu projeto, para que você possa iniciar novas aplicações já a partir do estágio atingido no passo 12. Para criar o template:

  1. No menu superior dê um BUILD na solução para ter certeza de que não há nenhum erro ou file,
  2. Salve os arquivos todos.
  3. No menu superior, em Project, escolha Export Template. Deixe marcado Export Template e dê um NEXT. e um nome e uma descriçao para o seu temlate, que vai aparecer cada vez que você iniciar um novo projeto

Se eu banco de dados está criado, mas não está vazio (tem tabelas)

Isso significa que você já fez pelo menos alguma migração neste projeto. Se você tentar adicionar uma nova migração, ela falhará devido à alteração da chave primária.

Neste caso, o caminho mais fácil é:

  1. Excluir sua pasta de migração
  2. Excluir seu banco de dados ou recriá-lo (não esqueça de mudar a string de conexão em APPSETTINGS.
  3. Se você não quiser recriar o banco de dados, mas somente limpá-lo completamente, deixando-o sem tabelas, views e procedures, rode no sue MSSQL a rotina proposta abaixo. Abra uma janela de NEW QUERY, copie a rotina e execute.
  4. Depois de recriar ou limpar o banco de dados, você poderá adicionar sua “primeira migração”.
  5. No Console do Gerenciador de Pacotes PMC rode estes dois comandos, um após o outro:
    1. Add-Migration Initial -o Data\Migrations
    2. Update-Database

Agora execute seu aplicativo e você poderá registrar uma conta com nome e sobrenome, ver o nome do usuário na barra de navegação e atualizar o primeiro e último nome. Agora você pode usar a propriedade AppUser.Id em seus próprios modelos para referenciar o registro do usuário como faria com qualquer outro modelo.

 

Rotina para limpar completamente o banco de dados MSSQL
/* Drop all non-system stored procs */
DECLARE @name VARCHAR(128)
DECLARE @SQL VARCHAR(254)

SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'P' AND category = 0 ORDER BY [name])

WHILE @name is not null
BEGIN
    SELECT @SQL = 'DROP PROCEDURE [dbo].[' + RTRIM(@name) +']'
    EXEC (@SQL)
    PRINT 'Dropped Procedure: ' + @name
    SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'P' AND category = 0 AND [name] > @name ORDER BY [name])
END
GO

/* Drop all views */
DECLARE @name VARCHAR(128)
DECLARE @SQL VARCHAR(254)

SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'V' AND category = 0 ORDER BY [name])

WHILE @name IS NOT NULL
BEGIN
    SELECT @SQL = 'DROP VIEW [dbo].[' + RTRIM(@name) +']'
    EXEC (@SQL)
    PRINT 'Dropped View: ' + @name
    SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'V' AND category = 0 AND [name] > @name ORDER BY [name])
END
GO

/* Drop all functions */
DECLARE @name VARCHAR(128)
DECLARE @SQL VARCHAR(254)

SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] IN (N'FN', N'IF', N'TF', N'FS', N'FT') AND category = 0 ORDER BY [name])

WHILE @name IS NOT NULL
BEGIN
    SELECT @SQL = 'DROP FUNCTION [dbo].[' + RTRIM(@name) +']'
    EXEC (@SQL)
    PRINT 'Dropped Function: ' + @name
    SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] IN (N'FN', N'IF', N'TF', N'FS', N'FT') AND category = 0 AND [name] > @name ORDER BY [name])
END
GO

/* Drop all Foreign Key constraints */
DECLARE @name VARCHAR(128)
DECLARE @constraint VARCHAR(254)
DECLARE @SQL VARCHAR(254)

SELECT @name = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' ORDER BY TABLE_NAME)

WHILE @name is not null
BEGIN
    SELECT @constraint = (SELECT TOP 1 CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' AND TABLE_NAME = @name ORDER BY CONSTRAINT_NAME)
    WHILE @constraint IS NOT NULL
    BEGIN
        SELECT @SQL = 'ALTER TABLE [dbo].[' + RTRIM(@name) +'] DROP CONSTRAINT [' + RTRIM(@constraint) +']'
        EXEC (@SQL)
        PRINT 'Dropped FK Constraint: ' + @constraint + ' on ' + @name
        SELECT @constraint = (SELECT TOP 1 CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' AND CONSTRAINT_NAME <> @constraint AND TABLE_NAME = @name ORDER BY CONSTRAINT_NAME)
    END
SELECT @name = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' ORDER BY TABLE_NAME)
END
GO

/* Drop all Primary Key constraints */
DECLARE @name VARCHAR(128)
DECLARE @constraint VARCHAR(254)
DECLARE @SQL VARCHAR(254)

SELECT @name = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'PRIMARY KEY' ORDER BY TABLE_NAME)

WHILE @name IS NOT NULL
BEGIN
    SELECT @constraint = (SELECT TOP 1 CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'PRIMARY KEY' AND TABLE_NAME = @name ORDER BY CONSTRAINT_NAME)
    WHILE @constraint is not null
    BEGIN
        SELECT @SQL = 'ALTER TABLE [dbo].[' + RTRIM(@name) +'] DROP CONSTRAINT [' + RTRIM(@constraint)+']'
        EXEC (@SQL)
        PRINT 'Dropped PK Constraint: ' + @constraint + ' on ' + @name
        SELECT @constraint = (SELECT TOP 1 CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'PRIMARY KEY' AND CONSTRAINT_NAME <> @constraint AND TABLE_NAME = @name ORDER BY CONSTRAINT_NAME)
    END
SELECT @name = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'PRIMARY KEY' ORDER BY TABLE_NAME)
END
GO

/* Drop all tables */
DECLARE @name VARCHAR(128)
DECLARE @SQL VARCHAR(254)

SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'U' AND category = 0 ORDER BY [name])

WHILE @name IS NOT NULL
BEGIN
    SELECT @SQL = 'DROP TABLE [dbo].[' + RTRIM(@name) +']'
    EXEC (@SQL)
    PRINT 'Dropped Table: ' + @name
    SELECT @name = (SELECT TOP 1 [name] FROM sysobjects WHERE [type] = 'U' AND category = 0 AND [name] > @name ORDER BY [name])
END
GO

Rodando o script acima, ele vai mostrar que apagou os índices e as tabelas:

Dropped FK Constraint: FK_AspNetRoleClaims_AspNetRoles_RoleId on AspNetRoleClaims
Dropped FK Constraint: FK_AspNetUserClaims_AspNetUsers_UserId on AspNetUserClaims
Dropped FK Constraint: FK_AspNetUserLogins_AspNetUsers_UserId on AspNetUserLogins
Dropped FK Constraint: FK_AspNetUserRoles_AspNetRoles_RoleId on AspNetUserRoles
Dropped FK Constraint: FK_AspNetUserRoles_AspNetUsers_UserId on AspNetUserRoles
Dropped FK Constraint: FK_AspNetUserTokens_AspNetUsers_UserId on AspNetUserTokens
Dropped PK Constraint: PK___EFMigrationsHistory on __EFMigrationsHistory
Dropped PK Constraint: PK_AspNetRoleClaims on AspNetRoleClaims
Dropped PK Constraint: PK_AspNetRoles on AspNetRoles
Dropped PK Constraint: PK_AspNetUserClaims on AspNetUserClaims
Dropped PK Constraint: PK_AspNetUserLogins on AspNetUserLogins
Dropped PK Constraint: PK_AspNetUserRoles on AspNetUserRoles
Dropped PK Constraint: PK_AspNetUsers on AspNetUsers
Dropped PK Constraint: PK_AspNetUserTokens on AspNetUserTokens
Dropped Table: __EFMigrationsHistory
Dropped Table: AspNetRoleClaims
Dropped Table: AspNetRoles
Dropped Table: AspNetUserClaims
Dropped Table: AspNetUserLogins
Dropped Table: AspNetUserRoles
Dropped Table: AspNetUsers
Dropped Table: AspNetUserTokens

Note, abaixo, que as tabelas foram geradas dom IDs com números inteiros!!!

Rodando a aplicação, a tela de entrada contendo à direita os campos de Login e Registro

Tela de Registro, com Nome e Sobrenome:

Tela Inicial, mostrando o nome no lugar do e-mail. E aparece o campo de Logout, no lugar de login.

Instalando o Syncfusion

Instalando os pacotes via NuGet Packages

O NuGet Package Manager pode ser usado para pesquisar e instalar pacotes do NuGet na solução ou no projeto do Visual Studio.

Passo 1 : Clique com o botão direito do mouse no projeto / solução na guia Solution Explorer e escolha Manage NuGet Packages…

 

Alternativamente, clique Tools, menu, NuGet Package Manager | Manage NuGet Packages for Solution…

Passo 2 : Selecione o NuGet.org na lista suspensa Fonte do pacote (Package source drop-down).

Passo 3 : Todos os Pacotes do Syncfusion NuGet estão listados e disponíveis. Pesquise e instale os pacotes necessários em seu aplicativo clicando no botão Instalar..

Os pacotes Syncfusion NuGet estão disponíveis publicamente em NuGet.org desde a versão v16.2.0.46 . Para instalar versões anteriores à 16.2.0.46 , configure Syncfusion URL fees privado.

Em nosso exemplo vamos instalar a versão CORE J2 17.2.0.34.

Os controles do ASP.NET Core UI são criados do zero para serem leves, responsivos, modulares e sensíveis ao toque. Inclui mais de 50 componentes, incluindo grade, gráfico, agendador e muito mais. Saiba mais: https://www.syncfusion.com/aspnet-core-ui-controls Características principais:

  • Suporta o TagHelper e o construtor fluente para inicialização de controle.
  • Suporte de anotação de dados.
  • Temas internos impressionantes, incluindo material, bootstrap, tecido, etc.
  • Conformidade com os padrões de acessibilidade – WCAG 2.0 e Seção 508.

Componentes e controles disponíveis:

GRIDS
DataGrid
Pivot Table
Tree Grid (Preview)
DATA VISUALIZATION
Charts
Stock Chart (Preview)
Circular Gauge
Diagram
Heatmap chart
Linear Gauge
Maps
Range Selector
Smith Chart
Sparkline Charts
TreeMap
EDITORS
Rich Text Editor
Word Processor
Chips (Preview)
CALENDARS
Scheduler
Calendar
DatePicker
DateRangePicker
DateTime Picker
TimePicker
BUTTONS
Button
ButtonGroup
Dropdown Menu
Progress Button
Split Button
DROPDOWNS
AutoComplete
ComboBox
Dropdown List
MultiSelect Dropdown
NAVIGATION
Accordion
Context Menu
Menu Bar
Sidebar
Tabs
Toolbar
TreeView
INPUTS
Form Validator
TextBox
Input Mask
Numeric Textbox
Radio Button
Checkbox
Color Picker
File Upload
Range Slider
Toggle Switch Button
LAYOUT
Avatar
Card
Dialog
ListView
Tooltip
Splitter (Preview)
NOTIFICATIONS
Badge
Toast
FORMS
In-place Editor (Preview)
Viewer
PdfViewer (Preview)

Passo 4: Ajustes Finais

Visual Studio 2017/2019

Abra o arquivo ~/Views/_ViewImports.cshtml e importe o pacote Syncfusion.EJ2. Acrescente:

@addTagHelper *, Syncfusion.EJ2

Adicione os recursos do lado do cliente por meio do CDN ou do pacote npm local no elemento <head> da página de layout ~/Views/Shared/_Layout.cshtml. Acrescente:

acrescentar na ~/Views/Shared/_Layout.cshtml layout page
<head>
    ....
    ....

    <!-- Syncfusion Essential JS 2 Styles -->
    <link rel="stylesheet" href="https://cdn.syncfusion.com/ej2/material.css" />

    <!-- Syncfusion Essential JS 2 Scripts -->
    <script src="https://cdn.syncfusion.com/ej2/dist/ej2.min.js"></script>
</head>

Adicione o Essential JS 2 Script Manager no final do elemento <body> na página de layout ~/Views/Shared/_Layout.cshtml.

Acrescentar em body
<body>
    ....
    ....
    <!-- Syncfusion Essential JS 2 ScriptManager -->
    <ejs-scripts></ejs-scripts>
</body>

Agora, adicione os componentes do Syncfusion Essential JS 2 em qualquer página da web (cshtml) na pasta Views.

Por exemplo, o componente de calendário é adicionado na página ~/Views /Home/Index.cshtml.

teste de calendario na view index
<div>
    <ejs-calendar id="calendar"></ejs-calendar>
</div>

Execute o aplicativo. O componente de calendário do Essential JS 2 será renderizado no navegador da web.

Conclusão

Agora você pode salvar o template do projeto considerando o uso dos componentes e controles do Syncfusion. Basta ir in Projec->Export Template. Ao iniciar um novo projeto, poderá desfrutar de inicio de todas as mudanças. Eventualmente, terá de mudar a conexão ao banco de dados (em appsettings), apagar o diretórios migrations e fazer a primeira migração novamente. Mas isso é coisa que tomará ,initos – e não horas. Esperamos que tenha curtido este artigo e – se tiver sugestões ou dúvidas – é só nos enviar,

Ajustando os parâmetros de senha no CORE MVC

Syncfusion Core: como instalar Identidade e Autorização da Microsoft

Antigamente, ao definir um projeto novo em core MVC, você tinha que definir se o projeto teria ou não identificação/autorização. A partir do core 2.1 isso não é mais necessário. Você pode definir isso depois. Melhor ainda: você pode criar um projeto novo com o Syncfusion, com todos componentes que deseja usar –  e depois acrescentar Autenticação e Identidade da Microsoft

Passo 1: Instale o Gerenciador (Painel de Controle) da Syncfusion

Ao instalar, escolha ao menos a opção Syncfusion ASP.NET Core versão JS 2. Importante: pegue sua licença comunitária na Syncfusion antes… Ela serve para todos seus projetos.

Passo 2: Instale as extensões “Syncfusion Visual Studio extension”.

Você pode definir se quer as extensões para o Visual Studio 2017, 2019 ou ambas. Notar que na verdão 2017 as opções do Syncfusion estarão na barra superior, primeiro nível. Já no 2019. procure em Extesões -> Syncfusion. Veja aqui como fazer.

Passo 3: Gere sua aplicação inicial

Duas opções para gerar:

  1. A partir do Visual Studio: procure o template CORE MVC com a marca da SYNCFUSION (deve ser o último da lista) e gere a aplicação.
  2. A partir do Painel de Controle da Syncfusion. Neste caso, você pode pré-selecionar todos os componentes que pretende usar em seus projetos.

Nos dois casos você pode escolher se quer arquivos auxiliares locais ou em CDN (eu escolhi CDN) e o tipo de tema que vai querer usar (eu escolhi Bootstrap).

Uma vez que você criou o projeto, rode em debug para ver se está OK na máquina local.

Passo 4: Instale a Identidade/Autorização da Microsoft

  • Clique (com o botão da direita) no nome de seu  projeto no Solution Explorer do Visual Studio. Você deverá rodar o Identity Scafolder;  Add > New Scaffolded Item.
  • No painel esquerdo do diálogo Add Scafold,  selecione  Identity > ADD;
  • No Add Identity, selecione as opções que você vai querer usar (tipo págias de deleção, de troca de senha, de confirmação de e-mail, etc – além da página de login);
    • Selecione a página de layout que você já possui (_layout.cshtm, normalmente em Views/Shared). Do contrário, seu arquivo de layout será escrito com códigos incorretos. Por exemplo , escolha ~/Pages/Shared/_Layout.cshtml  se você tiver páginas Razor. Ou <~/Views/Shared/_Layout.cshtml para projetos MVC (seu caso deve ser este)
    • Selecione o botá + para criar uma Classe de Contexto de Dados (Data context class) . O sistema vai sugerir um, use esse. Se não sugerir, defina um,
  • Selecione ADD.

Passo 5: Acertando o arquivo de Layout (_layout.cshtm)

Para poder “ver” os dados de login assim que rodar a aplicação, é fundamental adicionar no seu arquivo  Views/Shared/_Layout.cshtml o login parcial ( (_loginPartial). Para isso terá que inserir <partial name=“_LoginPartial” />. Como a aplicação usa cookies, também terá de inserir <partial name=”_CookieConsentPartial” />. Veja no arquido de exemplo abaixo onde  deve inserir esses comandos. No SEU próprio arquivo, nã mude MAIS NADA a não ser incluir essas duas linha. Veja um exemplo abaixo (não copie todas as linhas para o seu arquivo  Views/Shared/_Layout.cshtml.

Exemplo de c&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;oacute;digo: veja onde inserir as linhas de login parcial e cookies no arquivo _layout.cshtm
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - DiagnosticoCultura</title>

    <environment names="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
        <link rel="stylesheet" href="~/css/site.css" />
    </environment>
    <environment names="Staging,Production">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
              integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
              crossorigin="anonymous" />
        <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    </environment>
  <link rel="stylesheet" href="https://cdn.syncfusion.com/ej2/17.2.34/bootstrap4.css" />

  <script src="https://cdn.syncfusion.com/ej2/17.2.34/dist/ej2.min.js"></script>

</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <a class="navbar-brand" href="#">DiagnosticoCultura</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item active">
                    <a class="nav-link" asp-controller="Home" asp-action="Index">Home<span class="sr-only">(current)</span></a>
                    <!-- <a class="nav-link" href="#">Home <span class="sr-only">(current)</span></a> -->
                </li>
                <li class="nav-item">
                    <a class="nav-link" asp-controller="Home" asp-action="About">About</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" asp-controller="Home" asp-action="Contact">Contact</a>

                </li>
            </ul>
            @* https://docs.microsoft.com/pt-br/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-2.2&tabs=visual-studio *@
            <partial name="_LoginPartial" />



            <form class="form-inline my-2 my-lg-0">
                <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
                <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
            </form>
        </div>
    </nav>

    @* https://docs.microsoft.com/pt-br/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-2.2&tabs=visual-studio *@
    <partial name="_CookieConsentPartial" />



    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>© @DateTime.Now.Year - DiagnosticoCultura</p>
        </footer>
    </div>

    <environment names="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
        <script src="~/js/site.js" asp-append-version="true"></script>
    </environment>
    <environment names="Staging,Production">
        <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
                crossorigin="anonymous">
        </script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
                integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
                crossorigin="anonymous">
        </script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
                integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
                crossorigin="anonymous">
        </script>
        <script src="~/js/site.min.js" asp-append-version="true"></script>
    </environment>
    <ejs-scripts></ejs-scripts>
    @RenderSection("Scripts", required: false)
</body>
</html>

Você tem muitas outras opções de incluir ou alterar a identidade. Basta conferir no artigo “Scaffold Identity in ASP.NET Core projects” da Microsoft.

Passo 6 – Preparando o banco de dados

Muito provavelmente sua aplicação vai rodar em um servidor externo MYSQL ou MSSQL.

Crie o database em vazio (sem tabelas ainda) e pegue a definição da string de conexão. Exemplo:

“Data Source=EndereçoDoServidor;Initial Catalog=NomeDOBancoDeDados;User Id=NomeDoUsuario;Password=SenhaDoUsuario;”

No arquivo appsetings,json TROQUE a string de conexão gerada na criação do seu aplicativo pela sua string de conexão.

Passo 7 – Fazendo o Scafolding acertar seu banco de dados, criando as tabelas para identificação e autorização

A identidade é essencialmente configurada em Areas/Identity/IdentityHostingStartup.cs. Para mais informações, consulte IHostingStartup.

As tabelas do banco de dados para Identidade e Autorização requerem o uso do Entity Framework Core Migrations. Será necessário fazer uma migração e autulizar o banco de dados. Isso pode ser feito em Tools->NudgePackageManager->PacketManagerConsole (usuamente chamada de PMC). Rode os seguintes comando:

 

Rodando o Scafolding no PMC
Add-Migration CreateIdentitySchema
Update-Database

O parâmetro “CreateIdentitySchema” psrs o comando Add-Migration descreve no nome da migração. Ele é opcional, mas é sempre últil dar um nome para você lembrar depois o que essa migração fez (criou nova tabela, modificou uma tabela, acrescentou um campo, eliminou um campo, etc.

No arquivo startup.cs é fundamental ter certeza que estão as seguintes linhas de configuração da aplicação:

app.UseStaticFiles();

app.UseAuthentication();

A segunda linha é fundamental. Veja abaixo onde encaixar essas 2 linhas:

Incluindo app.UseStaticFiles() e app.UseAuthentication(); no arquivo startup.cs
public class Startup
{

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseAuthentication();
        app.UseMvcWithDefaultRoute();
    }
}

Agora você pode rodar sua aplicação localmente e fazer os ajustes que quiser. Os 2 artigos da referência dão muitas dicas, inclusive para implementar o serviço de e-mail e alterar páginas Razor.

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

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

modelo de dados ( //Troque a palavra Coaching pelo nome da sua aplica&amp;ccedil;&amp;atilde;o, por exemplo, AplicacaoTeste))

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

arquivo de contexto ContextoPrimario.cs (coloque os dados da SUA conex&amp;atilde;o com SEU banco de dados)

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

conteudo do arquivo de migra&amp;ccedil;&amp;atilde;o (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

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:

 

 

 

 

a view de cria&amp;ccedil;&amp;atilde;o original create.cshtml

É 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.

 

a view de cria&amp;ccedil;&amp;atilde;o modificada create.cshtml

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:

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):

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,

 

formul&amp;aacute;rio de intera&amp;ccedil;&amp;atilde;o MailingBoss (pegar o primeiro formul&amp;aacute;rio e n&amp;atilde;o o do iframe)

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:

post CREATE do controlador EmpreendedoresController.cs

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).

NOVO post CREATE do controlador EmpreendedoresController.cs

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″)

Solicitei o cadastramento com email alcides2@mailinator.com.

O email entrou:

Listando os contatos do MailingBoss, cuja lista estava vazia:

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:

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

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

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

Voltando aos contatos do MailingBoss, vemos que agora o e-mail está com status 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:

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.

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

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:

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

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,

 

arquivo de detalhes Details.cshtml

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 

 

detlahes do controlador EmpreendedorController.cs
detalhes do da procedure que insere a pessoa na aplica&amp;ccedil;&amp;atilde;o e gera a autoavalia&amp;ccedil;&amp;atilde;o

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.

arquivo Detail_confirma_emails.cshtml
mudan&amp;ccedil;as no controlador EmpreendedoresController.cs

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

O e-mail foi enviado para o Mailinator.com

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

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

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

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

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.

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

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:

Configure Services
 public void ConfigureServices(IServiceCollection services)
 {
         //-seu conteúdo atual

        //-inclua DEPOIS de sservices.AddMvc();
   
        //detalhes em https://stackoverflow.com/questions/31942037/how-to-enable-cors-in-asp-net-core
            services.AddCors(options =>
            {
                options.AddPolicy("MinhaPolitica",
                    builder => builder.AllowAnyOrigin()
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials());
            });
}

Configurando o serviço globalmente

Depois disso é preciso configurar o serviço. :

Configura&amp;ccedil;&amp;atilde;o Global
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, Alicecapella119Context context)
{
//- seu conteúdo atual
// - inclua se não quiser definir isso em todos os controladores que vão receber ou fornecer dados para os selects (ou outras finalidades)
//https://weblog.west-wind.com/posts/2016/Sep/26/ASPNET-Core-and-CORS-Gotchas
 // global policy - assign here or on each controller
 app.UseCors("MinhaPolitica");
}

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&amp;ccedil;&amp;atilde;o do Get no Action do Controller
 //para json
        [HttpGet]
        [EnableCors("MinhaPolitica")] // AQUI!!!
        public JsonResult PegaCompetencias(int Id)
        {

            List<SelectListItem>Lista = new List<SelectListItem>();
            var Dados = _context.CompetenciasBancoGeral
                    .Where(r => r.IdConsultoria == Id)
                    .OrderBy(r => r.TituloCompetencia)
                    .Select(r => new
                     { r.IdCompetenciaBanco, r.TituloCompetencia });
            foreach (var Linha in Dados)
            {
                Lista.Add(new SelectListItem()
                {
                    Value = Linha.IdCompetenciaBanco.ToString(),
                    Text = Linha.TituloCompetencia,
                    Selected = false
                });
            }
            return Json(Lista);
        }

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:

Configura&amp;ccedil;&amp;otilde;es para Desenvolvimento e Produ&amp;ccedil;&amp;atilde;o
if (env.IsDevelopment())
            {
                 services.AddCors(options =>
                 {
                   options.AddPolicy("MinhaPoliticaLocal",
                    builder => builder.AllowAnyOrigin("localhost")
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials());
                 })
            }
            else
            {
                aservices.AddCors(options =>
                 {
                   options.AddPolicy("MinhaPoliticaRemota",
                    builder => builder.AllowAnyOrigin("meudominio.com.br")
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials());
                 })
            
            }

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()“:

Jquery change()
$(document).ready(function() {
	$('#SelectConsultoria').change(function() {
		// aqui vai acontecer o recarregamento dos dados do 
                //select ESCRAVO

		console.log("clicou select consultoria e mudou");

		}
	});

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> }
Todas rotinas Jquery
$(document).ready(function() {
	$('#SelectConsultoria').change(function() {
		console.log('@Context.Request.Host');
		console.log("clicou select consultoria e mudou");
        //limpa seletc ESCRAVO
		$('#SelectCompetencia').empty();
		console.log("passou por remoção da lista");
		
		//Guarda opção que foi selecionada no Select MESTRE
		var OpcaoAtual = $(this).val();
		$('#SelectCompetencia').append($('<option></option>').val("0").html('Selecione uma Competência'));
		var x = document.getElementById("SelectCompetencia");
		var option = document.createElement("option");
		
		//Adiciona uma opção tipo Slecione um Valor
		//option.value = "0"
		x.add(option);
		
		//Mancada para forçar redraw - pode não ser necessário
		var element = document.getElementById('SelectCompetencia');
		var n = document.createTextNode(' ');
		var disp = element.style.display; // don't worry about previous display style
		element.appendChild(n);
		element.style.display = 'none';
		setTimeout(function() {
			element.style.display = disp;
			n.parentNode.removeChild(n);
		}, 20); // you can play with this timeout to make it as short as possible
        //fim do redraw - este bloco pode ser retirado

        //para conferir a opção select MESTRE selecionada
		console.log("Opcao Atual" + OpcaoAtual);
		
		//monta URL completa, com parâmetro
		var url = 'http://' + '@Context.Request.Host/PerguntasBancoGeral/PegaCompetencias/' + OpcaoAtual;
		//verifica url montada
		console.log(url);
		console.log('idConsultoria = ' + $('#SelectConsultoria').val());
        
        //chama AJAX
		$.ajax({
			type: 'GET',
			headers: {
				'Accept': 'application/json',
				'Content-Type': 'text/plain'
			},
			dataType: 'json',
			url: url,
			success: function(data) {
				console.log(data);
				callbackFunction(data); //IMPORTANTE
			}
		});

        //Carrega o select ESCRAVO
		function callbackFunction(resultData) {
			// alert("entrei");
			var items = '<option value="0"><<Selecione uma Competência>></option>';
			//console.log(resultData);
			$.each(resultData, function(i, competencia) {

				items += "<option value='" + competencia.value + "'>" + competencia.text + "</option>";
				$("#SelectCompetencia").append("<option value='" + competencia.value + "'>" + competencia.text + "</option>");
				//confere no console
				console.log("<option value='" + competencia.value + "'>" + competencia.text + "</option>");

			});
			//confere no console
			console.log(items);
			//truque para tirar linha em branco
			$('#SelectCompetencia option:eq(' + 1 + ')').remove(); //retira branco
			
		}
	});

    //função auxiliar REDRAW
    //https://gist.github.com/mgHenry/6154611
	jQuery.fn.redraw = function() {
		jQuery(this).each(function() {
			this.style.display = 'none';
			this.offsetHeight; // no need to store this anywhere, the reference is enough
			this.style.display = 'block';
		});
	};
});

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 A&amp;ccedil;&amp;atilde;o GET no Controlador
//para json
        [HttpGet]
        [EnableCors("MinhaPolitica")]
        public JsonResult PegaCompetencias(int Id)
        {

            List<SelectListItem> Lista = new List<SelectListItem>();
            var Dados = _context.CompetenciasBancoGeral
                    .Where(r => r.IdConsultoria == Id)
                    .OrderBy(r => r.TituloCompetencia)
                    .Select(r => new
                    { r.IdCompetenciaBanco, r.TituloCompetencia });
            foreach (var Linha in Dados)
            {
                Lista.Add(new SelectListItem()
                {
                    Value = Linha.IdCompetenciaBanco.ToString(),
                    Text = Linha.TituloCompetencia,
                    Selected = false
                });
            }
            return Json(Lista);
        }
Configura&amp;ccedil;&amp;otilde;es para Desenvolvimento e Produ&amp;ccedil;&amp;atilde;o
if (env.IsDevelopment())
            {
                 services.AddCors(options =>
                 {
                   options.AddPolicy("MinhaPoliticaLocal",
                    builder => builder.AllowAnyOrigin("localhost")
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials());
                 })
            }
            else
            {
                aservices.AddCors(options =>
                 {
                   options.AddPolicy("MinhaPoliticaRemota",
                    builder => builder.AllowAnyOrigin("meudominio.com.br")
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials());
                 })
            
            }

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

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.

C&amp;oacute;digo da View: Formul&amp;aacute;rio (Form) - P&amp;aacute;gina Index.cshtml / Action Index no Controller
<form asp-action="Index2">
    <h2>Filtra Lista de Perguntas por Consultoria e por Competência</h2>
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    <div class="form-group">
        <label asp-for="IdConsultoria" class="col-md-2 control-label">Consultoria</label>
        <div class="col-md-10">
            <select id="SelectConsultoria" asp-for="IdConsultoria" asp-items="ViewBag.Consultoria" class="form-control seletor"></select>
            <span asp-validation-for="IdConsultoria" class="text-danger"></span>
        </div>
    </div>
    <br /><br />
    <div class="form-group">
        <label asp-for="IdCompetenciaBanco" asp-items="" class="col-md-2 control-label">Competência</label>
        <div class="col-md-10" id="Competencias">
            <select asp-items="ViewBag.Competencia" id="SelectCompetencia" asp-for="IdCompetenciaBanco" class="form-control seletor"></select>
            <span asp-validation-for="IdCompetenciaBanco" class="text-danger"></span>
        </div>
    </div>
    <br /><br />
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10" align="left">
            <table>
                <tbody>
                    <tr>
                        <td><input type="submit" value="Filtra Perguntas" class="btn btn-primary" /> </td>
                        <td> </td>
                        <td><a asp-action="Index" class="btn btn-danger">Limpa Escolhas</a> </td>
                    </tr>
                </tbody>
            </table>
              <br />
        </div>
    </div>
</form>
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.

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!!!).

Index.cshtml - Bloco de Informa&amp;ccedil;&amp;otilde;es atrav&amp;eacute;s de ViewBags
<div class="row">
   <div class="col-sm-3">
      <div class="alert-danger" style="position:absolute; position:absolute;top:0;right:0;bottom:0">
         @ViewBag.MensagemAviso
         <span class="glyphicon glyphicon-alert"></span>
      </div>
   </div>
   <div class="col-sm-3">
      <div class="alert-info">
         Total de Consultorias : 
         <h3>@ViewBag.TotalDeConsultorias</h3>
      </div>
   </div>
   <div class="col-sm-3">
      <div class="alert-info">
         Total de Competências : 
         <h3>@ViewBag.TotalDeCompetencias</h3>
      </div>
   </div>
   <div class="col-sm-3">
      <div class="alert-info">
         Total de Perguntas: 
         <h3>@ViewBag.TotalDePerguntas</h3>
      </div>
   </div>
</div>
</div>

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.

O modelo das Perguntas é muito simples:

 

Modelo das Perguntas (Model/Perguntas.cs)
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace Coaching.Models
{
    public partial class PerguntasBancoGeral
    {
        [Key]
        [Display(Name = "Id da Pergunta do Banco")]
        public int IdPerguntaBanco { get; set; }

        [Required(ErrorMessage = "Favor escolher uma Consultoria.")]
        [Display(Name = "id da Consultoria")]
        public int IdConsultoria { get; set; }

        [Required(ErrorMessage = "Favor escolher uma Competência.")]
        [Display(Name = "Id da Competência do Banco")]
        public int IdCompetenciaBanco { get; set; }

        [Display(Name = "Assertiva Genérica (Hetero)")]
        public string Assertivageral { get; set; }

        [Display(Name = "Assertiva Auto")]
        public string Assertivaauto { get; set; }
    }
}
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).

Modelo das Perguntas Na Grid (Model/PerguntasComNomes.cs) - Modelo "FAKE"
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;

namespace Coaching.Models
{
    public class PerguntasBancoGeralComNome //somente para visualização
    {
        [Key]
        [Display(Name = "Id da Pergunta do Banco")]
        public int IdPerguntaBanco { get; set; }

        [Display(Name = "id da Consultoria")]
        public int IdConsultoria { get; set; }

        [Display(Name = "Id da Competência do Banco")]
        public int IdCompetenciaBanco { get; set; }

        [Display(Name = "Assertiva Genérica (Hetero)")]
        public string Assertivageral { get; set; }

        [Display(Name = "Assertiva Auto")]
        public string Assertivaauto { get; set; }

        [Display(Name = "Nome da Consultoria")]
        public string NomeConsultoria { get; set; }

        [Display(Name = "Nome da Competência")]
        public string TituloCompetencia { get; set; }
    }
}
HTML da Grid - Bloco 3 - Index.cshtml - Modelo "FAKE" com Nomes (IEnumerable)
<div class="row">
   <div class="col-sm-12">
      <p>
         <a asp-action="Create" asp-route-idConsultoria="@ViewBag.idConsultoria" asp-route-IdCompetenciaBanco="@ViewBag.IdCompetenciaBanco" class="btn btn-primary">Cria Nova Pergunta</a>
      </p>
      <h2>@ViewBag.MensagemTotal</h2>
      @*10 últimas perguntas cadastradas<*@
      <table class="table table-responsive table-hover">
         <thead>
            <tr>
               <th>
                  @Html.DisplayNameFor(model => model.Assertivageral)
               </th>
               <th>
                  @Html.DisplayNameFor(model => model.Assertivaauto)
               </th>
               <th>
                  @*@Html.DisplayNameFor(model => model.IdCompetenciaBanco)*@
                  Id/Competências
               </th>
               <th>
                  Id/Consultoria
               </th>
               <th><b>Ação</b></th>
            </tr>
         </thead>
         <tbody>
            @{
            foreach (var item in ViewBag.Lista as IEnumerable
            <Coaching.Models.PerguntasBancoGeralComNome>
            )
            {
            <tr>
               <td>@item.Assertivageral</td>
               <td>@item.Assertivaauto</td>
               <td>
                  @item.IdCompetenciaBanco/@item.TituloCompetencia
               </td>
               <td>@item.IdConsultoria/@item.NomeConsultoria</td>
               <td>
                  <a asp-action="Edit" asp-route-id="@item.IdPerguntaBanco">Edita</a> |
                  <a asp-action="Details" asp-route-id="@item.IdPerguntaBanco">Detalhes</a> |
                  <a asp-action="Delete" asp-route-id="@item.IdPerguntaBanco">Apaga</a>
               </td>
            </tr>
            }
            }
         </tbody>
      </table>
   </div>
</div>

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.

M&amp;eacute;todo Privado para Popular Consultorias - arquivo controlador (controller)
private void PopulateConsultoria(int TipoSelecionado = 0)
        {
            List<SelectListItem> Lista = new List<SelectListItem>();
            if (TipoSelecionado == 0)
            {
                Lista.Add(new SelectListItem()
                { Value = "0", Text = "<<Selecione uma Consultoria>>", Selected = true });
            }
else
                  { Value = "0", Text = "<<Selecione uma Consultoria>>", Selected = false});
            }
            var Dados = _context.Consultorias
                .OrderBy(r => r.NomeConsultoria)
                .Select(r => new
                { r.IdConsultoria, r.NomeConsultoria });
            foreach (var Linha in Dados)
            {
                bool valor = false;
                if (Linha.IdConsultoria == TipoSelecionado)
                {
                    valor = true;
                }
                Lista.Add(new SelectListItem()
                {
                    Value = Linha.IdConsultoria.ToString(),
                    Text = Linha.NomeConsultoria,
                    Selected = valor
                });
            }
            ViewBag.Consultoria = Lista;
        }
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.

M&amp;eacute;todo Privado para Popular Compet&amp;ecirc;ncias - arquivo controlador (controller)
private void PopulateCompetencia(int IdCompetenciaLida = 0, int idConsultoriaLido = 0)
        {
            List<SelectListItem> Lista = new List<SelectListItem>();
            var Comp = 0;
            var Cons = 0;
            if (IdCompetenciaLida >= 1)
            {
                Comp = 1;
            }
            if (idConsultoriaLido >= 1)
            {
                Cons = 1;
            }
            if (IdCompetenciaLida == 0)
            {
                Lista.Add(new SelectListItem()
                { Value = "0", Text = "<<Selecione uma Competência>>", Selected = true });
            }
            var Dados = (from i in _context.CompetenciasBancoGeral
                             //join k in _context.Consultorias on i.IdConsultoria equals k.IdConsultoria
                         where (Cons == 1 && i.IdConsultoria == idConsultoriaLido) || (Comp == 1 && i.IdCompetenciaBanco == IdCompetenciaLida)
                         || (Comp == 0 && Cons == 0)
                         select new CompetenciasBancoGeral
                         {
                             IdCompetenciaBanco = i.IdCompetenciaBanco,
                             TituloCompetencia = i.TituloCompetencia
                         })
                         .OrderBy(p => p.TituloCompetencia)
                         .ToList();
            foreach (var Linha in Dados)
            {
                bool valor = false;
                if (Linha.IdCompetenciaBanco == IdCompetenciaLida)
                {
                    valor = true;
                }
                Lista.Add(new SelectListItem()
                {
                    Value = Linha.IdCompetenciaBanco.ToString(),
                    Text = Linha.TituloCompetencia,
                    Selected = valor
                });
            }
            ViewBag.Competencia = Lista;
        }

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.

C&amp;oacute;digo da A&amp;ccedil;&amp;atilde;o Index do Controlador (controller)
        // GET: PerguntasBancoGeral
        public async Task<IActionResult> Index(int idConsultoria = 0, int IdCompetenciaBanco = 0)
        {
            //id1 -id Consultoria
            //id2 - id Competencia
            var id1 = 0;
            var id2 = 0;
            var pConsultoria = 0;
            var pCompetencia = 0;
            if (idConsultoria >= 1)
            {
                id1 = 1;
                pConsultoria = idConsultoria;
            }
            if (IdCompetenciaBanco >= 1)
            {
                id2 = 1;
                pCompetencia = IdCompetenciaBanco;
            }
            PopulateConsultoria(pConsultoria);
            PopulateCompetencia(pCompetencia, pConsultoria);
            ViewBag.Lista = await (from i in _context.PerguntasBancoGeral
                                   join k in _context.Consultorias on i.IdConsultoria equals k.IdConsultoria
                                   join m in _context.CompetenciasBancoGeral on i.IdCompetenciaBanco equals m.IdCompetenciaBanco
                                   where (
                                   (id1 == 0 && id2 == 0)
                                   ||
                                   (id1 == 0 && id2 == 1 && i.IdCompetenciaBanco == pCompetencia)
                                   ||
                                   (id1 == 1 && id2 == 0 && i.IdConsultoria == pConsultoria)
                                   ||
                                   (id1 == 1 && id2 == 1 && i.IdConsultoria == pConsultoria && i.IdCompetenciaBanco == pCompetencia)
                                   )
                                   select new PerguntasBancoGeralComNome
                                   {
                                       Assertivaauto = i.Assertivaauto,
                                       Assertivageral = i.Assertivageral,
                                       IdCompetenciaBanco = i.IdCompetenciaBanco,
                                       IdPerguntaBanco = i.IdPerguntaBanco,
                                       IdConsultoria = k.IdConsultoria,
                                       NomeConsultoria = k.NomeConsultoria,
                                       TituloCompetencia = m.TituloCompetencia
                                   })
                                   .OrderByDescending(p => p.IdPerguntaBanco)
                                   .Take(10) //pega as 10 últimas
                                   .ToListAsync();
            if (pConsultoria == 0 && pCompetencia == 0)
            {
                ViewBag.MensagemAviso = "Ainda sem filtragem";
                ViewBag.TotalDeConsultorias = await _context.Consultorias.CountAsync();
                ViewBag.TotalDeCompetencias = await _context.CompetenciasBancoGeral.CountAsync();
                ViewBag.TotalDePerguntas = await _context.PerguntasBancoGeral.CountAsync();
               
            }
            else if (pConsultoria >= 1 && pCompetencia == 0)
            {
                ViewBag.MensagemAviso = "Consultoria Definida";
                ViewBag.TotalDeConsultorias = await _context.Consultorias.Where(x => x.IdConsultoria == pConsultoria).CountAsync();
                ViewBag.TotalDeCompetencias = await _context.CompetenciasBancoGeral.Where(x => x.IdConsultoria == pConsultoria).CountAsync();
                ViewBag.TotalDePerguntas = await _context.PerguntasBancoGeral.Where(x => x.IdConsultoria == pConsultoria).CountAsync();
            }
            else if (pConsultoria >= 1 && pCompetencia >= 1)
            {
                ViewBag.MensagemAviso = "Consultoria Definida e Competência Definida: Clique em CRIA NOVA PERGUNTA";
                ViewBag.TotalDeConsultorias = await _context.Consultorias.Where(x => x.IdConsultoria == pConsultoria).CountAsync();
                ViewBag.TotalDeCompetencias = await _context.CompetenciasBancoGeral.Where(x => x.IdConsultoria == pConsultoria && x.IdCompetenciaBanco == pCompetencia).CountAsync();
                ViewBag.TotalDePerguntas = await _context.PerguntasBancoGeral.Where(x => x.IdConsultoria == pConsultoria && x.IdCompetenciaBanco == pCompetencia).CountAsync();
            }
            else // else if (pConsultoria = 0 1 && pCompetencia >= 1)
            {
                ViewBag.MensagemAviso = "Competência Definida: falta definir consultoria";
                ViewBag.TotalDeConsultorias = await _context.Consultorias.CountAsync();
                ViewBag.TotalDeCompetencias = await _context.CompetenciasBancoGeral.Where(x => x.IdCompetenciaBanco == pCompetencia).CountAsync();
                ViewBag.TotalDePerguntas = await _context.PerguntasBancoGeral.Where(x => x.IdCompetenciaBanco == pCompetencia).CountAsync();
            }
            if (ViewBag.TotalDePerguntas >= 10) { ViewBag.MensagemTotal = "10 últimas perguntas cadastradas"; }
            else if (ViewBag.TotalDePerguntas >= 1 && ViewBag.TotalDePerguntas <= 9) { ViewBag.MensagemTotal = Convert.ToString(ViewBag.TotalDePerguntas) + " últimas perguntas cadastradas"; }
            else { ViewBag.MensagemTotal = "Nenhuma pergunta cadastrada ainda (nesta filtragem)"; }
            ViewBag.idConsultoria = idConsultoria;
            ViewBag.IdCompetenciaBanco = IdCompetenciaBanco;
            return View();
        }

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.