MessageQueue e HTTP

Quando trabalhamos com o Microsoft Message Queue, serviço de mensagens fornecido pelo Windows, ele nos permite expor as filas através do protocolo HTTP. Isso permitirá apenas o envio de mensagens através deste protocolo (podendo também ser via HTTPS), e com isso, clientes que estiverem além dos limites da empresa, poderão enfileirar mensagens para o processamento interno de alguma rotina que rodará periodicamente.

Apenas o envio é permitido via HTTP, pois somente a porta 80 é necessária para executar esta ação; para recepcionar a mensagem, é exigido que outras portas também estejam abertas no firewall, e devido a isso, a Microsoft optou por não permitir o recebimento de mensagens sobre estes protocolos.

Para fazer uso deste recurso, ao instalar o Message Queue no computador que receberá as mensagens, você deve marcar a opção de suporte ao protocolo HTTP, conforme é mostrado na imagem abaixo. Vale lembrar que esta opção exige que o IIS (Internet Information Services) também esteja instalado nesta mesma máquina.
Depois de instalado, o Message Queue cria uma diretório virtual dentro do IIS, que servirá o ponto de acesso (HTTP/S) para as filas criadas nesta máquina. Abaixo vemos a fila chamada “Exemplo” criada e o diretório virtual chamado “MSMQ”.
Por fim, ao fazer uso da API System.Messaging, podemos apontar para o endereço HTTP, combinando o diretório virtual criado com o nome da fila para qual queremos enviar a mensagem.

using (var mq = new MessageQueue(“FormatName:DIRECT=http://localhost/msmq/private$/exemplo”))
    mq.Send(“Algo Aqui”, MessageQueueTransactionType.Single);

Apesar disso ser possível, considere o uso do WCF com MessageQueue.

Testes com a classe TelemetryClient

No artigo anterior eu mostrei o uso da classe TelemetryClient para catalogar informações customizadas. O fato de utilizar a classe em sua configuração padrão, fará com que as informações sejam enviadas para o serviço (na nuvem) mesmo que você ainda esteja em ambiente de testes. Talvez isso não seja o que estamos querendo, pois espalhamos pelo código os pontos que são cruciais para enviar as informações, mas talvez só faça sentido quando estive em ambiente de produção.

A classe TelemetryClient confia nas configurações que são disponibilizadas pela classe TelemetryConfiguration. Ela, por sua vez, possui uma propriedade estática chamada Active que retorna as configurações globais (para a aplicação) do Application Insights. A partir daí podemos recorrer a propriedade DisableTelemetry que quando definida como true, não enviará as informações ao serviço. Para utiliza-la podemos também condicionar à diretiva de DEBUG para somente desabilitar enquanto estivermos em desenvolvimento:

#if DEBUG
            TelemetryConfiguration.Active.DisableTelemetry = true;
#endif

Ainda com o exemplo utilizado no artigo anterior, não estávamos nos preocupando com os testes unitários. É uma preocupação que também devemos ter para não reportar os eventos quando estamos rodando os testes. Apesar da opção acima resolver, as vezes podemos querer validar se o nosso código está ou não reportando quando necessário e se as informações que foram levadas estão de acordo com a nossa expectativa.

A classe de configuração também nos permite configurar o canal de comunicação, e com isso customizar (sobrescrever) onde e como queremos armazenar os logs. Só que antes de visualizarmos o código necessário para alcançar isso, vamos entender a dependência entre a classe TelemetryClient e o restante da configuração.

A classe TelemetryClient possui dois overloads, onde um deles espera a instância da classe TelemetryConfiguration, que se for omitida, o Application Insights utiliza o valor fornecido pela propriedade Active. A classe TelemetryConfiguration possui uma propriedade chamada TelemetryChannel, onde podemos customizar o canal de comunicação através da implementação da interface ITelemetryChannel. Para o exemplo, vou optar por armazenar os logs em uma coleção interna, conforme é possível ver no código abaixo:

public class TestChannel : ITelemetryChannel
{
    private readonly IList<ITelemetry> itens;

    public TestChannel()
    {
        this.itens = new List<ITelemetry>();
    }

    public void Send(ITelemetry item)
    {
        this.itens.Add(item);
    }

    public IList<ITelemetry> Itens
    {
        get
        {
            return this.itens;
        }
    }

    //outros membros, omitidos por questão de espaço
}

Depois da classe implementada, precisamos criar a instância e associa-la à propriedade da configuração para que ela passe a ser utilizada quando chamarmos os métodos para catalogar as informações (Track*). O código que foi criado para calcular o frete não foi alterado em nada. Mesmo que a configuração tenha sido realizada externamente, a classe TelemetryClient irá utilizar esta classe via TelemetryConfiguration (via propriedade Active). O teste ficaria da seguinte forma:

[TestClass]
public class CalculadoraDeFrete
{
    private TestChannel channel;

    [TestInitialize]
    public void Inicializar()
    {
        this.channel = new TestChannel();
        TelemetryConfiguration.Active.TelemetryChannel = channel;
    }

    [TestMethod]
    public void DeveCalcularFreteParaValinhos()
    {
        var cep = “13273-047”;
        var valorEsperado = 12.38M;

        var calculadora = new CalculadoraDeFrete();
        var resultado = calculadora.Calcular(cep);

        Assert.AreEqual(valorEsperado, resultado);

        var evento = this.channel.Itens.Single() as EventTelemetry;

        Assert.AreEqual(“CalculoDeFrete”, evento.Name);
        Assert.AreEqual(cep, evento.Properties[“Cep”]);
        Assert.AreEqual(resultado.ToString(), evento.Properties[“Resultado”]);
    }
}

ApplicationInsights – Utilizando a classe TelemetryClient

A Microsoft está disponibilizando um serviço de telemetria de aplicações chamado de Application Insights. É um serviço que roda no Azure (nuvem) e que permite integrar nas aplicações que desenvolvemos para que seja possível extrair informações, armazenar e, consequentemente, analisar os dados gerados para tentar identificar algum problema de performance ou de qualquer outra natureza.

Dependendo do tipo de aplicações onde este serviço é instalado, ele utiliza o recurso exposto pela aplicação para interceptar a requisição e catalogar as informações. Por exemplo, se este serviço é instalado em uma aplicação ASP.NET, então o módulo (IHttpModule) ApplicationInsightsHttpModule é incluído para interceptar as requisições que chegam à aplicação; já para aplicações ASP.NET 5, acopla-se através do método UseApplicationInsightsRequestTelemetry. Em ambos os casos, automaticamente o Application Insights começa a receber informações sobre as requisições que chegam para a aplicação, duração da requisição, cabeçalho, etc.

A identificação da aplicação para o serviço é uma chave chamada instrumentation key (GUID) que é gerada pelo Azure, e deve ser configurada na aplicação para que o Application Insights possa enviar ao serviço e ele, por sua vez, saiba qual aplicação está gerando os dados. E, novamente, dependendo da tecnologia, o arquivo utilizado para armazenar a chave é o ApplicationInsights.config para os projetos ASP.NET tradicionais ou aplicações Windows, e o config.json para o ASP.NET 5.

[ApplicationInsights.config]
<?xml version=”1.0″ encoding=”utf-8″?>
<ApplicationInsights>
  …
  <InstrumentationKey>bab34a9e-f1d9-4cf9-8a09-5f33b8f4ebed</InstrumentationKey>
</ApplicationInsights>

[config.json]
{
  “ApplicationInsights”: {
    “InstrumentationKey”: “bab34a9e-f1d9-4cf9-8a09-5f33b8f4ebed”
  },
  …
  }
}

Mesmo que ocorra essa mágica, o Application Insights fornece uma API que podemos recorrer para customizar os dados que são enviados para o serviço. Isso pode ser feito através da classe TelemetryClient, que expõe alguns métodos específicos ou de mais baixo nível para customizar diversas informações que podem ser levadas para o serviço. Para fazer uso desta classe, primeiramente precisamos utilizar o Nuget para adicionar o seguinte pacote:

Install-Package Microsoft.ApplicationInsights

É importante dizer que é possível fazer uso desta classe em projetos que não sejam do tipo ASP.NET. Como exemplo, vou utilizar uma aplicação Console para catalogar os mais diversos tipos de informações no Application Insights. No caso de projetos ASP.NET, já há uma opção durante a criação do projeto para habilita-lo, assim como é possível vermos na imagem abaixo, e a instalação explícita do pacote não é necessária.

>

Independente da forma como você adiciona o suporte para o Application Insights na aplicação, a classe TelemetryClient pode ser utilizada. Como já era de se esperar, esta classe fornece uma propriedade chamada InstrumentationKey, que é onde devemos configurar a chave gerado pelo Azure, mas em casos de projetos do tipo Windows, podemos criar o arquivo ApplicationInsights.config e lá ter o elemento InstrumentationKey, o que facilita a alteração sem precisar recompilar a aplicação.

var tc = new TelemetryClient()
{
    InstrumentationKey = “bab34a9e-f1d9-4cf9-8a09-5f33b8f4ebed”
};

A partir da instância da classe TelemetryClient criada, temos alguns métodos para enviar informações para o serviço. O Application Insights possui diversos tipos de informações (“categorias”) que possamos enviar os dados: Trace, Request, Page View, Expcetion, Dependency e Custom Event. Para cada uma destas informações, há um método correspondente: TrackTrace, TrackRequest, TrackPageView, TrackException, TrackDependency e TrackEvent.

Para exemplificar, considere o código abaixo. Estamos utilizando o método TrackEvent para indicar ao serviço que trata-se de um evento que vamos gerar diversas entradas e que corresponde à uma atividade de rotina da nossa aplicação. Além do nome, também podemos, opcionalmente, informar um dicionário de dados contendo propriedades que queremos anexar aquele evento. No exemplo, estou optando por incluir o parâmetro, o resultado e o tempo que levou para realizar o cálculo.

public class CalculadoraDeFrete
{
    private static TelemetryClient tc = new TelemetryClient()
    {
        InstrumentationKey = “bab34a9e-f1d9-4cf9-8a09-5f33b8f4ebed”
    };

    public static decimal Calcular(string cep)
    {
        var sw = Stopwatch.StartNew();

        //cálculo do frete
        var resultado = 12.38M;

        tc.TrackEvent(
            “CalculoDeFrete”,
            new Dictionary<string, string>()
            {
                { “Cep”, cep },
                { “Resultado”, resultado.ToString() },
                { “Tempo”, sw.Elapsed.ToString() }
            });
        tc.Flush();

        return resultado;
    }
}

Ainda em relação as propriedades que podemos anexar ao log no momento em que o evento ocorre, podemos também definir valores globais que serão enviados em todas as requisições, independentemente se está catalogando um evento, uma exceção, ou qualquer outra informação. E para isso, basta recorrer ao dicionário que está exposto através do contexto da classe TelemetryClient:

static CalculadoraDeFrete()
{
    tc.Context.Properties.Add(“Usuario”, “Israel Aece”);
}

Internamente a classe mantém um buffer com as entradas e periodicamente envia as requisições para o serviço. Utilizamos o método Flush para adiantar esse processo. Se consultarmos o portal, já podemos visualizar o evento adicionado e as respectivas propriedades que enviamos (incluindo a propriedade global Usuario). A partir deste nome do evento, eles sempre serão agrupados para uma melhor visualização e análise.

Como foi comentado acima, também é possível realizar o log de uma exceção. Para isso, podemos envolver o código inseguro em um bloco try/catch e utilizar o método TrackException. Estou optando por utilizar a versão mais simples do método, mas há parâmetros opcionais que permitem incluir um dicionário com valores customizados, assim como fizemos no código acima.

public static decimal Calcular(string cep)
{
    var resultado = 0M;

    try
    {
        throw new ArgumentException(“O CEP informado está fora da área de cobertura da transportadora.”);
        //cálculo do frete
    }
    catch (Exception e)
    {
        tc.TrackException(e);
        tc.Flush();
        throw;
    }

    return resultado;
}

Agora se atualizarmos o portal do Azure, irmos até a seção de Failures, o contador foi incrementado e é possível visualizar o erro que ocorreu, incluindo além da mensagem, toda a stack trace, útil para nós desenvolvedores, identificar onde o problema exatamente aconteceu.

Como é um exemplo simplista, estou fazendo o tratando localizado da exceção. Dependendo da estratégia de tratamento de erros da aplicação, é possível centralizar o envio das informações do erro em um ponto global, e dependendo da tecnologia utilizada, você pode recorrer ao suporte que ela dá para isso. Em aplicações ASP.NET tradicionais, você pode concentrar isso no evento Application_Error no arquivo Global.asax, no ASP.NET MVC em um filtro, no ASP.NET Web API no ExceptionLogger ou, no WCF, através da interface IErrorHandler.

Como podemos notar, o Application Insights eleva o “simples log de aplicações” para um outro nível. Podemos gerar diversas informações e temos a certeza que por trás existe uma grande infraestrutura para armazenamento e processamento delas. Terceirizando isso nos permite ainda mais focar no que de fato importa: o desenvolvimento da regra de negócio para qual a aplicação está sendo construída.

Composição de Tokens para Cancelamento

Ao contrário do que temos na classe Thread, a classe Task não fornece um método para abortar a execução da mesma. Se desejamos “monitorar” a execução e ter a chance de cancelar, temos que passar uma espécie de token ao criar a tarefa, e externamente, quando quisermos, podemos cancelar a execução. Compete aquele que programa a tarefa a ser executada, avaliar se o cancelamento foi ou não solicitado.

No .NET Framework a classe que nos permite controlar o cancelamento é a CancellationTokenSource. Ela expõe uma propriedade chamada Token que por sua vez, é representada pela classe CancellationToken, e é este valor que temos que passar para a tarefa a ser executada, e através do método Cancel podemos solicitar o cancelamento. Um detalhe importante aqui é que a classe CancellationTokenSource também disponibiliza um método estático chamado CreateLinkedTokenSource, que nos permite agrupar vários tokens, e quando qualquer um deles for cancelado, a tarefa é cancelada.

var cts1 = new CancellationTokenSource();
var cts2 = new CancellationTokenSource();

using (var cts3 = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token))
{
    var task = Task.Factory.StartNew(() =>
    {
        for (int i = 0; i < int.MaxValue; i++)
        {
            cts3.Token.ThrowIfCancellationRequested();
            Console.WriteLine(i);
        }
    }, cts3.Token);
               
    Console.ReadLine();
    cts2.Cancel();
    Console.ReadLine();
}

Repare que criamos dois tokens (cts1 e cts2) e agrupamos em um terceiro chamado cts3. Se invocarmos o método Cancel de qualquer um dos três, a tarefa será interrompida. Vale lembrar que somente o fato de passar o token na criação da tarefa não é suficiente para interromper a execução; como foi dito acima, é de responsabilidade do desenvolver monitorar a solicitação de cancelamento, e para isso, neste caso estamos utilizando o método ThrowIfCancellationRequested, que dispara uma exceção se a solicitação foi feita.

Por fim, note que apenas o terceiro CancellationTokenSource está sendo envolvido em um bloco using. Isso é porque quando criamos este objeto a partir do link entre outros, o método Dispose irá executar algumas atividades que irão impactar a memória, descartando objetos que são criados exclusivamente para isso. O método Dispose nesta classe também pode ser útil quando estamos utilizando a funcionalidade CancelAfter, que utiliza internamente um Timer para monitorar o tempo e, automaticamente, interromper a tarefa que está – ainda – sendo executada depois que o tempo expira. 

Criação e Gerenciamento de Conexões HTTP

Desde o início do .NET Framework temos uma classe chamada HttpWebRequest, que tem a finalidade de realizar requisições HTTP para algum recurso disponível. E como era de se esperar, ela expõe diversos métodos e propriedades para configurar as requisições que partem da instancia desta classe.

A cada requisição que é realizada, internamente ela recorre a classe ServicePointManager para criar a conexão até o recurso desejado, e a partir dai passa a reutilizar a mesma conexão nas chamadas subsequentes, poupando recursos da máquina de onde a requisição esta sendo feita. O objeto de conexão é criado considerando o protocolo, o host e a porta; se algum destes parâmetros variar, uma nova conexão é criada para atender este novo endereço e, consequentemente, reutilizada nas próximas requisições.

Só que existe a possibilidade de interferir na criação e gestão destas conexões. A classe HttpWebRequest define uma proprieade chamada ConnectionGroupName, e como o próprio nome sugere, ele considera o valor especificado nesta propriedade para criar a conexão, ou seja, para cada valor distinto definido nesta propriedade, uma nova conexão é criada. Se o valor se repetir, a conexão criada previamente é reutilizada.

Como sabemos, o WCF utiliza a classe HttpWebRequest nos bastidores quando acessa um serviço através do protocolo HTTP, e até então ele não disponibilizava qualquer propriedade no binding para configurar este pool de conexões HTTP. A partir da versão 4.6 do .NET Framework, a Microsoft permite ajustar este parâmetro (adicionando um prefixo) a fim de fazer uma melhor gestão e reuso das conexões HTTP pelos serviços WCF.

Por padrão, uma única conexão é mantida e compartilhada por todos os channels factories (proxy) que são instanciados pelo cliente. O que essa nova configuração irá fazer é criar uma conexão dedicada para cada channel factory criado pelo cliente, utilizando uma espécie de contador de instâncias para definir a propriedade ConnectionGroupName.

Para isso funcionar, basta ligar o flag useUniqueConnectionPoolPerFactory através do arquivo de configuração (app.config ou web.config) da aplicação cliente, assim como é mostrado no código abaixo.

<appSettings>
    <add key=”wcf:httpTransportBinding:useUniqueConnectionPoolPerFactory”
         value=”true” />
</appSettings>

Agora, se quiser um controle mais refinado e, de repente, variar a conexão de acordo com alguma regra ou parâmetro de negócio, você pode extrair e definir o nome do pool de conexões através das propriedades da mensagem do WCF, que em baixo nível, também vai utilizar o valor definido aqui para configurar a propriedade ConnectionGroupName.

OperationContext
    .Current
    .OutgoingMessageProperties
    .Add(“HttpTransportConnectionGroupNamePrefix”, “ValorCustomizado”);

Por fim, para ver isso em funcionamento, considere o código a seguir, que cria um laço e dentro dele instancia o channel factory e invoca o método do serviço. Antes de rodar, vamos configurar o flag como True para ver o impacto causado, provocando a criação de uma conexão dedicada para cada proxy. Por fim, é possível visualizar as conexões criadas através do utilitário TCPView.

var endereco = “http://localhost:9388&#8221;;

using (var host = new ServiceHost(typeof(Teste), new Uri(endereco)))
{
    host.AddServiceEndpoint(typeof(IServico), new BasicHttpBinding(), “”);
    host.Open();

    for (int i = 1; i < 11; i++)
        using (var srv = new ChannelFactory<IServico>(
            new BasicHttpBinding(), new EndpointAddress(endereco)))
                Console.WriteLine(srv.CreateChannel().Ping(“Teste ” + i.ToString()));
}

Propagando Transações

Como já sabemos, desde a versão 2.0 o .NET Framework possui uma API para criar e gerenciar transações dentro da aplicação. É o assembly System.Transactions.dll que disponibiliza um conjunto de APIs que podemos criar blocos transacionais, e assim alistar recursos que precisam de “proteção” para garantir que tudo seja efetivado ou desfeito caso algum problema ocorra (independente se o problema for de infraestrutura ou regra de negócio).

A principal classe que temos para isso é a TransactionScope, que ao ser envolvida em um bloco using, no método Dispose ela realiza o commit ou o rollback, dependendo se o método Complete foi ou não chamado. É dentro deste escopo criado que o .NET gerencia, e dependendo do que está envolvido nele, ele utiliza uma transação local (LTM) ou escala para uma transação distribuída (DTC).

Aqui estamos falando de um mesmo processo que é responsável por criar a transação, alistar os recursos, e por fim, finalizar a mesma (com sucesso ou falha). Mas como fazer quando a transação é iniciada pelo processo A e precisamos envolver as tarefas executadas por outro processo (B)? Ainda dentro deste mesmo assembly, temos uma classe chamada TransactionInterop, que expõe alguns métodos para “expandir” a transação entre processos.

Basicamente ele fornece métodos que geram uma chave e métodos que permitem recriar a transação a partir da mesma. Isso nos permitirá na aplicação A gerar a chave e importar na aplicação B, e com isso, ambas aplicações estarão envolvidas na mesma transação. É através do método estático GetTransmitterPropagationToken que geramos um array de bytes que representa o token de identificação da transação. Para exemplificar, vamos gerar este token a partir da aplicação A e armazenar este token um arquivo no disco para compartilhar com a aplicação B. No código abaixo estou removendo os logs para poupar espaço.

//Aplicação A
using (var ts = new TransactionScope())
{
    File.WriteAllBytes(@”C:TempTransactionToken.bin”,
        TransactionInterop.GetTransmitterPropagationToken(Transaction.Current));

    ts.Complete();
}

Do outro lado, utilizamos o método (também estático) GetTransactionFromTransmitterPropagationToken da classe TransactionInterop. Ele recebe como parâmetro um array de bytes (que estamos extraindo do arquivo) e recria a transação. Aqui o detalhe importante é reparar que o resultado deste método está sendo passado diretamente para um dos construtores da classe TransactionScope, que faz uso desta transação (criada pela outra aplicação) e envolve nela tudo o que está em seu próprio bloco using.

//Aplicação B
using (var ts =
    new TransactionScope(
        TransactionInterop.GetTransactionFromTransmitterPropagationToken(
            File.ReadAllBytes(@”C:TempTransactionToken.bin”))))
{
    ts.Complete();
}

Se repararmos as imagens abaixo, notamos que na a aplicação A possui o identificador de transação distribuída (DI) zerado, e quando a aplicação B se envolve na mesma transação, o identificador da transação distribuída na aplicação A reflete o mesmo identificador da aplicação B, ou seja, ambas aplicações estão envolvidas na mesma transação. Por fim, é possível ver na última imagem o log do DTC, indicando que a mesma foi promovida à uma transação distribuída.

Curiosidade: sabemos que o WCF fornece suporte à transações em serviços, podendo o cliente iniciar a mesma e o serviço participar. O id da transação viaja do cliente para o serviço através de headers do envelope SOAP, e quando o serviço recebe a informação, ele recria a transação utilizando os recursos (classes e métodos) que vimos neste artigo.

Validando XML com Schemas da NF-e

No post anterior comentei como configurar os certificados para realizar consultas nos serviços da NF-e. Naquele exemplo estamos montando o XML manualmente que corresponde ao corpo da mensagem enviada ao serviço. Montar o XML manualmente é uma tarefa complicada, já que estamos sujeito à erros de digitação e, consequentemente, o serviço irá rejeitar a requisição por não estar dentro do formato esperado.

Mas qual é esse formato? Felizmente temos schemas (arquivos XSD) que são fornecidos pelo governo para que possamos validar as mensagens localmente antes de fazermos a requisição. O .NET Framework já fornece classe nativas para tal validação, e tudo o que precisamos fazer, é submeter o XML (sendo um arquivo ou até mesmo uma string) para que a validação possa ser realizada.

É através da classe XmlReaderSettings que devemos configurar o arquivo XSD a ser utilizado e o namespace que será utilizado para varrer o XML e validá-lo. Apesar de informar apenas um arquivo XSD, internamente ele referencia outros schemas que o complementam, e é importante que eles existam fisicamente no mesmo local para que a validação possa ser realizada. 

Esta classe também fornece um evento chamado ValidationEventHandler, que podemos assinar e recebermos as notificações dos problemas que ocorrerem durante a validação. Com os argumentos deste evento, podemos extrair a severidade e mensagem do erro. O código abaixo ilustra a criação e configuração desta classe:

var config = new XmlReaderSettings()
{
    ValidationType = ValidationType.Schema
};

config.Schemas.Add(“http://www.portalfiscal.inf.br/nfe&#8221;, “consStatServ_v3.10.xsd”);
config.ValidationEventHandler += (s, e) =>
{
    Console.WriteLine(“{0}: {1}”, e.Severity, e.Message);
};

Depois dela devidamente configurada, informamos a mesma durante a criação do XmlReader. No exemplo abaixo, estou optando por validar o conteúdo XML que está em uma string, mas nada impede do conteúdo vir de um arquivo no disco.

var mensagem = “<?xml version=”1.0″ encoding=”utf-8″ ?>” +
            “<consStatServ xmlns=”http://www.portalfiscal.inf.br/nfe&#8221; versao=”3.10″>” +
            “<tpAmb>1</tpAmb>” +
            “<cUFx>35</cUFx>” +
            “<xServ>STATUS</xServ>” +
            “</consStatServ>”;

using (var reader = XmlReader.Create(new StringReader(mensagem), config))
    while (reader.Read()) { }

Propositalmente eu alterei o elemento cUF para cUFx, que é um elemento que não é suportado pelo XSD. Ao rodar a aplicação, a mensagem abaixo é exibida indicando o problema:

Error: The element ‘consStatServ’ in namespace ‘http://www.portalfiscal.inf.br/n
fe’ has invalid child element ‘cUFx’ in namespace ‘http://www.portalfiscal.inf.b
r/nfe’. List of possible elements expected: ‘cUF’ in namespace ‘http://www.porta
lfiscal.inf.br/nfe’.