Corrigindo o problema do "Server Application Unavailable"

Esses dias me deparei com esse problema. Bem, há diversas causas que podem levar a isso e algumas soluções podem ser facilmente encontradas pela net mostrando problemas:
E inúmeros artigos/helps contendo outras soluções/causas... Mas também há um caso devido ao Application PoolResumo: o Application Pool gerencia a memória que é utilizada pelas aplicações no IIS. Não vou entrar em detalhes e serei bem direto na solução.


Bem, se você deu de cara com o problema do Server Application Unavailable e já não sabe mais o que fazer (já criou e recriou inúmeras vezes o site e deu na mesma), verifique todas as configurações (novamente), veja se o Application Pool trabalhado está ativo e funcionando sobre a mesma versão do Framework .NET da aplicação. Revise também a sua configuração ou crie um novo e altere na configuração do Web Site para o que foi criado. Isso deve resolver o problema...

Redirecionando o endereço para SSL no Global.asax

Post rápido para redirecionar seu endereço caso um usuário, ao digitar http://thiagomarcal.blogspot.com/, seja redirecionado automaticamente para https://thiagomarcal.blogspot.com/. Adicione um arquivo Global.asax e aplique o seguinte método:

protected void Application_BeginRequest(Object sender, EventArgs e)
{
     if (!Request.IsSecureConnection)
     {
          string endereco = string.Format("https{0}", Request.Url.AbsoluteUri.Substring(4));
          Response.Redirect(endereco);
     }
}

Ler código de barras através de um leitor óptico com ASP.NET

Não há nenhum segredo ou driver específico para realizar a leitura. Basicamente o leitor óptico lê o código de barras, converte para string e retorna para o buffer de entrada do computador. Ou seja, é como se fosse digitar no teclado! Acabou o mistério...
Há várias formas de codificação do código de barras e quem é responsável por essa codificação/decoficação pode ser tanto a leitora quanto a aplicação. Irei considerar em nosso exemplo que o que a leitora lê e irei utilizar não me importando o que seja. Supondo e para fins ilustrativos que nosso código de barras seja essa aí de cima, o valor de entrada seria 3805565154 (frisando novamente, apenas supondo). A leitora irá passar para nossa aplicação a seguinte entrada 3805565154 ENTER. A maioria das leitoras já facilitam sua vida forçando o ENTER ao terminar de ler o conteúdo. Então fica fácil! Vamos por a mão na massa.
Crie uma tela com apenas um TextBox. Criei essa bonitinha aqui:

Selecione o TextBox, vá na guia Properties e clique no ícone de Events. Adicione um evento em KeyDown conforme a figura abaixo:

Com isso, ao digitar qualquer valor no TextBox, irá disparar esse evento. Mas como queremos ler apenas o valor completo, adicionamos uma determinada condição para começar a analisar apenas quando pressionar/entrar o ENTER, logo nosso código ficaria da seguinte forma:

private void txtNumero_KeyDown(object sender, KeyEventArgs e)
{
        if (e.KeyCode == Keys.Enter)
        {
                string entrada = txtNumero.Text;
                txtNumero.Text = "";
        }
}
 
Ou seja, a cada entrada de caracteres, ativo o evento mas só iniciarei a análise quando a entrada for um ENTER. Ao final, zero o conteúdo do TextBox para que possa dar uma nova entrada. Daí você pode adicionar outras instruções no meio para fazer o que quiser. Eu complementei a aplicação fazendo uma consulta em um banco de dados e analisando se existe ou não um determinado cupom. Vamos aos testes...
A leitora que usei é plugada diretamente na entrada PS/2 do teclado. A depender de sua leitora pode ser na USB, na PS/2 ou utilizar algum adaptador específico. Depois de conectado, inicie sua aplicação. Posicione o leitor sobre o código de barras e pressione o botão para realizar a leitura e voilá... Sua aplicação irá ler a entrada e realizar o processo que desejar.
Minha aplicação leu um cupom cujo número codificado era 1000909. Bem fácil e útil... 
Quem quiser saber mais informações como tipos de codificações, como gerar/codificar/decodificar strings para código de barras no Access, acessem esse artigo da MSDN que é bem interessante e explicativo.

ASP.NET com Banco Firebird

Semelhantemente com o post do MySQL, irei demonstrar como conectar a um banco Firebird usando o Framework (DLL) que eles disponilizam e que pode ser obtido aqui. Vou resumir o processo pois é bem parecido, daí é só dar uma olhadinha rápida no post anterior.
Basicamente incorpore a DLL no projeto e crie uma nova classe chamada Firebird. Adicione o namespace FirebirdSql.Data.FirebirdClient e adicione o seguinte código para consulta:

FbConnection conexao = new FbConnection("String Conexão Firebird");
FbCommand comando = new FbCommand();
FbDataReader datareader = null;
try
{
    conexao.Open();
    try
    {
        FbTransaction transacao = conexao.BeginTransaction();
        comando.Connection = conexao;
        comando.CommandText = "SELECT * FROM Tabela";
        datareader = comando.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
        transacao.Commit();
    }
    catch (FbException erro) { }
}
catch (FbException erro) { }

Bem semelhante... nada de novo! Daí pode-se gerar os demais métodos de conexão a partir desse. 
Vi em alguns tópicos pela NET que o pessoal encontra alguns problemas ao tentar conectar ao Firebird. O principal erro é: "Your user name and password are not defined. Ask your database administrator to set up a Firebird login."
Novamente, vi soluções muito loucas e esdrúxulas mas que foram tentativas de ajudar e de boa vontade do pessoal. Bem, mesmo que seu login e senha estejam certos (e tenha certeza disso antes de reclamar), o Firebird, em comunicação com a DLL, não dão muita transparência e ajuda ao usuário. O problema, basicamente, é a string de conexão. Basta usar essa:
User=SYSDBA; Password=masterkey; Database=C:\Bancos\Firebird\Thiago.fdb; DataSource=localhost; Port=3050; Dialect=3; Charset=NONE; Role=; Connection lifetime=15; Pooling=true; MinPoolSize=0; MaxPoolSize=50; Packet Size=8192; ServerType=0;
Ou

User=SYSDBA; Password=masterkey; Database=C:\Bancos\Firebird\Thiago.fdb; Server=localhost;

Problema resolvido!

Processo de Assinatura de Rps/Lote - Nota Fiscal Eletrônica

Lendo aquele bolo de especificação da ABRASF muitos devem perguntar: "Certo, sei quais campos colocar no XML mas qual a ordem do processo de assinatura?". A documentação não fica claro realmente como e qual a melhor forma de assinar. Realize a seguinte ordem:
  1. Gere o XML da Rps e assine;
  2. Se for enviar mais de uma Rps em um mesmo lote, repita o passo 1 para cada Rps;
  3. Anexe todas as Rps já assinadas no XML do Lote;
  4. Assine o Lote e faça o envio.
Seu XML de envio deve ficar semelhante à figura abaixo:


Para fazer a assinatura dos XMLs, utilize a função abaixo:

private XmlDocument AplicaAssinatura(string xml, string uri)
{
try
{
// Obtem o certificado
X509Certificate2 X509Cert = ObtemCertificado();
// Cria um documento XML para carregar o XML
XmlDocument docXML = new XmlDocument();
docXML.PreserveWhitespace = true;
// Carrega o documento XML
docXML.LoadXml(xml);
// Cria o objeto XML assinado
SignedXml signedXml = new SignedXml(docXML);
// Assina com a chave privada
signedXml.SigningKey = X509Cert.PrivateKey;
// Atribui o método de canonização
signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
// Atribui o método para assinatura
signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
// Cria a referencia
Reference reference = new Reference();
// Pega a URI para ser assinada
XmlAttributeCollection _Uri = docXML.GetElementsByTagName(uri).Item(0).Attributes;
foreach (XmlAttribute _atributo in _Uri)
{
if (_atributo.Name == "id")
reference.Uri = "#" + _atributo.InnerText;
}
// Adiciona o envelope à referência
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
reference.AddTransform(env);
// Atribui o método do Hash
reference.DigestMethod = "http://www.w3.org/2000/09/xmldsig#sha1";
// Adiciona a referencia ao XML assinado
signedXml.AddReference(reference);
// Cria o objeto keyInfo
KeyInfo keyInfo = new KeyInfo();
// Carrega a informação da KeyInfo
KeyInfoClause rsaKeyVal = new RSAKeyValue((System.Security.Cryptography.RSA)X509Cert.PrivateKey);
KeyInfoX509Data x509Data = new KeyInfoX509Data(X509Cert);
x509Data.AddSubjectName(X509Cert.SubjectName.Name.ToString());
keyInfo.AddClause(x509Data);
keyInfo.AddClause(rsaKeyVal);
// Adiciona a KeyInfo
signedXml.KeyInfo = keyInfo;
// Atribui uma ID à assinatura
signedXml.Signature.Id = "Assigned" + uri;
// Efetiva a assinatura
signedXml.ComputeSignature();
// Obtem o XML assinado
XmlElement xmlDigitalSignature = signedXml.GetXml();
// Adiciona o elemento assinado ao XML
docXML.DocumentElement.AppendChild(docXML.ImportNode(xmlDigitalSignature, true));
// Retorna o XML
return docXML;
}
catch (Exception erro) { throw erro; }
}

O parâmetro URI é o nome da tag geral que deve ser assinada de um XML, no caso deverá assinar as tags InfRps (cada uma) e LoteRps. A função ObtemCertificado já foi exibida aqui em um tópico anterior que serve para captura do certificado digital.

Criar RSS

Depois de muito tempo voltei a escrever alguns artigos. Pouco tempo disponível e algumas prioridades... Bem, esse tópico, apesar de simples, é de bem ajuda para que desejar gerar RSS dinamicamente. O código é bem simples: apenas Response!
Crie uma página que irá exibir o RSS e coloque o seguinte código no Page_Load:

StringBuilder rss = new StringBuilder();
rss.AppendLine("<rss version="2.0">");
rss.AppendLine("<channel>");
rss.AppendLine("<title>Thiago Marçal</title>");
rss.AppendLine("<link>http://thiagomarcal.blogspot.com/</link>");
rss.AppendLine("<description>Blog que compartilha idéias e código-fontes à comunidade.</description>");
rss.AppendLine("<language>pt-br</language>");
SqlDataReader dr = consulta("SELECT * ");
while (dr.Read())
{
        rss.AppendLine("<item>");
        rss.AppendLine("<title>" + Funcoes.RemoveHTML(Server.HtmlDecode(dr["titulo"].ToString())) + "</title>");
        rss.AppendLine("<description>" + Funcoes.RemoveHTML(Server.HtmlDecode(dr["texto"].ToString())) + "</description>");
        rss.AppendLine("<link>" + dr["link"].ToString() + "</link>");
        rss.AppendLine("<pubDate>" + DateTime.Parse(dr["data"].ToString()).ToString("dd/MM/yyyy HH:mm:ss") + "</pubDate>");
        rss.AppendLine("</item>");
}
rss.AppendLine("</channel>");
rss.AppendLine("</rss>");
dr.Close();
Response.ContentType = "text/xml";
Response.Write(rss.ToString());

Bem simples, não? O método Funcoes.RemoveHTML remove qualquer tag HTML que contenha na string. Eu criei uma bem simples baseada em expressão regular. Você pode criar a seu gosto...

Multi-Upload com ASP.NET usando JQuery e escrita na sessão do Handler

Apesar do título grande e, para uns, confuso, nesse tópico irei ajudar muitos a resolver um problema: múltiplos uploads de vez sem precisar selecionar cada arquivo.



A idéia primordial é fazer com que o usuário possa clicar em um botão, selecionar vários arquivos de vez (que nem o GMail) e fazer o envio para uma pasta desejada. Para isso iremos utilizar um Handler em C# para fazer o upload. Mas nesse mesmo Handler irei escrever na sessão para que possa utilizar em outra página. Lembrando que as variáveis de sessão, para usar dentro de um Handler precisa-se de um artifício. Sem mais delongas, vamos à solução.

Crie ou abra um Web Site Project e nele adicione um Generic Handler. Coloque com o nome Upload.ashx para ficar mais fácil de encontrá-lo. Insira o seguinte cógido:

using System;
using System.Data;
using System.IO;
using System.Web;
using System.Web.SessionState;

public class Upload : IHttpHandler, IRequiresSessionState{

        public void ProcessRequest (HttpContext context) {
                context.Response.ContentType = "text/plain";
                context.Response.Expires = -1;
                try
                {
                        // Obtêm o arquivo a ser postado
                        HttpPostedFile postedFile = context.Request.Files["filedata"];
                        // Caminho
                        string path = context.Server.MapPath("~/Upload/");
                        string filename = postedFile.FileName;
                        // Cria diretório de não existir
                        if (!Directory.Exists(path))
                                Directory.CreateDirectory(path);
                        // Verifica se o arquivo já existe com mesmo nome
                        string nome = filename.Substring(0, filename.LastIndexOf("."));
                        string extensao = filename.Substring(filename.LastIndexOf(".") + 1);
                        int i = 1;
                        // Verifica se existe, senão renomeia
                        while (File.Exists(path + @"\" + filename))
                        {
                                filename = nome + "(" + i + ")." + extensao;
                                i++;
                        }
                        // Adiciona na sessão
                        DataTable galeria = (DataTable)context.Session["galeria"];
                        DataRow item = galeria.NewRow();
                        item["imagem"] = filename;
                        galeria.Rows.Add(item);
                        context.Session["galeria"] = galeria;
                        // Salva arquivo
                        postedFile.SaveAs(path + @"\" + filename);
                        // Retorna o arquivo salvo
                        context.Response.Write(path + "/" + filename);
                        context.Response.StatusCode = 200;
                }
                catch (Exception ex)
                {
                        context.Response.Write("Erro: " + ex.Message);
                }
        }


        public bool IsReusable {
                get {
                       return true;
                }
        }

}
 
Pronto! Já temos nosso Handler que faz o upload de um arquivo e o adiciona em um DataTable. Esse DataTable já estava previamente adicionado na sessão. Você pode utilizar quaisquer outra estrutura para armazenar. A dica da escrita na sessão no Handler está na herança da interface IRequiresSessionState onde permitirá fazer isso.
 
Crie uma página ASPX e referencie os JavaScripts do JQuery abaixo (os arquivos, caso não possuam, irei disponibilizar em um link abaixo) e um CSS pra dar um estilo legal:

<link rel="stylesheet" type="text/css" href="css/stilo.css" />
<script type="text/javascript" src="js/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="js/jquery.uploadify.js"></script>

Agora adicione um controle FileUpload e um link (ou um controle HyperLink). Iremos também fazer a chamada do JQuery para ser aplicado sobre o FileUpload ficando dessa forma:

<asp:FileUpload ID="Fotos" runat="server" />
<input type="button" onclick="javascript:$('#<%=Fotos.ClientID%>').fileUploadStart();" value="Enviar" />
<script type="text/javascript">
$(window).load(
       function() {
       $("#<%=Fotos.ClientID %>").fileUpload({
           'uploader': 'js/uploader.swf',
           'cancelImg': 'imagens/cancelar.png',
           'buttonText': 'Selecionar',
           'script': 'Upload.ashx',
           'folder': 'Upload',
           'fileDesc': 'Selecione arquivos do tipo imagem',
           'fileExt': '*.jpg;*.jpeg;*.gif;*.png',
           'multi': true,
           'auto': false
       });
      }
);
</script>

Agora rode a aplicação. Verá apenas um botão com o nome Selecionar. Ao clicar irá abrir um pop-up pedindo para selecionar os arquivos. Selecione um ou mais e clique em OK. Verá que será montado uma lista com os arquivos que irão ser enviados. Você poderá remover itens clicando no X sobre cada um deles. Ao clicar no link Enviar, será feito uma chamada para cada item sobre o Handler fazendo o envio para a pasta Upload e adicionando no DataTable armazenado na sessão.

Não vou entrar muito em datelhes, porque está bem fácil e compreensível o código. Se desejar, ao invés de clicar em Enviar para fazer o envio, você pode deixar automático (como se fosse uma trigger) mudando o parâmetro 'auto' para true da chamada JQuery. Então, ao selecionar e clicar em OK automaticamente será feito o upload. Se também quiser que o link faça um PostBack na página, basta colocar depois da chamada de início do upload, insira o __doPostBack('',''); colocando parâmetro ou não dependendo do que for fazer com ele após.

Nesse link aqui encontram-se alguns arquivos necessários para que possam utilizar. Não vou colocar os fontes ASP.NET porque senão já seria demais, né? Mãos à obra...

Obs: a fonte e outras matérias sobre o mesmo tópico você acha aí pela net. Não me lembro qual site eu havia visto algo do tipo e fiz essa. Mas fica a dica aí!

Obter Certificado Digital para Nota Fiscal Eletrônica

Post rápido sobre Nota Fiscal Eletrônica... Caso esteja desenvolvendo a aplicação de NFe para um PF e/ou PJ e esteja utilizando certificados do tipo A3 (no A1 deve servir também, mas ainda não testei), utilize a função abaixo para capturar da leitora o certificado. Os dados são armazenados no objeto para posterior manipulação, exemplo, assinatura do XML para envio.

using System.Security.Cryptography.X509Certificates;

X509Certificate2 oX509Cert = new X509Certificate2();
X509Store store = new X509Store("MY", StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates;
X509Certificate2Collection collection1 = (X509Certificate2Collection)collection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
X509Certificate2Collection collection2 = (X509Certificate2Collection)collection.Find(X509FindType.FindByKeyUsage, X509KeyUsageFlags.DigitalSignature, false);
X509Certificate2Collection scollection = (X509Certificate2Collection)collection2.Find(X509FindType.FindBySubjectName, "NOME DA EMPRESA \ PESSOA", false);
if (scollection.Count == 0)
    oX509Cert = null;
else
    oX509Cert = scollection[0];
 
No objeto oX509Cert é o objeto representativo de seu certificado digital no seu sistema. Geralmente esse tipo de certificado fica armazenado temporariamente na pasta Pessoal do Gerenciador de Certificados do Windows. Caso deseje visualizar qual o nome correto do certificado e realizar demais operações, vá em Iniciar, Executar e digite certmgr.msc. Uma tela como essa irá surgir:
 

Se o seu certificado não aparecer aí, deve ser que o leitor não está reconhecendo o cartão, ou o software de leitura não foi corretamente instalado e outros fatores que agora não entrará em detalhes.

SqlDataSource e MySQL

Estava fazendo uma integração de bases de dados MySQL e SQL Server e, em certa etapa, teria que utilizar o SqlDataSource para preencher um GridView alternando entre o SQL Server e o MySQL. Contudo, nativamente, o provider do SqlDataSource não aceita o MySQL a não ser por ODBC conforme especificação. Dei uma lida na net para ver alguns comentários e ví alguns absurdos como: "o SqlDataSource é específico do SQL Server" entre outros. Parece que o pessoal não lê as especificações. Como disse, dá sim, mas por ODBC. Mas usando o conector MySQL, conforme um post anterior, é possível realizar a integração do SqlDataSource a uma base MySQL sem ser por ODBC usando o conector específico. Vamos por a mão na massa...

Primeiramente vá no web.config e adicione as seguintes configurações:

<system.data>
<DbProviderFactories>
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description="Conector .NET para o MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.2.2.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
</DbProviderFactories>
</system.data>

Atente-se à versão da DLL do conector que está utilizando. No caso, estou usando a 6.2.2.0. Aplique também o Assembly inserindo a tag abaixo:

<add assembly="MySql.Data, Version=6.2.2.0, Culture=neutral, PublicKeyToken=C5687FC88969C44D" />

Como disse, não vou entrar em detalhes, mas adicionando essas configurações no web.config de seu projeto já estará apto para trabalhar adequadamente com o MySQL. Agora  o resto é simples, basta inserir o SqlDataSource e colocar o ProviderName="MySql.Data.MySqlClient".

Obs: Não coloque nenhum driver no ConnectionString do SqlDataSource como "Driver=MySQL ODBC 3.5;" ou suas variantes. Utilize a string de conexão convencional, pois o provider irá fornecer informações específicas para que o objeto possa se conectar normalmente.

ASP.NET com Banco MySQL

Todos viram aqui que meus posts dispensam muito embasamento teórico e parte logo para a prática. Gosto de ser direto... então, vamos lá! Para conectar a bancos MySQL podemos utilizar o ODBC do .NET para conectar e realizar consultas a ele. Contudo, esse tipo de conexão é mais lento e não usurfrui de conjunto de instruções específicas que podem otimizar as operações sobre o banco de dados, exemplo o namespace System.Data.SqlClient no qual possuem instruções específicas do SQL Server para manipulações dos dados. Assim sendo, a própria MySQL disponibilizou um conector específico para programação .NET. Para isso, pode realizar o download aqui.

Faça o download do instalador ou zip para obtermos as DLLs. Caso tenham dificuldade na obtenção dos arquivos, vejam nesse link aqui onde contêm apenas as DLLs básicas para o funcionamento. Com os arquivos em mãos, insira-os na pasta Bin do projeto ou dê um Add Reference para incorporá-las. Com as DLLs no seu devido local, as classes MySQL já podem ser acessadas quando adicionado o namespace MySql.Data.MySqlClient. Abaixo, temos um exemplo de realizar uma consulta no banco de dados. Nota-se que se assemelha bastanto com o SqlDataReader, OdbcDataReader, etc:

using MySql.Data.MySqlClient;

MySqlConnection conexao = new MySqlConnection("String Conexão MySQL");
MySqlCommand comando = new MySqlCommand();
MySqlDataReader datareader = null;
try
{
    conexao.Open();
    try
    {
        comando.Connection = conexao;
        comando.CommandText = "SELECT * FROM Tabela";
        datareader = comando.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
    }
    catch (MySqlException erro) { }
}
catch (MySqlException erro) { }

O objeto MySqlDataReader contêm os dados da consulta efetuada. Bem similiar com as outras formas de uso de conexão, então não há erro. Parameters, DataSet e muitos métodos que o conector fornece podem ser usados de forma similar aos demais conectores citados anteriormente. Fácil...

Então é isso! Caso tenham dificuldade na string de conexão para o MySQL veja no ConnectionStrings a mais adequada a sua necessidade.