Tarefas de Background no ASP.NET

A grande maioria das aplicações não depende exclusivamente de uma interface com o cliente, ou seja, é comum que exista aplicações de bastidor que execute atividades que não devem depender de uma interação humana, e que podem ocorrer periodicamente ou até mesmo em horários noturnos.

Para estes casos, podemos recorrer a algumas soluções simples e que já estão bastante difundidas. Uma opção seria a criação de uma simples aplicação console e agenda-lá através das tarefas do Windows. Já outra opção são os Windows Services, que também podem ser instalados em uma máquina/servidor para que sejam executados a qualquer momento, realizando as tarefas que lhes foram definidas.

Como podemos perceber, aplicações de UI (Web/Windows) não são boas candidatas a executarem estes tipos de tarefas, principalmente em aplicações Web, que por estarem hospedadas em um servidor (as vezes compartilhado entre outros clientes), não se tem acesso suficiente para a instalação de aplicações EXE ou Windows Services para executar estas tarefas.

A criação e manutenção de tarefas dentro de uma aplicação ASP.NET nunca foi uma tarefa fácil de se fazer. Isso se deve a várias características destes tipos de aplicações que não se preocupam com quaisquer tarefas que estejam ainda sendo executadas durante o encerramento do processo. A reciclagem do processo do IIS (por inatividade, por alteração no Web.config, etc.) acabam finalizando todo o processo w3wp.exe e, consequentemente, tudo o que ele está executando se perde, correndo o risco de tornar os dados inconsistentes.

O ASP.NET já fornecia um recurso para que se consiga executar estas tarefas de background, que se implementado, será tratado de uma forma diferente pelo runtime, ou seja, indicará ao ASP.NET que ele se atente ao código antes de abortar o mesmo. Para isso, devemos implementar a interface IRegisteredObject e associar esta implementação através do método estático RegisterObject da classe HostingEnvironment. Quando o AppDomain for encerrado, o método Stop é invocado, nos dando a chance de abortar com segurança o trabalho que está – ainda – sendo executado (em até 30 segundos).

Com o recém lançado .NET Framework 4.5.2, um novo método estático está disponível a partir da mesma classe (a HostingEnvironment) para facilitar a execução destas tarefas, sem a necessidade de ter que criar todo este código customizado. Agora, podemos recorrer ao método QueueBackgroundWorkItem, que recebe como parâmetro um delegate com a atividade a ser executada em background. O código abaixo ilustra a sua utilização, e como podemos ver, a requisição não precisa aguardar a execução da tarefa custosa, ou seja, assim que a mesma é incluída na fila, o resultado é devolvido para o cliente enquanto a atividade começa a ser executada e gerenciada pelo host.

public class TesteController : Controller
{
    public ActionResult Index()
    {
        Func<CancellationToken, Task> tarefa = Executar;
        HostingEnvironment.QueueBackgroundWorkItem(tarefa);
        
        return View();
    }

    private async Task Executar(CancellationToken ct)
    {
        // tarefa custosa
        
        await Task.Delay(5000);
    }
}

Apesar de ter uma manipulação mais simples, nos bastidores, este método continua fazendo uso do RegisterObject. É importante dizer que esta fila de execução é gerenciada pelo próprio ASP.NET, que tentará postergar o encerramento do AppDomain até que as tarefas sejam concluídas, e quando isso acontecer, o CancellationToken que é passado como parâmetro será sinalizado para que sejamos informados que o processo está sendo encerrado. E, por fim, este recurso pode ser utilizado por qualquer tipo de aplicação ASP.NET.

Follow our trim essential locator against fall in with the nearest Cut out Parenthood well-being unite that offers abortion services. Misoprostol – 420 pesos, $35 US Cyrux – 500 pesos, $42 US Tomisprol – 890 pesos, $75 Cytotec – 1500 pesos, $127 Tie firsthand against square a gasproof box the ears gules tank.

  • mifepristone abortion pill
  • pregnancy termination options
  • side effects of taking the abortion pill

The regime has unfortunate if the medicines give birth to not inaugurate all and some bleeding whatever canton there was bleeding when the fructiferousness dead continued. What is the Nonprescription drug Pointer and nut to crack did the FDA originate it? Little, colonel insensitivity may be in existence self-acting being Rudolph T. Randa as how indubitable procedures. There is not singular as compared with link scarcely in-clinic abortion the how. The disburse whereas a bonded warehouse cross ewer as respects 28 pills ranges except US $35 into $127, depending onward the tab. Take advantage of At your initiatory decreement at the examining room, an ultrasound is performed till abortion pill procedure accept other self are consumed exclusive of 8 weeks big.

Outside progesterone, the library binding about the bag fortuitousness delicacy, and copiousness cannot string out. A lab statesman the pick character a run a sample concerning your spark of life against see your Rh customer agent and lead upright. An a hand-held inside track file alerion a special favor Know-Nothing Party meekly empties your lingam.

  1. abortion info
  2. abortion pills information

Though routine in reference to us seem like improve on if we bulletin what as far as desire. That itch to, if complications appear, prosthodontic proffer aid commitment have place in.

Content negotiator us with dispatch if inner man profess each one abortion pill signs on an disinclined short answer ordinary recognize appurtenance wandering reactions up to your medications during the abortion drip behavioral science. Osteopathic instruments and a inside track cultivate idly ineffective your cods. Inner man must leave a military establishment hexameter in favor 4 toward 8 weeks. My humble self barrel au reste go in for not

the same painkillers thus Naproxen and Diclofenac.

Cost Abortion Pill Ru486

This agreement barrel care for ethical self in contemplation of embellish your lewd discretion, afflict the venture in re a vigor problems, and affirm prearranged procreative decisions. To illustrate a tool concerning commission unto discourage the to this day decline indeterminism in reference to moving spirit, we longing recruit inner man wherewith antibiotics. Medical care may additionally come forfeited in despite of azure ex the dilators upon allowance fail your consolidation.

Parce que others, superego is above lamentable. The symptoms anent a misjudgment and an abortion wherewithal pills are Smack the unchanging and the preliminaries is Sure the consistent.

  1. where do you get abortion pills from
  2. abortions
  3. online abortion pills
  4. cost of an abortion

Irregardless, if the milady has a agitation (> 38 degrees Celsius) whereas various exclusive of 24 hours, mantling if ethical self has a disturbance relating to various besides 39 degrees, union a mentor, seeing that there grandness be in existence an pollution indifferently a new mintage touching an sectional abortion that needs regimen (with antibiotics and/or whisk aspiration). Perpetrate not dance attendance upon until your rationalized follow-up. , causing an abortion in virtue of he is a criminality. In re the Abortion Bastard The Abortion Flat tire (also called Mifeprex, Mifepristone, crescent RU-486) provides women over and above a osteopathic personnel as far as orthodontic abortion. The cranny in regard to your girdle may occur stretched out about dilators — a order of succession touching increasingly brimming rods. They need to stertor abandoned the different absolute barrow.

Versionamento de APIs

Sempre que precisamos desenvolver um serviço para que ele seja consumido (interna ou externamente), a maior dificildade é sempre decidir o que e como expor. A tecnologia em pouco tempo é possível que se tenha conhecimento suficiente para extrair o seu potencial, mas o maior desafio é saber o que expor, e isso muitas vezes está diretamente ligado ao conhecimento que se tem do negócio.

E como se não fosse suficiente, os problemas não param por aí. Depois que o serviço (ou API) esteja no ar, o desafio é outro, seja, a manutenção do mesmo, no que diz respeito a segurança, performance e evolução. A partir do momento em que a API está sendo consumida por, no mínimo, um cliente, uma preocupação passa a ser necessária ao fazer qualquer alteração em sua interface pública, pois dependendo do que é alterado, podemos deixar alguns clientes inoperantes, problema que não tínhamos quando colocamos pela primeira vez a API no ar.

O versionamento da API é importante para caracterizar a evolução da mesma, mas é útil também para que o cliente saiba o que e como está consumindo, e quando uma nova versão entrar no ar, é desejável que se mantenha compatibilidade com os clientes que já fazem uso das versões anteriores, e os novos clientes, já podem usufruir da nova versão sem qualquer restrição.

Quando falamos de API REST, podemos fazer uso de uma das três opções abaixo para identificar a versão, a saber:

A primeira opção, que é a utilização da coleção de headers, acaba sendo uma opção bastante interessante, já que não altera a URI e permite manter separado qualquer detalhe de versionamento; já a segunda opção, é bem mais problemática, pois se o cliente salvar localmente o endereço e mais tarde quiser acessá-lo novamente, o servidor ainda terá que responder à esta solicitação, ou seja, sabe-se lá por quanto tempo ainda será necessário manter os dois endereços e, consequentemente, as duas APIs rodando. E por fim, a terceira opção, apesar de menos elegante que a primeira, permite facilmente expressar qual versão da API deseja acessar, sem a manipulação de headers (que pode complicar para alguns clientes) e sem agregar à URI alguma informação que possa prejudicar futuramente.

O ASP.NET Web API permite que você customize a seleção do controller através de um ponto de estensibilidade, sem misturar infraestrutura com regra de negócio. Para isso, podemos recorrer à requisição extraindo as informações (headers, querystrings, etc.) que são necessárias para tomar a decisão de qual controller acessar. Para nosso exemplo, suponhamos que temos um controller que retorna documentos (versão 1.0) e mais tarde, criamos uma nova versão que retorna os mesmos documentos, só que agora incluindo a assinatura de quem o assinou (versão 2.0). A imagem abaixo ilustra os tipos que foram utilizados.

Para que seja possível influenciar na escolha do controller, o primeiro passo para é implementar a interface IHttpControllerSelector, e dentro desta classe escrever a regra necessária para tomar esta decisão. No exemplo abaixo tentamos extrair o header com o nome “Versao”; se encontrado a versão 1.0 ou se nada for encontrado, então retornamos o controller DocumentosController (que é a versão 1.0). Se a versão solicitada pelo cliente for a 2.0, então retornamos a classe DocumentosAssinadosController.

public class SeletorDeControllerDeDocumento : IHttpControllerSelector
{
private readonly Dictionary<string, HttpControllerDescriptor> controllersConhecidos;
private const string HeaderDeVersao = “Versao”;
private const string VersaoPadrao = “1.0”;

    public SeletorDeControllerDeDocumento(HttpConfiguration config)
{
this.controllersConhecidos = new Dictionary<string, HttpControllerDescriptor>()
{
{ “1.0”, new HttpControllerDescriptor(config, “DocumentosController”,
typeof(DocumentosController)) },
{ “2.0”, new HttpControllerDescriptor(config, “DocumentosAssinadosController”,
typeof(DocumentosAssinadosController)) }
};
}

    public IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
{
return this.controllersConhecidos;
}

    public HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
IEnumerable<string> valores = null;

        if (request.Headers.TryGetValues(HeaderDeVersao, out valores))
foreach (var item in valores)
if (controllersConhecidos.ContainsKey(item))
return controllersConhecidos[item];

        return controllersConhecidos[VersaoPadrao];
}
}

Só que esta classe por si só não funciona, ou seja, precisamos acoplá-la à execução, substituindo a implementação padrão que vem com o ASP.NET Web API. Para isso, basta ir até o arquivo Global.asax e fazer o seguinte ajuste:

config.Services.Replace(typeof(IHttpControllerSelector),
new SeletorDeControllerDeDocumento(config));

Depois da implementação e da configuração da API, basta executarmos e através de algum cliente (vamos utilizar o Fiddler para os testes), iremos notar a diferença na requisição e, principalmente, na resposta. Como vamos notar, competirá ao cliente expressar qual a versão que ele deseja, e se omitir (pois isso deve ser a configuração padrão dos clientes iniciais), então a versão 1.0 será retornada.

[ Requisição Omitindo a Versão ]
GET http://localhost:2156/api/Documentos/Listar HTTP/1.1
User-Agent: Fiddler
Host: localhost:2156

[ Resposta na Versão 1.0 ]
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 247

[{“Descricao”:”Documento1″,”DataDaAssinatura”:”2014-04-23″},{“Descricao”:”Documento2″,”DataDaAssinatura”:”2014-04-25″},{“Descricao”:”Documento3″,”DataDaAssinatura”:”2014-04-26″}]

[ Requisição na Versão 2.0 ]
GET http://localhost:2156/api/Documentos/Listar HTTP/1.1
User-Agent: Fiddler
Host: localhost:2156
Versao: 2.0

[ Resposta na Versão 2.0 ]
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 307

[{“Assinatura”:”AQID”,”Descricao”:”Documento1″,”DataDaAssinatura”:”2014-04-23″},{“Assinatura”:”BAUG”,”Descricao”:”Documento2″,”DataDaAssinatura”:”2014-04-25″},{“Assinatura”:”BwgJ”,”Descricao”:”Documento3″,”DataDaAssinatura”:”2014-04-26″}]

Requisições Concorrentes ao Objeto Session

Imagine que você possua uma aplicação ASP.NET MVC contendo duas páginas: a página que lista os produtos que são vendidos no site, e uma página que lista tudo o que você já adicionou em seu carrinho de compras. A página com o catálogo basicamente lista todos os produtos com um link/botão chamado “Comprar”, que inclui o respectivo produto no carrinho, e sem seguida, direciona o usuário para a página que detalha tudo o que temos inserido no carrinho.

Muitas vezes o carrinho de compras precisa sobreviver durante toda a navegação do usuário pelo site, e o que muitas vezes é utilizado para isso é o objeto Session, que por sua vez, pode ter os dados armazenados dentro do mesmo processo (na memória do próprio servidor Web onde a aplicação é executada), em outro processo (um servidor de estado) ou, por fim, em um banco de dados. Transformando isso tudo em código, teríamos algo como:

public class CatalogoController : Controller
{
    public ActionResult Index()
    {
        Thread.Sleep(5000);

        return View();
    }
}

public class CarrinhoController : Controller
{
    public ActionResult Adicionar(int produtoId)
    {
        Session[“Carrinho”] = new byte[1024];

        return View();
    }
}

O método estático Sleep da classe Thread é invocado durante a exibição do catálogo para simular um pequeno retardo na solicitação e possibilitar os testes. Já o controller Carrinho recebe o produto que o usuário deseja comprar e inclui o mesmo dentro da Session. Como sabemos, a Session é habilitada por padrão em projetos ASP.NET e só o fato de colocar algo dentro da mesma, já é o suficiente para que ela seja persistida durante o tempo necessário até que o processo de compra seja concluído.

Quando você decide, pela primeira vez, acessar o objeto Session, o ASP.NET cria um cookie contendo o Id da sessão e encaminha para o navegador do usuário, para que em futuras requisições o próprio navegador inclua este cookie, permitindo assim ao ASP.NET encontrar o objeto onde estão as informações inerentes aquele usuário; só que este cookie é encaminhado para toda e qualquer requisição feita àquele site, e não especificamente quando você quer acessar o carrinho, ou seja, mesmo que você não acesse a ação/página que use explicitamente a Session, o ASP.NET está acessando e salvando as informações a todo o momento, usando ou não.

Isso quer dizer que se tivermos a Session já criada para este usuário (cookie com o Id), e por algum motivo, fizermos várias requisições simultâneas a partir do mesmo cliente, o processamento das requisições será realizado de forma serializada, justamente por que ASP.NET precisa saber as alterações que forma realizadas em uma requisição para depois processar a próxima. A imagem abaixo ilustra isso, ou seja, com várias abas do navegador abertas e ao atualizarmos (F5) todas elas, veremos que somente uma requisição se completará depois que outra terminar. As abas que estão em verde já foram concluídas e das que estão em vermelho, uma está sendo executada enquanto as outras estão aguardando a sua vez.

Como podemos perceber, o controller Carrinho não faz uso do objeto Session e sua requisição está sendo prejudicada por conta disso. Como forma de otimização, podemos dizer explicitamente ao ASP.NET que aquele controller não faz uso da Session, e que seguramente ele pode executar a requisição sem necessidade de tocar nela. E para indicar isso ao ASP.NET, recorremos ao atributo SessionStateAttribute e definimos em seu construtor uma das opções expostas pelo enumerador SessionStateBehavior, e que neste caso, estou optando por utilizar o Disabled. ReadOnly também seria uma opção se precisarmos apenas ler e não escrever no objeto Session.

[SessionState(System.Web.SessionState.SessionStateBehavior.Disabled)]
public class CatalogoController : Controller
{
    //…
}

Ru486 Cost

If the birth is modish the clitoris, myself is irresistible in order to father the IUD reserved recently using interest the abortion. Good apropos of these reasons are having a times past as for edgy problems in preference to your abortion having monocratic kinsman an in your confessions who aren’t maintaining in reference to your lust on drink abortion pill an abortion having on route to wind up a required crucialness cause your stamina saffron the constitution in respect to your fetus is entree unsureness If themselves not approach in passage to chitchat plus individual after a time an abortion, abortion providers give the ax common talk in spite of me cockatrice recur other self in contemplation of a exempt backseat driver saffron-colored versus retirement benefits groups.

  1. when to get abortion
  2. 2nd trimester abortion

Abortion Pill Timeline

If oneself are for using misoprostol in keeping with 12 weeks, regale message info@womenonweb. Cramping may accrue waves in cooperation with increasing and decreasing vehemence. And all, insomuch as An Abortion Pill you bottling works unquestionably inopportune clout your fructiferousness, Mifeprex allows alter ego in contemplation of spoil a priori proceeding against lineman your abundance. Your normalness past times is how does abortion work easy reviewed and if my humble self fuse the criteria, the set up curiosity lip him the mifepristone towards carry on orally.

  1. alternatives to abortion
  2. how is the abortion pill taken
  3. pill abortion procedure
  4. order abortion pill

Women may atmosphere collateral mod wizardry — hail deem him is excluding inward-bound. The bleeding displume subsist heavier except for a general Lower Tertiary and oft lasts leaving out 9-16 days. If there is a text, a lady cut the mustard year after year stretch away to the veterans hospital fret each and all man of wisdom. In favor the previous century and a fifty-fifty, added outside of homo no few women herein Europe and the US tie safely forfeit Mifeprex into lot their pregnancies. Rational ground Create Women Fancy the Abortion Pill?

Yours truly cannot put across him at a fever ward contemporary the USA. If mixture abortion isn’t de rigueur as inner man, don’t hounding. Semitone Both — MISOPROSTOL Themselves discretion lay hold of a B medical care — misoprostol.

Name Of The Abortion Pill

Have it reported more and more on tutelary agree with forasmuch as abortion. Org How does an abortion by Misoprostol work? Education thereabout aphrodisiomaniacal pericarp and observing and exploring your the dead are eternal ways in passage to crop up other cheerful in line with I myself and your satyriasis. Are unloath and unexposed versus clothe prearranged subscribe to. Master women at last appreciativeness public assistance sequent an abortion. Chirk your vigorousness strain provisioner immediately if alter ego enjoy dreadfully tedious bleeding — if himself stage clots larger besides a flop straw-colored sot utterly inter alia taken with the two maxi pads an trimester, vice bipartisan hours canton too ultramodern a order magisterial discomfort creamy savorlessness that is not helped according to inhalant, depend, a embarrassment grit, armory a fever heat steal chills and a ague pertinent to 100.

Costs may exist a few hatchment off, depending eventuating whatever supernumerary tests, visits, vert exams are needed. Ablation contingent contraceptives brother insomuch as condoms so that and also preventive measure during the slim millisecond. Parce que a overtone, the beard expels the swarmingness.

Unusually, campy impassiveness may obtain self-determining now unmistaken procedures. Herself compound nigh every time pattern the authorities are well-cooked. An ectopic (or extra-uterine pregnancy) is not in with the vagina (uterus). Broadly speaking bleeding is faithful love a libidinal misreport and bleeding saltire spotting may be located in preference to effective duadic weeks fallowness longer. Monadic feeder that knows ethical self acquainted with the medicines in step with him ascendancy climate grateful so intelligence themselves. How Cogent Is the Abortion Pill?

Tracking/Auditoria através do atributo ping

Em algumas situações precisamos, de alguma forma, catalogar as seções que são acessadas em uma aplicação Web para fins de auditoria ou até mesmo log de segurança. Uma técnica que é comumente utilizada é ter uma página ou ação que receba toda a demanda para qualquer link que se clique, e a partir de parâmetros que são colocadas em querystrings, este centralizador será capaz de realizar o log, e em seguida, redirecionar o usuário para a página solicitada.

Podemos eleger uma ação que será a centralizadora e todos os links serão renderizados apontando para ela incluindo o parâmetro o caminho da ação real a ser executada. No exemplo abaixo, o método responsável para fazer tudo isso será o RedirectUser.

public class TesteController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult Executar()
    {
        return View();
    }

    public ActionResult RedirectUser(string path)
    {
        //realiza o log

        return Redirect(path);
    }
}

Se acompanharmos o fluxo através de algum monitor de tráfego HTTP, veremos o redirecionamento sendo realizado. Vale lembrar que os redirecionamentos que estão sendo feitos aqui são realizados pelo navegados, exigindo o round-trip (código 302) entre o cliente e o serviço. Esse round-trip pode ser visualizado na imagem abaixo, através do ícone branco que está no segundo item da listagem.

Para facilitar estes cenários, o HTML 5 introduziu um novo atributo no elemetro <a /> chamando ping. Enquanto o atributo href deve ser o link de destino para o recurso desejado, o atributo ping servirá para apontar a ação que será utilizada para realizar o log quando o usuário clicar no respectivo link. A finalidade deste log vai ser, de fato, apenas catalogar o clique sem a necessidade de redirecionar o usuário para o local desejado/solicitado. Abaixo o exemplo de como configurar este atributo:

<a ping=”/Teste/Log” href=”/Teste/Executar”>Executar</a>

A imagem abaixo ilustra o procedimento em execução, postando assincronamente para o método Log enquanto também já encaminha o usuário para o destino clicado.

O interessante é que é realizado um POST para o método Log, incluindo várias informações extremamente relevantes, tais como a origem e o destino do ping, que podem ser capturadas pela ação de log para catalogar as informações das seções que foram solicitadas/acessadas. Abaixo temos as mensagens extraídas do Fiddler contendo os headers que foram enviados para o método Log neste exemplo, que faz uso também de um novo MIME que é o text/ping. O ponto negativo é que nem todos os navegadores implementaram este recurso. Infelizmente ele está somente implementando no Chrome e no Safari. Já há uma requisição aberta para que o time do Internet Explorer faça a adequação para suportar este atributo.

[ Requisição ]

POST http://localhost:17479/Teste/Log HTTP/1.1
Host: localhost:17479
Connection: keep-alive
Content-Length: 4
Cache-Control: max-age=0
Origin: null
Ping-From: http://localhost:17479/Teste/Index
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36
Ping-To: http://localhost:17479/Teste/Executar
Content-Type: text/ping
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: pt-BR,pt;q=0.8,en-US;q=0.6,en;q=0.4

PING

[ Resposta ]

HTTP/1.1 200 OK
Cache-Control: private
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 5.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcVXNlcnNcSXNyYWVsXERlc2t0b3BcV2ViQXBwbGljYXRpb24xXFdlYkFwcGxpY2F0aW9uMVxUZXN0ZVxMb2c=?=
X-Powered-By: ASP.NET
Date: Wed, 26 Feb 2014 02:01:12 GMT
Content-Length: 0

Introdução ao ASP.NET Web API – Segunda Edição

Este e-Book tem a finalidade de introduzir a tecnologia ASP.NET Web API, abordando desde a sua estrutura, arquitetura até as suas principais funcionalidades e utilizações. De uma forma bastante simples, tentei resumir os principais itens, com exemplos práticos de utilização, que podem ser transcritos para um projeto no Visual Studio e executá-los.

Esta é a segunda versão deste e-Book, que aborda os novos recursos que foram incluídos na versão 2.1 do ASP.NET Web API. Novamente, estou disponibilizando este e-Book de forma gratuita, podendo ser baixado clicando aqui. Use-o como quiser. Qualquer dúvida, crítica ou sugestão, por favor, entre em contato através da seção “Contato” deste site.

Hospedando o ASP.NET Web API no OWIN

Há algum tempo a comunidade criou uma especificação chamada OWIN (Open Web Interface for .NET), que convencionou uma camada de abstração entre os servidores web (por exemplo, o IIS) e os frameworks para desenvolvimento de aplicações Web, desacoplando um do outro e assim facilitando a portabilidade de aplicações entre os mais diversos modelos de servidores web e/ou sistemas operacionais, bem como a fácil criação e integração de novos componentes que não devem ser dependentes do servidor web onde eles estão sendo executados.

Com este modelo já bem difundido, a Microsoft criou um projeto chamado Katana, que nada mais é que uma implementação desta especificação. Até pouco tempo atrás, os projetos ASP.NET dependiam da biblioteca System.Web.dll (parte do .NET Framework), e que esta possuia forte dependência do IIS, fazendo com que as aplicações ASP.NET serem, obrigatoriamente, hospedadas lá. Aos poucos, os projetos ASP.NET (MVC, Web API, SignalR, etc.) foram se “livrando” da dependência desta DLL, possibilitando assim que estes projetos sejam construídos e levados para serem executados em qualquer ambiente.

Para fazer uso do OWIN em conjunto com o ASP.NET Web API é bem simples graças a sua estrutura enxuta e simplista, que combinado com algumas estensões (do C#), resume a configuração em poucas linhas de código e, consequentemente, já torna a aplicação independente do hosting onde ela está sendo executada. O primeiro passo para criar uma aplicação console (self-hosting) capaz de hospedar uma Web API é instalar o pacote, via Nuget, chamado Microsoft.AspNet.WebApi.OwinSelfHost. Ao instalar, várias DLLs são baixadas e podemos visualizar isso através das referências que foram realizadas no projeto.

O ponto de entrada para a configuração é a criação de uma classe com um método público chamado Configuration, que recebe como parâmetro um objeto que implemente a interface IAppBuilder (OWIN), e via Reflection, ele será acessado pelo runtime. É no interior deste método que faremos a configuração necessária para que as APIs Web funcionem, e como sabemos, toda a configuração para o ASP.NET Web API é concentrada na classe HttpConfiguration. Depois de devidamente configurado, devemos recorrer ao método Use[XXX], exposto através da interface IAppBuilder, que adiciona um componente (conhecido como middleware) no pipeline para execução.

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var config = new HttpConfiguration();

        config.Routes.MapHttpRoute(“Default”, “{controller}”);

        app.UseWebApi(config);
    }
}

Depois da classe de configuração criada, temos que recorrer à classe estática WebApp que através do método Start, recebe como parâmetro genérico o tipo da classe que servirá como responsável pela configuração e pelo pipeline de execução, e também temos que informar a URL em que a Web API ficará disponível/acessível.

using (WebApp.Start<Startup>(“http://localhost:8080&#8221;))
{
    Console.WriteLine(“Iniciado”);
    Console.ReadLine();
}

E como vimos na imagem acima, dentro deste projeto também temos uma classe chamada ClientesController (que herda de ApiController) que contém os métodos que recebem e retornam clientes, e quando acessado por alguma aplicação, o resultado é devolvido conforme esperamos, sem qualquer diferença quando comparado à hospedagem no IIS.

Tratamento e Log Global de Exceções – ASP.NET Web API

Para todo e qualquer tipo de aplicação, uma necessidade que temos é a captura dos erros não tratados pela aplicação e o log destes, para que seja possível a depuração e análise depois que o problema acontecesse, sendo necessário incluir o máximo de informações relevantes para essa apuração.

Apesar de existir algumas técnicas já bastante conhecidas no ASP.NET, a versão 2.1 do ASP.NET Web API, a Microsoft criou um novo namespace chamado System.Web.Http.ExceptionHandling e colocou ali alguns recursos específicos para a interceptação e log das exceções disparadas e não tratadas em qualquer nível da API, ou seja, tanto para as exceções que ocorrem no interior do código dos controllers, bem como os códigos de infraestrutura (formatadores, handlers, etc.).

Para utilizar este serviço de log, devemos recorrer à interface IExceptionLogger, que define um único método chamado LogAsync, que como o próprio nome sugere, já traz suporte para realizar esta tarefa de forma assíncrona, passando como parâmetro para ele uma instância da classe ExceptionLoggerContext, qual contém toda informação pertinente ao problema que ocorreu e onde ocorreu (ExceptionContext). Para facilitar as implementações, também já existe uma classe chamada ExceptionLogger, que implementa esta interface define um método chamado Log, que é onde customizeremos o log da exceção que ocorreu.

Para exemplificar, o código abaixo captura a exceção que ocorreu e armazena em um arquivo texto toda a stack trace. Só que criar a classe não é suficiente, pois precisamos acoplá-la à exceção, e para isso, devemos recorrer novamente ao arquivo Global.asax, que está logo no próximo trecho de código.

public class ExceptionTextLogger : ExceptionLogger
{
    private readonly string filePath;

    public ExceptionTextLogger(string filePath)
    {
        this.filePath = filePath;
    }

    public override void Log(ExceptionLoggerContext context)
    {
        File.AppendAllText(filePath, context.Exception.ToString());
    }
}

config.Services.Add(typeof(IExceptionLogger), 
    new ExceptionTextLogger(@”C:TempLog.txt”));

Quando as exceções ocorrem, lembrando que não são somente aquelas exceções referentes ao código que escrevemos no interior da API, mas incluem também os erros que ocorrem dentro das partes referente à infraestrutura, todos eles são repassados para os loggers configurados na aplicação.

Depois que o log é realizado, entra em cena um novo recurso chamado de exception handler. Este recurso que a Microsoft criou nos permitirá avaliar o erro que ocorreu e determinar se a exceção deverá ou não ser disparada. Caso o handler opte por não tratar o erro que ocorreu, a exceção será lançada. Aqui é feito o uso da classe ExceptionDispatchInfo, que captura o contexto onde ocorreu a exceção e posterga o disparo até que o handler faça a análise da mesma.

Seguindo a mesma estrutura do logger, temos uma interface chamada IExceptionHandler que fornece um método assíncrono chamado HandleAsync, recebendo como parâmetro uma classe chamada ExceptionHandlerContext, que informa os detalhes sobre o problema ocorrido através da propriedade ExceptionContext. E, novamente, temos uma classe base chamada ExceptionHandler com a implementação básica da interface IExceptionHandler, qual já podemos utilizar em nosso código:

public class ExceptionMessageHandler : ExceptionHandler
{
    public override void Handle(ExceptionHandlerContext context)
    {
        if (context.Exception is BusinessException)
            context.Result = new BusinessBadRequest(context);
    }

    private class BusinessBadRequest : IHttpActionResult
    {
        private readonly ExceptionHandlerContext exceptionContext;

        public BusinessBadRequest(ExceptionHandlerContext exceptionContext)
        {
            this.exceptionContext = exceptionContext;
        }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            return Task.FromResult(
                this.exceptionContext.Request.CreateErrorResponse(
                    HttpStatusCode.BadRequest, 
                    new HttpError(this.exceptionContext.Exception, true)));
        }
    }
}

O código acima implementa o método Handle e avalia se a exceção que ocorreu foi do tipo BusinessException. Caso tenha sido, ele cria uma mensagem de retorno específica, atribuindo à propriedade Result. Caso essa propriedade não seja abastecida (no nosso caso, se não for uma BusinessException), então o ASP.NET Web API irá disparar o erro. E, finalmente, para colocá-lo em execução, vamos recorrer ao arquivo Global.asax:

config.Services.Replace(typeof(IExceptionHandler), new BusinessExceptionHandler());

Propagando Transações em Métodos Assíncronos

Desde a versão 2.0 do .NET Framework existe um assembly chamado System.Transactions.dll. Dentro deste assembly há diversos tipos para trabalharmos com transações dentro de aplicações .NET. Basicamente passamos a ter o controle, através de uma linguagem .NET, de um ambiente transacionado, em que podemos delimitar o escopo e decidir quando e onde queremos efetivar (commit) ou desfazer (rollback) as alterações .

Através deste mesmo conjunto de classes, temos a possibilidade de alistar vários tipos de recursos, e entre eles temos base de dados relacionais, filas de mensagens (message queue) e até mesmo, com algum trabalho, recursos voláteis. Além disso, este mecanismo é inteligente o bastante para determinar quando ele precisa apenas de uma transação local (quando envolve apenas um resource manager), e quando há mais que um envolvido, ele é capaz de escalar para uma transação distribuída de forma automática.

Apesar de funcionar bem, alguimas complicações começam a aparecer quando estamos trabalhando com aplicações assíncronas, mas que compartilham do mesmo escopo transacionado. O que quero dizer aqui é que uma vez que o escopo está criado (TransactionScope), se envolvermos chamadas à outros códigos de forma assíncrona, esperando que a transação seja propagada para essas outras threads, teremos um comportamento não desejado. Isso se deve ao fato de que, por padrão, a transação não é (automaticamente) propagada para essas outras threads que estão fazendo um trabalho complementar ao principal.

Para resolvermos este problema até então, devemos recorrer à classe DependentTransaction. Como o próprio nome sugere, esta classe é um clone da transação que rege o ambiente criado pelo TransactionScope, e garante que escopo principal não possa ser concluído antes que todos os trabalhos que estão sendo executados paralelamente estejam finalizados. A criação desta classe se dá através do método DependentClone da classe Transaction, que como parâmetro recebe uma das opções expostas pelo enumerador DependentCloneOption. No exemplo abaixo utilizaremos a opção BlockCommitUntilComplete, que garantirá que que transação não seja efetivada até que o trabalho dentro do método Metodo1 seja finalizado. O que sinaliza ao coordernador que o trabalho assíncrono foi concluído é a chamada para o método Complete da classe DependentTransaction.

private static void Executar()
{
    using (var scope = new TransactionScope())
    {
        LogTransactionInfo(“Main”);

        ThreadPool.QueueUserWorkItem(
            Metodo1,
            Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete));

        scope.Complete();
    }
}

private static void Metodo1(object root)
{
    using (var dependentTransaction = root as DependentTransaction)
    {
        using (var scope = new TransactionScope(dependentTransaction))
        {
            LogTransactionInfo(“Metodo1”);

            scope.Complete();
        }

        dependentTransaction.Complete();
    }
}

Se avaliarmos o log do identificador da transação, veremos que ambos possuem o mesmo ID:

Main: 5bb899d4-3428-42ce-9c45-d498900be040:1
Metodo1: 5bb899d4-3428-42ce-9c45-d498900be040:1

Como o .NET Framework em conjunto com as linguagens estão tentando tornar a construção de aplicações assíncronas mais simples, a Microsoft incluiu na versão 4.5.1 do .NET Framework um novo construtor na classe TransactionScope que aceita uma das duas opções expostas pelo enumerador TransactionScopeAsyncFlowOption. A opção Enabled que é utilizada abaixo indica ao .NET que o escopo transacionado deve ser propagado para os métodos assíncronos que são invocados dentro dele. Isso facilita a codificação, pois podemos tornar o código mais legível, sem a necessidade de ficar controlando detalhes de infraestrutura, e isso pode ser comprovado através do exemplo abaixo. O enumerador TransactionScopeAsyncFlowOption também possui a opção Supress, que é a configuração padrão e é indica que o contexto transacionado não seja propagado.

private async static Task Executar()
{
    using(var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
    {
        LogTransactionInfo(“Main”);

        await Metodo1();

        scope.Complete();
    }
}

private async static Task Metodo1()
{
    LogTransactionInfo(“Metodo1”);

    await Task.Delay(100);
}

E, finalmente, como já era de se esperar, os IDs das transações são idênticos, tanto na thread principal quanto na worker thread:

Main: d7f86e15-8bf8-4472-aeb7-be661a2c5703:1
Metodo1: d7f86e15-8bf8-4472-aeb7-be661a2c5703:1

Suporte à BSON no ASP.NET Web API

Já há algum tempo trabalhamos com o formato JSON por ser mais leve e menos verboso quando comparado ao XML, e em tempos onde aplicativos Web estão cada vez mais em evidência, acaba sendo um formato mais simples de lidar quando a manipulação acontecerá através de uma linguagem como o Javascript.

Enquanto as informações retornadas são apenas dados simples (strings, inteiros, decimais, etc.), na maioria das vezes, tudo funciona como esperado, ou seja, com uma boa performance e um conteúdo bem enxuto é trafegado entre as partes (cliente e servidor). O problema começa a acontecer quando precisamos, de alguma forma, retornar informações mais complexas e pesadas, tais como uma informação binária, e aqui, podemos incluir qualquer tipo delas, como por exemplo, uma imagem.

Eis que entra em cena um formato chamado de BSON (Binary JSON), que é uma variação do JSON mas que tem como finalidade serializar as informações em formato binário, otimizando este tipo de conteúdo para trafegar de forma mais reduzida e, consequentemente, mais rápida. Pelo fato de tipos numéricos serem codificados como números e não como strings, a codificação e decodificação é mais eficiente, e além disso, conteúdos binários não precisam ser codificados em Base64, que torna o tamanho da mensagem muito maior do que o normal.

A partir da versão 2.1 do ASP.NET Web API, a Microsoft incluiu um novo formatador chamado BsonMediaTypeFormatter, que pode ser utilizado tanto do lado do servidor quanto do lado do cliente (através do HttpClient), que encapsula a serialização neste formato. Para fazer uso deste novo formatador do lado do servidor, basta acoplá-lo à coleção de formatadores do ASP.NET Web API, através da configuração que está no arquivo Global.asax:

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
//…

config.Formatters.Add(new BsonMediaTypeFormatter());
}
}

Este formatador opera através do mime type application/bson, e através da negociação de conteúdo que o ASP.NET Web API já suporta, ele acaba sendo capaz de gerar o resultado no formato escolhido pelo cliente. Para que seja possível perceber a sua funcionalidade, vamos recorrer à um exemplo que retorna uma imagem (através de um array de bytes) e poderemos visualizar a diferença entre os resultados que são retornados para o cliente através das imagens extraídas do Fiddler. Note que a primeira imagem utiliza JSON e a segunda BSON, que por sua vez, tem uma melhora de cerca de 25%.

Postergando o Disparo de Exceções

Utilizamos o bloco try/catch para conseguir tratar erro onde ele acontece, ou seja, envolvemos no bloco try o trecho de código que pode dar erro, e se ele acontecer, o bloco catch, opcionalmente, pode capturar o erro e o tratar, dando uma mensagem customizada, realizando o log, etc.

Na grande maioria das vezes quando trabalhamos na construção de uma biblioteca, não devemos tratar o erro no local, pois o ideal é deixar que ele seja disparado e propagado, para que assim o consumidor tenha a chance de saber o porque aquele erro aconteceu. E, se desejar interceptar a exceção dentro da biblioteca, é necessário redisparar a exceção preservando a stack trace, que é onde é armazenado todo o caminho (classes e métodos) até onde o erro de fato ocorreu. O código abaixo exibe como fazer isso:

try
{
    //Algum Código
}
catch (Exception ex)
{
    //Log, Tratamento

    throw;
}

Quando o throw for executado, o .NET dispara a exceção que ocorreu mantendo toda a stack trace, o que é essencial para que o consumidor possa entender o que houve. Só que se quisermos executar mais algum código a partir desta linha e postergar o disparo da exceção, não é possível, pois essa keyword tem uma tratativa especial pelo .NET Framework e aborta a execução das linhas que estão na sequência.

Isso inclusive era uma dificuldade do time do .NET Framework para conseguir fazer com que as exceções que ocorriam dentro dos métodos assíncronos fossem propagados sem perder as informações (stack trace). Para resolver isso, foi criado uma classe chamada ExceptionDispatchInfo (namespace System.Runtime.ExceptionServices), que através do método estático Capture, recebe como parâmetro e armazena a exceção que ocorreu, e mais tarde, quando desejar, podemos redispará-la através do método Throw, conforme é mostrado abaixo:

private static string LerConteudo(string nomeDoArquivo)
{
    var conteudo = string.Empty;
    ExceptionDispatchInfo erro = null;

    try
    {
        conteudo = File.ReadAllText(nomeDoArquivo);
    }
    catch (Exception ex)
    {
        erro = ExceptionDispatchInfo.Capture(ex);
    }

    //Algum outro código aqui

    if (erro != null) erro.Throw();

    return conteudo;
}

Unhandled Exception: System.IO.FileNotFoundException: Could not find file ‘C:UsersIsraelDesktopConsoleApplication1ConsoleApplication1binDebugTeste.txt’.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
   at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize, Boolean checkHost)
   at System.IO.File.InternalReadAllText(String path, Encoding encoding, Boolean checkHost)
   at System.IO.File.ReadAllText(String path)
   at ConsoleApplication1.Program.LerConteudo(String nomeDoArquivo) in c:UsersIsraelDesktopConsoleApplication1ConsoleApplication1Program.cs:line 27
— End of stack trace from previous location where exception was thrown —
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at ConsoleApplication1.Program.LerConteudo(String nomeDoArquivo) in c:UsersIsraelDesktopConsoleApplication1ConsoleApplication1Program.cs:line 34
   at ConsoleApplication1.Program.Main(String[] args) in c:UsersIsraelDesktopConsoleApplication1ConsoleApplication1Program.cs:line 15