sexta-feira, 16 de janeiro de 2015

Studying keywords await and async (ASP.NET C#)

Studying keywords await and async (ASP.NET C#)


To do a study about task based asynchronous calls in .NET 4.5 we are going to use a C# console application.

A single limitation: The main method, the entry point of the console application, can't be async. It's a .NET rule, not mine.

Please import System.Net and System.Threading.Tasks namespaces.

Case 1- Caller is not async and function called is async

Code:
        static void Main(string[] args)
        {
            Console.WriteLine("Main Begin");
            var t = DownloadAsync();
            Console.WriteLine("Main End");
            Console.ReadKey();
        }

        static async Task DownloadAsync()
        {
            Console.WriteLine("DownloadAsync Begin");
            using (var web = new System.Net.WebClient())
                Console.WriteLine((await web.DownloadStringTaskAsync("http://www.google.com/")).Substring(0, 50));
            Console.WriteLine("DownloadAsync End");
        }

Output:

Main Begin
DownloadAsync Begin
Main End
<!doctype html><html itemscope="" itemtype="http:/
DownloadAsync End

As you can see, the function runs asynchronously, Main method ends first, because of http comunication.  Let's use the wait method of Task, so the Main method waits for DownloadAsync.

Code:

        static void Main(string[] args)
        {
            Console.WriteLine("Main Begin");
            var t = DownloadAsync();
            Console.WriteLine("Calling Wait Method of Task");
            t.Wait();
            Console.WriteLine("Main End");
            Console.ReadKey();
        }

Output:

Main Begin
DownloadAsync Begin
Calling Wait Method of Task
<!doctype html><html itemscope="" itemtype="http:/
DownloadAsync End
Main End

The wait method of the Task, makes the Main method wait the asynchronous task to end.

Case 2- Caller is async and function called is async

Code:

        static void Main(string[] args)
        {
            var t = MainAsync();
            Console.ReadKey();
        }
     
        static async Task MainAsync()
        {
            Console.WriteLine("Main Begin");
            var t = DownloadAsync();
            Console.WriteLine("Using wait operator");
            await t;
            Console.WriteLine("Main End");
        }

        static async Task DownloadAsync()
        {
            Console.WriteLine("DownloadAsync Begin");
            using (var web = new System.Net.WebClient())
                Console.WriteLine((await web.DownloadStringTaskAsync("http://www.google.com/")).Substring(0, 50));
            Console.WriteLine("DownloadAsync End");
        }

Output:

Main Begin
DownloadAsync Begin
Using wait operator
<!doctype html><html itemscope="" itemtype="http:/
DownloadAsync End
Main End

We achieve the same result! The difference is that with async methods instead of using the Wait() method of Task we use the await operator, a .NET sugar to use instead of Wait() method. If your code is very simple and doesn't need to control if Asynchronous call is processing indefinitely, you can use the wait operator. If you need to control the time that async function will take, or some other kind of control, then you can do it using Task Methods. 



Case 3- Caller is async and function called is not async

Code:

        static void Main(string[] args)
        {
            var t = MainAsync();
            Console.ReadKey();
        }
     
        static async Task MainAsync()
        {
            Console.WriteLine("Main Begin");
            Download();
            Console.WriteLine("Main End");
        }

        static void Download()
        {
            Console.WriteLine("Download Begin");
            using (var web = new System.Net.WebClient())
                Console.WriteLine((web.DownloadStringTaskAsync("http://www.google.com/")).Result.Substring(0, 50));
            Console.WriteLine("Download End");
        }

Output:

Main Begin
Download Begin
<!doctype html><html itemscope="" itemtype="http:/
Download End
Main End

By the results we can see that the code runs synchronously. If the function we are calling is sync it will run sync by default. But we can still make an asynchronous call, using Task.Run(), as we'll see in the next situation.

Case 4- Caller is not async and function called is not async

In this case all code execute in the sequence that is given, in one word, synchronously.

But we can still make an asynchronous call, using Task.Run().

Code:

        static void Main(string[] args)
        {
            Console.WriteLine("Main Begin");
            var t = Task.Run(() => Download());
            Console.WriteLine("Main End");
            Console.ReadKey();
        }
     
        static void Download()
        {
            Console.WriteLine("Download Begin");
            using (var web = new System.Net.WebClient())
                Console.WriteLine((web.DownloadStringTaskAsync("http://www.google.com/")).Result.Substring(0, 50));
            Console.WriteLine("Download End");
        }

Output:

Main Begin
Main End
Download Begin
<!doctype html><html itemscope="" itemtype="http:/
Download End

Task.Run is not perfect substitute for async methods, but it worked. Lets give some time to the task so it can at least start:

        static void Main(string[] args)
        {
            Console.WriteLine("Main Begin");
            var t = Task.Run(() => Download());
            t.Wait(10);
            Console.WriteLine("Main End");
            Console.ReadKey();
        }

Output:

Main Begin
Download Begin
Main End
<!doctype html><html itemscope="" itemtype="http:/
Download End

This the function started before Main ended, and continued asynchronously after it ended. But we can still use the Wait method without parameters (equals to wait indefinitely):

        static void Main(string[] args)
        {
            Console.WriteLine("Main Begin");
            var t = Task.Run(() => Download());
            t.Wait();
            Console.WriteLine("Main End");
            Console.ReadKey();
        }

Output:

Main Begin
Download Begin
<!doctype html><html itemscope="" itemtype="http:/
Download End
Main End

And the async call executed in propper order.


Conclusion:

Although .NET let you relly on Task.Run to start async calls, prefer using await and async operators, or at least async operator and Task methods to control the Asynchronous method invoked.


sábado, 10 de janeiro de 2015

Download da Nota Fiscal Eletrônica (NFe) - Instalação do certificado

Download da Nota Fiscal Eletrônica (NFe)

Instalação do certificado


Após o artigo Download da Nota Fiscal Eletrônica (NFe) em que expliquei como fazer o acesso ao servidor da receita através de uma aplicação windows forms c#, segue este artigo que esclarece como configurar o certificado tanto para o windows forms como para para rodar no IIS.

Existem Storages ou armazenamentos distintos. Na aplicação windows forms o certificado na forma de um arquivo pfx deve ser instalado para o usuário local, enquanto que para rodar no IIS deve ser instalado na Máquinha Local - Local Machine.

A instalação procede da mesma forma tanto em uma como em outra, com pequenas diferenças.

1- Abra o MMC (Microsoft Management Console) clicando no windows 7 em iniciar e digitando mmc.exe ou em qualquer windows utilize o atalho iniciar + R que chama a janela executar e digite mmc.exe:



2- Clique em File (Arquivo) / Add-Remove Snap-In e escolha Certificates:

3- Para a aplicação windows forms utiliza-se o My user account. Para o IIS utilize o Computer account:


4- No caso de Computer account aparece uma tela a mais, escolha a primeira opção, computador local:


5- Expanda a pasta Certificates e clique com o botão direito em Personal, All Tasks, Import:


6- Indique o local do arquivo, a senha, e opte por salvar o certificado no local automático baseado no tipo.

7- No caso de windows forms o processo está completo aqui. No caso do IIS é preciso dar permissão de acesso à chave privada ao usuário do IIS, se não vai dar erro na hora de acessar o certificado. Para isso é preciso instalar o utilitário winhttpcertcfg, que se baixa no site da microsoft em: http://www.microsoft.com/en-us/download/details.aspx?id=19801

8- Se sua aplicação no IIS usa impersonation, será preciso dar permissão ao usuário configurado no web.config para o impersonation e também ao usuário do pool do IIS. Caso contrário, apenas ao usuário do pool do IIS. Para saber que usuário é esse, abra o IIS, expanda a pasta sites, clique no site que quer configurar e em advanced settings:



Na primeira linha, ao lado de Application Pool, está o nome do usuário que precisa da permissão.

9- Configurando a permissão:

A) Entre no prompt do DOS e digite cd "pasta que vc instalou o utilitário", por default o comendo seria este: cd "C:\Program Files (x86)\Windows Resource Kits\Tools"

B) Ai execute:

winhttpcertcfg -g -c LOCAL_MACHINE\My -s Nome_Do_Certificado -a UsuarioDoPoolDoIIS

C) E se houver impersonation execute também:

winhttpcertcfg -g -c LOCAL_MACHINE\My -s Nome_Do_Certificado -a UsuarioDoImpersonation


OBS 1:

Se estiver adaptando o código do post anterior para usar numa aplicação Web, não esqueça de mudar a linha:

NFe_Rec.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, certificado);

Para:

NFe_Rec.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, certificado);

Conforme a orientação no comentário do Reginaldo Souza. Obrigado Reginaldo.

sábado, 3 de janeiro de 2015

Download da Nota Fiscal Eletrônica (NFe)

Download da Nota Fiscal Eletrônica (NFe)

========================================
Atualização em 21/08/2017:

Graças à contribuição dos colegas  Thiago Motta e André Scudeler temos agora uma versão funcional do código para a nova versão do download da NFe, o NFeDistribuicaoDFe. Segundo os amigos os passos são os mesmos, a manifestação é igual, e no passo 3 do tutorial deve ser adicionado o NFeDistribuicaoDFe no lugar do NFeDownloadNF. Utilize para isso a nova url: https://www1.nfe.fazenda.gov.br/NFeDistribuicaoDFe/NFeDistribuicaoDFe.asmx

Segue o novo código do procedimento de download:

private void DownloadXML(object sender, EventArgs e)
{
    //Download da NFe, Para Download da xml por chave é necessario que a versão seja 1.01
    var texto = @"<distDFeInt xmlns=""http://www.portalfiscal.inf.br/nfe"" versao = ""1.01"">
    <tpAmb>1</tpAmb>
    <cUFAutor>35</cUFAutor>
    <CNPJ>CNPJ DA EMPRESA</CNPJ>
    <consChNFe>
        <chNFe>CHAVE NOTA FISCAL</chNFe>
    </consChNFe>
    </distDFeInt>";

    var xml = ConverterStringToXml(texto);
    BaixarXml(xml);
}

private void BaixarXml(XmlDocument xml)
{
    var NFe_Sc = new Distribuicao.NFeDistribuicaoDFeSoapClient();
    NFe_Sc.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySerialNumber, "NÚMERO SERIAL DO SEU CERTIFICADO");

    XElement x = XElement.Parse(xml.InnerXml);

    var arquivo = NFe_Sc.nfeDistDFeInteresse(x).ToString();

    var xmlNota = ConverterStringToXml(arquivo);
    var conteuZip = xmlNota.GetElementsByTagName("docZip")[0].InnerText;

    byte[] dados = Convert.FromBase64String(conteuZip);
    //TODO: fazer uso dos dados da variável retorno
    var xmlRetorno = descompactar(dados);
}

private XmlDocument ConverterStringToXml(string texto)
{
    var xml = new XmlDocument();
    xml.LoadXml(texto);
    return xml;
}

public string descompactar(byte[] conteudo)
{
    using (var memory = new MemoryStream(conteudo))
    using (var compression = new GZipStream(memory, CompressionMode.Decompress))
    using (var reader = new StreamReader(compression))
    {
        return reader.ReadToEnd();
    }
}

Amigos, muito obrigado! Compartilhar é agradável e muito comum no estrangeiro. Nós brasileiros devemos nos unir a esta causa!

========================================

(adaptação de A Saga do Download da Nota Fiscal Eletrônica (NFe), previamente publicado neste blog, mas já retirado)

Pré requisitos da receita para fazer o download da NFe via Web Service:

1- Certificado digital: Se sua empresa não tem esse certificado, é preciso adquirir com uma das entidades certificadoras, e deve estar válido. (Sobre como instalar o certificado, inclusive para rodar no IIS, veja o meu outro post em:
http://pmarco777.blogspot.com.br/2015/01/download-da-nota-fiscal-eletronica-nfe_10.html)

2- Até 30 dias da emissão: Se a nota foi emitida há mais de 30 dias só será possível o download "manual", nota por nota, digitando o captcha, no site: http://www.nfe.fazenda.gov.br/portal/consulta.aspx?tipoConsulta=completa&tipoConteudo=XbSeqxE8pl8=

3- Registrar um evento de "ciência da operação": É preciso se "manifestar" em relação a nota para depois poder baixá-la. Exigência da receita.

O manual da receita é a Nota Técnica 2012.002 que pode ser baixada em:
http://www.nfe.fazenda.gov.br/portal/listaConteudo.aspx?tipoConteudo=tW+YMyk/50s=

Este tutorial utiliza .NET C# 4.0, projeto windows form com 1 form e 2 botões. 1 botão "manifesta" e o outro faz o download.

1- Abra o Visual Studio e crie um novo projeto através do menu file/new project:


e coloque dois botões no form:



2- Adicione a referência a dll System.Security.dll, do .NET. Ela permitirá o acesso ao Namespace System.Security.Cryptography.Xml, necessário para assinar digitalmente o xml de manifestação. Para isso clique com o botão direito do mouse em referências do projeto, add reference:


Selecione a dll e adicione ao projeto.

3- Agora adicione a referência aos 2 web services da receita:
Namespace NfeRecepcao: https://www.nfe.fazenda.gov.br/RecepcaoEvento/RecepcaoEvento.asmx
Namespace NfeDownloadhttps://www.nfe.fazenda.gov.br/NfeDownloadNF/NfeDownloadNF.asmx

também usando o botão direito do mouse em references, só que o item é "add service reference".
Coloque a url do web service e clique no botão Go. Se neste momento vc não tiver o certificado instalado na sua máquina de desenvolvimento, não vai conseguir avançar, conforme foi avisado no início do artigo:


Coloque os namespaces corretos e adicione as referências com o botão ok.

4- No app.config (quando utilizar um projeto web será web.config) mude as linhas:
<security mode="Transport">
    <transport clientCredentialType="None" proxyCredentialType="None"
Para:
<security mode="Transport">
    <transport clientCredentialType="Certificate" proxyCredentialType="None"

5- Segue agora o código do form1.cs que deverá ser adaptado as suas necessidades, lembrando que o código do botão 1 faz a manifestação e o código do botão 2 faz o download. Onde se lê:

"Ponha o CNPJ da sua empresa"
"Ponha a chave de 44 dígitos da NFe recebida pela sua empresa"
"Nome do certificado da sua empresa"

Deve ser alterado para os valores corretos.

using System;
using System.Net;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using System.Xml;
using System.Text;
using System.Windows.Forms;
using Recepcao = WindowsFormsApplication1.NfeRecepcao;
using Download = WindowsFormsApplication1.NfeDownload;

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
//
//Ciencia da Operacao
//
var NFe_Rec = new Recepcao.RecepcaoEventoSoapClient();
NFe_Rec.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "Nome do certificado da sua empresa");

var notas = new string[] {
"Ponha a chave de 44 dígitos da NFe recebida pela sua empresa"
,"Ponha a chave de 44 dígitos da NFe recebida pela sua empresa"
}; // este array não deve passar de 20 elementos, máximo permitido por lote de manifestação

var sbXml = new StringBuilder();
sbXml.Append(@"<?xml version=""1.0"" encoding=""UTF-8""?>
<envEvento xmlns=""http://www.portalfiscal.inf.br/nfe"" versao=""1.00"">
<idLote>1</idLote>
");
foreach (var nota in notas)
{
sbXml.Append(@"
<evento xmlns=""http://www.portalfiscal.inf.br/nfe"" versao=""1.00"">
<infEvento Id=""ID210210" + nota + @"01"">
<cOrgao>91</cOrgao>
<tpAmb>1</tpAmb>
<CNPJ>Ponha o CNPJ da sua empresa</CNPJ>
<chNFe>" + nota + @"</chNFe>
<dhEvento>" + DateTime.Now.AddDays(-1).ToString("yyyy-MM-ddTHH:mm:ss") + @"-03:00</dhEvento>
<tpEvento>210210</tpEvento>
<nSeqEvento>1</nSeqEvento>
<verEvento>1.00</verEvento>
<detEvento versao=""1.00"">
<descEvento>Ciencia da Operacao</descEvento>
</detEvento>
</infEvento>
</evento>
");
}
sbXml.Append("</envEvento>");

var xml = new XmlDocument();
xml.LoadXml(sbXml.ToString());

var i = 0;
foreach (var nota in notas)
{
var docXML = new SignedXml(xml);
docXML.SigningKey = NFe_Rec.ClientCredentials.ClientCertificate.Certificate.PrivateKey;
var refer = new Reference();
refer.Uri = "#ID210210" + nota + "01";
refer.AddTransform(new XmlDsigEnvelopedSignatureTransform());
refer.AddTransform(new XmlDsigC14NTransform());
docXML.AddReference(refer);

var ki = new KeyInfo();
ki.AddClause(new KeyInfoX509Data(NFe_Rec.ClientCredentials.ClientCertificate.Certificate));
docXML.KeyInfo = ki;

docXML.ComputeSignature();
i++;
xml.ChildNodes[1].ChildNodes[i].AppendChild(xml.ImportNode(docXML.GetXml(), true));
}

var NFe_Cab = new Recepcao.nfeCabecMsg();
NFe_Cab.cUF = "33"; //RJ => De acordo com a Tabela de Código de UF do IBGE
NFe_Cab.versaoDados = "1.00";
var resp = NFe_Rec.nfeRecepcaoEvento(NFe_Cab, xml);

var fileResp = "c:\\" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + "-tempResp.xml";
var fileReq = "c:\\" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + "-tempRequ.xml";
File.WriteAllText(fileReq, xml.OuterXml);
File.WriteAllText(fileResp, resp.OuterXml);
System.Diagnostics.Process.Start(fileReq);
System.Diagnostics.Process.Start(fileResp);
}

private void button2_Click(object sender, EventArgs e)
{
//
//Download da NFe
//
var notas = new string[] {
"Ponha a chave de 44 dígitos da NFe recebida pela sua empresa"
,"Ponha a chave de 44 dígitos da NFe recebida pela sua empresa"
}; // este array não deve passar de 10 elementos, máximo permitido por lote de download da NFe

var sbXml = new StringBuilder();
sbXml.Append(@"<?xml version=""1.0"" encoding=""UTF-8""?>
<downloadNFe xmlns=""http://www.portalfiscal.inf.br/nfe"" versao=""1.00"">
<tpAmb>1</tpAmb>
<xServ>DOWNLOAD NFE</xServ>
<CNPJ>Ponha o CNPJ da sua empresa</CNPJ>
");

foreach (var nota in notas)
{
sbXml.Append("<chNFe>" + nota +"</chNFe>");
}
sbXml.Append("</downloadNFe>");

var xml = new XmlDocument();
xml.LoadXml(sbXml.ToString());

var NFe_Cab = new NfeDownload.nfeCabecMsg();
NFe_Cab.cUF = "33"; //RJ => De acordo com a Tabela de Código de UF do IBGE
NFe_Cab.versaoDados = "1.00";

var NFe_Sc = new NfeDownload.NfeDownloadNFSoapClient();
NFe_Sc.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "Nome do certificado da sua empresa");
var resp = NFe_Sc.nfeDownloadNF(NFe_Cab, xml);

var fileResp = "c:\\" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + "-tempResp.xml";
var fileReq = "c:\\" + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + "-tempRequ.xml";
File.WriteAllText(fileReq, xml.OuterXml);
File.WriteAllText(fileResp, resp.OuterXml);
System.Diagnostics.Process.Start(fileReq);
System.Diagnostics.Process.Start(fileResp);
}
}
}