Uma das razões do sucesso do ASP.NET é que ele reduz a barreira de entrada para desenvolvedores Web. Você não precisa ter doutorado em ciência da computação para escrever código ASP.NET. Muitos desenvolvedores ASP.NET que conheço no trabalho são autodidatas e escreviam planilhas do Microsoft® Excel® antes de escreverem C# ou Visual Basic®. Agora, eles estão escrevendo aplicações Web e, em geral, merecem elogios pelo trabalho que estão realizando.
Mas com o poder vem a responsabilidade, e até mesmo desenvolvedores ASP.NET experientes podem cometer erros. Em muitos anos de consultoria em projetos ASP.NET, descobri que certos erros têm maior probabilidade de levar a defeitos recorrentes. Alguns desses erros podem afetar o desempenho. Outros erros podem inibir a escalabilidade. Alguns bugs também podem custar às equipes de desenvolvimento um tempo valioso para rastrear bugs e comportamentos inesperados.
Aqui estão 10 armadilhas que podem causar problemas durante o lançamento de aplicativos de produção ASP.NET e maneiras de evitá-las. Todos os exemplos vêm de minha própria experiência na construção de aplicativos Web reais em empresas reais e, em alguns casos, forneço contexto descrevendo alguns dos problemas que a equipe de desenvolvimento do ASP.NET encontrou durante o processo de desenvolvimento.
LoadControl e cache de saída Existem muito poucos aplicativos ASP.NET que não usam controles de usuário. Antes das páginas mestras, os desenvolvedores usavam controles de usuário para extrair conteúdo comum, como cabeçalhos e rodapés. Mesmo no ASP.NET 2.0, os controles de usuário fornecem uma maneira eficiente de encapsular conteúdo e comportamento e de dividir a página em regiões cuja capacidade de armazenamento em cache pode ser controlada independentemente da página como um todo (um processo denominado segmentos. Uma forma especial de armazenamento em cache de saída). ).
Os controles de usuário podem ser carregados de forma declarativa ou forçada. O carregamento forçado depende de Page.LoadControl, que instancia um controle de usuário e retorna uma referência de controle. Se o controle de usuário contiver membros de um tipo personalizado (por exemplo, uma propriedade pública), você poderá converter a referência e acessar o membro personalizado do seu código. O controle de usuário na Figura 1 implementa uma propriedade chamada BackColor. O código a seguir carrega o controle de usuário e atribui um valor a BackColor:
protected void Page_Load(object sender, EventArgs e){// Carregue o controle de usuário e adicione-o à página Control control = LoadControl("~/MyUserControl.ascx") ;PlaceHolder1 .Controls.Add(control);//Define sua cor de fundo ((MyUserControl)control).BackColor = Color.Yellow;}
O código acima é na verdade muito simples, mas é uma armadilha que espera o desenvolvedor incauto cair. Você consegue encontrar a falha?
Se você adivinhou que o problema está relacionado ao cache de saída, você está correto. Como você pode ver, o exemplo de código acima compila e funciona bem, mas se você tentar adicionar a seguinte instrução (que é perfeitamente legal) ao MyUserControl.ascx:
<%@ OutputCache Duration="5" VaryByParam="None" %>
Então, na próxima vez que você executar a página, você verá uma InvalidCastException (que bom!) e a seguinte mensagem de erro:
"Não é possível converter um objeto do tipo 'System.Web.UI.PartialCachingControl' para digitar 'MyUserControl'."
Portanto, esse código funciona bem sem a diretiva OutputCache, mas falha se a diretiva OutputCache for adicionada. O ASP.NET não deveria se comportar dessa maneira. As páginas (e controles) devem ser independentes do cache de saída. Então, o que isso significa?
O problema é que quando o cache de saída está habilitado para um controle de usuário, LoadControl não retorna mais uma referência à instância de controle, em vez disso, ele retorna uma referência a uma instância de PartialCachingControl, que pode ou não agrupar a instância de controle, dependendo se o cache de saída está ativado; a saída do controle é cache. Portanto, se um desenvolvedor chamar LoadControl para carregar dinamicamente um controle de usuário e converter a referência de controle para acessar métodos e propriedades específicas do controle, ele deverá prestar atenção em como faz isso para que o código seja executado independentemente de haver ou não um controle. Diretiva OutputCache.
A Figura 2 ilustra a maneira correta de carregar dinamicamente um controle de usuário e converter a referência de controle retornada. Aqui está um resumo de como funciona:
• Se faltar uma diretiva OutputCache no arquivo ASCX, LoadControl retornará uma referência MyUserControl. Page_Load converte a referência para MyUserControl e define a propriedade BackColor do controle.
• Se o arquivo ASCX incluir uma diretiva OutputCache e a saída do controle não estiver armazenada em cache, LoadControl retornará uma referência ao PartialCachingControl cuja propriedade CachedControl contém uma referência ao MyUserControl subjacente. Page_Load converte PartialCachingControl.CachedControl em MyUserControl e define a propriedade BackColor do controle.
• Se o arquivo ASCX incluir uma diretiva OutputCache e a saída do controle estiver armazenada em cache, LoadControl retornará uma referência ao PartialCachingControl cuja propriedade CachedControl está vazia. Observe que Page_Load não continua mais. A propriedade BackColor do controle não pode ser definida porque a saída do controle vem do cache de saída. Em outras palavras, não há nenhum MyUserControl para definir propriedades.
O código na Figura 2 será executado independentemente de haver uma diretiva OutputCache no arquivo .ascx. Embora pareça um pouco mais complicado, evitará erros irritantes. Simples nem sempre significa fácil de manter.
Voltar ao início Sessões e cache de saída Falando em cache de saída, tanto o ASP.NET 1.1 quanto o ASP.NET 2.0 têm um possível problema que afeta páginas de cache de saída em servidores que executam o Windows Server™ 2003 e o IIS 6.0. Eu pessoalmente vi esse problema ocorrer duas vezes em um servidor de produção ASP.NET e nas duas vezes ele foi resolvido desativando o buffer de saída. Mais tarde, aprendi que existe uma solução melhor do que desabilitar o cache de saída. Aqui está o que parecia quando encontrei esse problema pela primeira vez.
O que aconteceu foi que um site (vamos chamá-lo de Contoso.com aqui, que executa um aplicativo público de comércio eletrônico em um pequeno domínio da Web ASP.NET) entrou em contato com minha equipe reclamando que estava enfrentando um erro de "threading cruzado". Os clientes que usam o site Contoso.com muitas vezes perdem repentinamente os dados inseridos, mas em vez disso veem dados relacionados a outro usuário. Após uma pequena análise, descobrimos que a descrição do cross-threading não é precisa, o erro de “sessão cruzada” é mais apropriado. Parece que Contoso.com armazena dados no estado de sessão e, por algum motivo, os usuários se conectam ocasionalmente e aleatoriamente às sessões de outros usuários.
Um dos membros da minha equipe escreveu uma ferramenta de diagnóstico que registra os principais elementos de cada solicitação e resposta HTTP, incluindo o cabeçalho Cookie. Em seguida, ele instalou a ferramenta no servidor Web da Contoso.com e deixou-a funcionar por alguns dias. Os resultados são muito óbvios. Cerca de uma vez em cada 100.000 solicitações, o ASP.NET atribui corretamente um ID de sessão a uma sessão completamente nova e retorna o ID de sessão no cabeçalho Set-Cookie. Em seguida, ele retorna o mesmo ID de sessão (ou seja, o mesmo cabeçalho Set-Cookie) na próxima solicitação imediatamente adjacente, mesmo que a solicitação já esteja associada a uma sessão válida e o ID de sessão no cookie tenha sido enviado corretamente. Na verdade, o ASP.NET alterna aleatoriamente os usuários para fora de suas próprias sessões e os conecta a outras sessões.
Ficamos surpresos e decidimos descobrir o porquê. Primeiro verificamos o código-fonte do Contoso.com e, para nosso alívio, o problema não estava lá. Em seguida, para garantir que o problema não estava relacionado ao host do aplicativo no domínio Web, deixamos apenas um servidor em execução e encerramos todos os outros. O problema persiste, o que não é surpreendente, já que nossos registros mostram que os cabeçalhos Set-Cookie correspondentes nunca vêm de dois servidores diferentes. O ASP.NET gera acidentalmente IDs de sessão duplicados, o que é incrível porque ele usa a classe RNGCryptoServiceProvider do .NET Framework para gerar esses IDs, e os IDs de sessão são longos o suficiente para garantir que o mesmo ID nunca seja gerado duas vezes (pelo menos no próximo). não será gerado duas vezes em trilhões de anos). Além disso, mesmo que o RNGCryptoServiceProvider gere por engano números aleatórios repetidos, ele não explica por que o ASP.NET substitui misteriosamente o ID de sessão válido por um novo (que não é exclusivo).
Por palpite, decidimos dar uma olhada no cache de saída. Quando o OutputCacheModule armazena em cache uma resposta HTTP, ele deve ter cuidado para não armazenar em cache o cabeçalho Set-Cookie, caso contrário, uma resposta em cache contendo um novo ID de sessão conectará todos os destinatários da resposta em cache (e o usuário cuja solicitação gerou a resposta em cache). para a mesma sessão. Verificamos o código-fonte Contoso.com tem cache de saída habilitado em ambas as páginas. Desativamos o cache de saída. Como resultado, o aplicativo foi executado por vários dias sem um único problema de sessão cruzada. Depois disso, funcionou sem erros por mais de dois anos. Em outra empresa com um aplicativo diferente e um conjunto diferente de servidores web, vimos exatamente o mesmo problema desaparecer. Assim como em Contoso.com, eliminar o cache de saída resolve o problema.
Posteriormente, a Microsoft confirmou que esse comportamento decorre de um problema no OutputCacheModule. (Uma atualização pode ter sido lançada no momento em que você lê este artigo.) Quando o ASP.NET é usado com o IIS 6.0 e o cache no modo kernel está habilitado, o OutputCacheModule às vezes falha ao remover o cabeçalho Set-Cookie das respostas armazenadas em cache que ele passa para HTTP.sys. A seguir está a sequência específica de eventos que resulta no erro:
• Um usuário que não visitou recentemente o site (e, portanto, não tem uma sessão correspondente) solicita uma página que tenha o cache de saída habilitado, mas cuja saída não esteja disponível no momento no cache.
• A solicitação executa o código que acessa a sessão criada mais recentemente pelo usuário, fazendo com que o cookie de ID da sessão seja retornado no cabeçalho Set-Cookie da resposta.
• OutputCacheModule fornece saída para Http.sys, mas não pode remover o cabeçalho Set-Cookie da resposta.
• HTTP.sys retorna respostas armazenadas em cache em solicitações subsequentes, conectando outros usuários à sessão por engano.
A moral da história? O estado da sessão e o cache de saída do modo kernel não combinam. Se você usar o estado da sessão em uma página com cache de saída habilitado e o aplicativo estiver em execução no IIS 6.0, será necessário desativar o cache de saída no modo kernel. Você ainda se beneficiará do cache de saída, mas como o cache de saída no modo kernel é muito mais rápido que o cache de saída normal, o cache não será tão eficiente. Para obter mais informações sobre esse problema, consulte support.microsoft.com/kb/917072.
Você pode desativar o cache de saída no modo kernel para uma página individual incluindo o atributo VaryByParam="*" na diretiva OutputCache da página, embora isso possa resultar em um aumento repentino nos requisitos de memória. Outra abordagem mais segura é desativar o cache do modo kernel para todo o aplicativo, incluindo o seguinte elemento no web.config:
<httpRuntime enableKernelOutputCache="false" />
Você também pode usar uma configuração de registro para desabilitar o cache de saída no modo kernel globalmente, ou seja, desabilitar o cache de saída no modo kernel para todos os servidores. Consulte support.microsoft.com/kb/820129 para obter detalhes.
Cada vez que ouço um cliente relatar problemas intrigantes de sessão, pergunto se ele está usando cache de saída em alguma página. Se eles usarem cache de saída e o sistema operacional host for o Windows Server 2003, eu recomendaria que eles desabilitassem o cache de saída no modo kernel. O problema geralmente é resolvido. Se o problema não for resolvido, o bug existe no código.
Esteja
alerta!
Vida útil do ticket de autenticação de formulários Você consegue identificar o problema com o código a seguir?
FormsAuthentication.RedirectFromLoginPage(nome de usuário, verdadeiro);
Esse código pode parecer bom, mas nunca deve ser usado em um aplicativo ASP.NET 1.x, a menos que o código em outro lugar do aplicativo compense os efeitos negativos dessa instrução. Se você não tem certeza do porquê, continue lendo.
FormsAuthentication.RedirectFromLoginPage executa duas tarefas. Primeiro, quando FormsAuthenticationModule redireciona o usuário para a página de login, FormsAuthentication.RedirectFromLoginPage redireciona o usuário para a página solicitada originalmente. Segundo, ele emite um ticket de autenticação (normalmente transportado em um cookie, e sempre transportado em um cookie no ASP.NET 1.x) que permite ao usuário permanecer autenticado por um período de tempo predeterminado.
O problema está neste período de tempo. No ASP.NET 1.x, passar outro parâmetro falso para RedirectFromLoginPage emite um tíquete de autenticação temporário que expira após 30 minutos por padrão. (Você pode alterar o período de tempo limite usando o atributo Timeout no elemento web.config.) No entanto, passar outro parâmetro true emitirá um tíquete de autenticação permanente válido por 50 anos. Isso criará um problema porque se alguém roubar essa autenticação! ticket, eles podem usar a identidade da vítima para acessar o site durante a duração do ticket. Há muitas maneiras de roubar tickets de autenticação – sondando tráfego não criptografado em pontos de acesso sem fio públicos, criando scripts em sites, obtendo acesso físico ao computador da vítima, etc. – portanto, passar true para RedirectFromLoginPage é mais seguro do que desabilitar seu site. Não muito melhor. Felizmente, esse problema foi resolvido no ASP.NET 2.0. RedirectFromLoginPage agora aceita os tempos limite especificados em web.config para tickets de autenticação temporários e permanentes da mesma maneira.
Uma solução é nunca passar true no segundo parâmetro de RedirectFromLoginPage em aplicativos ASP.NET 1.x. Mas isso é impraticável porque as páginas de login geralmente apresentam uma caixa "Mantenha-me conectado" que o usuário pode marcar para receber um cookie de autenticação permanente em vez de temporário. Outra solução é usar o trecho de código em Global.asax (ou o módulo HTTP se preferir), que modifica o cookie que contém o ticket de autenticação permanente antes que ele seja retornado ao navegador.
A Figura 3 contém um desses trechos de código. Se esse trecho de código estiver em Global.asax, ele modificará a propriedade Expires do cookie de autenticação de Formulários permanente de saída para que o cookie expire após 24 horas. Você pode definir o tempo limite para qualquer data que desejar, modificando a linha comentada "Nova data de expiração".
Você pode achar estranho que o método Application_EndRequest chame um método auxiliar local (GetCookieFromResponse) para verificar o cookie de autenticação para a resposta de saída. O método Helper é uma solução alternativa para outro bug no ASP.NET 1.1 que fazia com que cookies falsos fossem adicionados à resposta se você usasse o gerador de índice de string do HttpCookieCollection para verificar cookies inexistentes. Usar um gerador de índice inteiro como GetCookieFromResponse resolve o problema.
Voltar ao início View State: O assassino silencioso do desempenho De certa forma, o view state é a melhor coisa de todos os tempos. Afinal, o estado de visualização permite que páginas e controles mantenham o estado entre postbacks. Portanto, você não precisa escrever código para evitar que o texto em uma caixa de texto desapareça quando um botão é clicado ou para consultar novamente o banco de dados e religar o DataGrid após um postback, como faria no ASP tradicional.
Mas o estado de visualização tem uma desvantagem: quando fica muito grande, torna-se um assassino silencioso de desempenho. Alguns controles, como caixas de texto, tomam decisões com base no estado de exibição. Outros controles (principalmente DataGrid e GridView) determinam seu estado de visualização com base na quantidade de informações exibidas. Eu ficaria assustado se um GridView exibisse 200 ou 300 linhas de dados. Embora o estado de visualização do ASP.NET 2.0 tenha aproximadamente metade do tamanho do estado de visualização do ASP.NET 1.x, um GridView incorreto pode facilmente reduzir a largura de banda efetiva da conexão entre o navegador e o servidor Web em 50% ou mais.
Você pode desativar o estado de exibição de controles individuais definindo EnableViewState como falso, mas alguns controles (especialmente o DataGrid) perdem algumas funcionalidades quando não podem usar o estado de exibição. Uma solução melhor para controlar o estado da visualização é mantê-la no servidor. No ASP.NET 1.x, você pode substituir os métodos LoadPageStateFromPersistenceMedium e SavePageStateToPersistenceMedium da página e manipular o estado de visualização da maneira que desejar. O código na Figura 4 mostra uma substituição que impede que o estado de visualização seja retido em campos ocultos e, em vez disso, o mantém no estado de sessão. Armazenar o estado de exibição no estado da sessão é particularmente eficaz quando usado com o modelo de processo de estado de sessão padrão (ou seja, quando o estado da sessão é armazenado em um processo de trabalho do ASP.NET na memória). Por outro lado, se o estado da sessão for armazenado no banco de dados, apenas os testes poderão mostrar se a retenção do estado da visualização no estado da sessão melhora ou diminui o desempenho.
A mesma abordagem é usada no ASP.NET 2.0, mas o ASP.NET 2.0 fornece uma maneira mais fácil de persistir o estado de exibição no estado da sessão. Primeiro, defina um adaptador de página personalizado cujo método GetStatePersister retorne uma instância da classe SessionPageStatePersister do .NET Framework:
public class SessionPageStateAdapter :System.Web.UI.Adapters.PageAdapter{public override PageStatePersister GetStatePersister () {return new SessionPageStatePersister(this.Page ) ;}}
Em seguida, registre o adaptador de página personalizado como o adaptador de página padrão, colocando o arquivo App.browsers na pasta App_Browsers do seu aplicativo da seguinte forma:
<browsers><browser refID="Default"><controlAdapters><adapter controlType=" System.Web. UI.Page"adapterType="SessionPageStateAdapter" /></controlAdapters></browser></browsers>
(Você pode nomear o arquivo como quiser, desde que ele tenha uma extensão .browsers.) Depois disso, o ASP.NET carrega o adaptador de página e usa o SessionPageStatePersister retornado para preservar todo o estado da página, incluindo o estado de exibição.
Uma desvantagem de usar um adaptador de página personalizado é que ele se aplica globalmente a todas as páginas do aplicativo. Se você preferir manter o estado de visualização de algumas páginas no estado de sessão, mas não de outras, use o método mostrado na Figura 4. Além disso, você poderá ter problemas ao usar esse método se o usuário criar várias janelas do navegador na mesma sessão.
Voltar ao topo
Estado da sessão do SQL Server: outro assassino de desempenho
O ASP.NET facilita o armazenamento do estado da sessão no banco de dados: basta alternar uma opção no web.config e o estado da sessão é facilmente movido para o banco de dados backend. Este é um recurso importante para aplicativos executados no domínio da Web porque permite que cada servidor no domínio compartilhe um repositório comum de estado de sessão. A atividade adicional do banco de dados reduz o desempenho de solicitações individuais, mas o aumento da escalabilidade compensa a perda de desempenho.
Tudo isso parece bom, mas as coisas mudam quando você considera alguns pontos:
• Mesmo em aplicativos que usam estado de sessão, a maioria das páginas não usa estado de sessão.
• Por padrão, o gerenciador de estado de sessão do ASP.NET executa dois acessos (um acesso de leitura e um acesso de gravação) ao armazenamento de dados da sessão em cada solicitação, independentemente de a página solicitada usar o estado de sessão.
Em outras palavras, ao usar a opção de estado de sessão do SQL Server™, você paga um preço (dois acessos ao banco de dados) em cada solicitação, mesmo em solicitações de páginas que não têm nada a ver com o estado da sessão. Isso tem um impacto negativo direto no rendimento de todo o site.
Figura 5 Elimine o acesso desnecessário ao banco de dados de estado de sessão
Então, o que você deve fazer? É simples: desabilite o estado da sessão em páginas que não usam o estado da sessão. Isso é sempre uma boa ideia, mas é especialmente importante quando o estado da sessão é armazenado em um banco de dados. A Figura 5 mostra como desabilitar o estado da sessão. Se a página não usar o estado de sessão, inclua EnableSessionState="false" em sua diretiva Page, assim:
<%@ Page EnableSessionState="false" ... %>
Esta diretiva impede que o gerenciador de estado da sessão leia e grave no banco de dados de estado da sessão em cada solicitação. Se a página lê dados do estado da sessão, mas não grava dados (ou seja, não modifica o conteúdo da sessão do usuário), defina EnableSessionState como ReadOnly da seguinte maneira:
<%@ Page EnableSessionState="ReadOnly" ... %>
Finalmente, se a página exigir acesso de leitura/gravação ao estado da sessão, omita a propriedade EnableSessionState ou defina-a como verdadeira:
<%@ Page EnableSessionState="true" ... %>
Ao controlar o estado da sessão dessa maneira, você garante que o ASP.NET acesse o banco de dados de estado da sessão somente quando for realmente necessário. Eliminar o acesso desnecessário ao banco de dados é o primeiro passo na construção de aplicativos de alto desempenho.
A propósito, a propriedade EnableSessionState é pública. Essa propriedade foi documentada desde o ASP.NET 1.0, mas ainda raramente vejo desenvolvedores tirando vantagem dela. Talvez porque não seja muito importante para o modelo de estado de sessão padrão na memória. Mas é importante para o modelo SQL Server.
Voltar ao início Funções não armazenadas em cache A instrução a seguir aparece frequentemente no arquivo web.config de um aplicativo ASP.NET 2.0 e nos exemplos que apresentam o gerenciador de funções do ASP.NET 2.0:
<roleManager enabled="true" />
Mas, como mostrado acima, esta afirmação tem um impacto negativo significativo no desempenho. Você sabe por quê?
Por padrão, o gerenciador de funções do ASP.NET 2.0 não armazena em cache os dados da função. Em vez disso, ele consulta o armazenamento de dados da função sempre que precisa determinar a qual função, se houver, o usuário pertence. Isso significa que, depois que um usuário for autenticado, quaisquer páginas que utilizem dados de função (por exemplo, páginas que usam mapas de sites com configurações de recorte de segurança habilitadas e páginas que tenham acesso restrito usando diretivas de URL baseadas em função em web.config) causarão a função gerenciador para consultar o armazenamento de dados da função. Se as funções estiverem armazenadas em um banco de dados, você poderá facilmente dispensar o acesso a vários bancos de dados para cada solicitação. A solução é configurar o gerenciador de funções para armazenar em cache os dados da função em cookies:
<roleManager enabled="true" cacheRolesInCookie="true" />
Você pode usar outros atributos <roleManager> para controlar as características do cookie de função — por exemplo, por quanto tempo o cookie deve permanecer válido (e, portanto, com que frequência o gerenciador de função retorna ao banco de dados de função). Os cookies de função são assinados e criptografados por padrão, portanto o risco de segurança, embora não seja zero, é mitigado.
Voltar ao inícioSerialização de propriedades do arquivo de configuração
O serviço de perfil do ASP.NET 2.0 fornece uma solução pronta para o problema de manutenção do estado por usuário, como preferências de personalização e preferências de idioma. Para usar o serviço de perfil, defina um perfil XML que contenha os atributos que deseja preservar em nome de um usuário individual. O ASP.NET então compila uma classe que contém as mesmas propriedades e fornece acesso fortemente digitado a instâncias de classe por meio de propriedades do arquivo de configuração adicionadas à página.
A flexibilidade do perfil é tão grande que permite até mesmo que tipos de dados personalizados sejam usados como propriedades de perfil. No entanto, há um problema que vi pessoalmente fazer com que os desenvolvedores cometessem erros. A Figura 6 contém uma classe simples chamada Posts e uma definição de perfil que usa Posts como um atributo de perfil. No entanto, esta classe e este arquivo de configuração produzem um comportamento inesperado em tempo de execução. Você consegue descobrir por quê?
O problema é que Posts contém um campo privado chamado _count, que deve ser serializado e desserializado para congelar e recongelar totalmente a instância da classe. No entanto, _count não é serializado e desserializado porque é privado e o ASP.NET Profile Manager usa serialização XML por padrão para serializar e desserializar tipos personalizados. O serializador XML ignora membros não públicos. Portanto, as instâncias de Posts são serializadas e desserializadas, mas cada vez que uma instância de classe é desserializada, _count é redefinido para 0.
Uma solução é tornar _count um campo público em vez de um campo privado. Outra solução é encapsular _count com uma propriedade pública de leitura/gravação. A melhor solução é marcar Postagens como serializáveis (usando SerializableAttribute) e configurar o gerenciador de perfil para usar o serializador binário do .NET Framework para serializar e desserializar instâncias de classe. Esta solução mantém o design da própria classe. Ao contrário dos serializadores XML, os serializadores binários serializam campos independentemente de serem acessíveis. A Figura 7 mostra a versão fixa da classe Posts e destaca a definição de perfil de acompanhamento alterada.
Uma coisa que você deve ter em mente é que se você estiver usando um tipo de dados personalizado como propriedade de perfil e esse tipo de dados tiver membros de dados não públicos que devem ser serializados para serializar completamente uma instância do tipo, use serializeAs=" Binary" nas propriedades da declaração de propriedade e certifique-se de que o tipo em si seja serializável. Caso contrário, a serialização completa não acontecerá e você perderá tempo tentando determinar por que o perfil não está funcionando.
Voltar ao início Saturação do pool de threads Muitas vezes fico muito surpreso com o número real de páginas ASP.NET que vejo ao executar uma consulta ao banco de dados e esperar 15 segundos ou mais para que os resultados da consulta sejam retornados. (Também esperei 15 minutos antes de ver os resultados da minha consulta!) Às vezes, o atraso é uma consequência inevitável da grande quantidade de dados retornados; outras vezes, o atraso é devido ao design inadequado do banco de dados. Mas, independentemente do motivo, longas consultas ao banco de dados ou qualquer tipo de operações de E/S longas farão com que a taxa de transferência diminua em aplicativos ASP.NET.
Já descrevi esse problema em detalhes antes, então não vou entrar em muitos detalhes aqui. Basta dizer que o ASP.NET depende de um pool de threads limitado para lidar com solicitações. Se todos os threads estiverem ocupados aguardando a conclusão de uma consulta ao banco de dados, chamada de serviço da Web ou outra operação de E/S, eles serão liberados quando uma operação for concluída. concluído Antes de um thread ser emitido, outras solicitações devem estar na fila e aguardando. Quando as solicitações são enfileiradas, o desempenho cai drasticamente. Se a fila estiver cheia, o ASP.NET fará com que as solicitações subsequentes falhem com um erro HTTP 503. Esta não é uma situação que gostaríamos de ver em um aplicativo de produção em um servidor Web de produção.
A solução são páginas assíncronas, um dos recursos melhores e ainda pouco conhecidos do ASP.NET 2.0. Uma solicitação para uma página assíncrona começa em um thread, mas quando inicia uma operação de E/S, ela retorna para esse thread e para a interface IAsyncResult do ASP.NET. Quando a operação for concluída, a solicitação notificará o ASP.NET por meio de IAsyncResult e o ASP.NET extrairá outro thread do pool e concluirá o processamento da solicitação. É importante notar que quando ocorrem operações de E/S, nenhum thread do pool de threads está ocupado. Isso pode melhorar significativamente o rendimento, evitando que solicitações de outras páginas (páginas que não estão executando operações de E/S demoradas) aguardem na fila.
Você pode ler tudo sobre páginas assíncronas na edição de outubro de 2005 da MSDN® Magazine. Qualquer página que esteja vinculada à E/S em vez de vinculada à máquina e que leve muito tempo para ser executada tem uma boa chance de se tornar uma página assíncrona.
Quando conto aos desenvolvedores sobre páginas assíncronas, eles geralmente respondem com "Isso é ótimo, mas não preciso delas em meu aplicativo".
serviçosda
web? Você verificou os contadores de desempenho do ASP.NET para obter estatísticas sobre solicitações enfileiradas e tempos médios de espera? Mesmo que seu aplicativo esteja funcionando bem até agora, à medida que o tamanho do cliente aumenta, a carga pode aumentar?
dos aplicativos ASP.NET do mundo real exigem páginas assíncronas. Por favor, lembre-se disso!
Voltar ao início Representação e autorização ACL A seguir está uma diretiva de configuração simples, mas que faz meus olhos brilharem toda vez que a vejo no web.config:
<identity impersonate="true" />
Esta diretiva permite a representação do lado do cliente em aplicativos ASP.NET. Ele anexa um token de acesso que representa o cliente ao thread que trata a solicitação, para que as verificações de segurança executadas pelo sistema operacional sejam contra a identidade do cliente e não contra a identidade do processo de trabalho. Os aplicativos ASP.NET raramente exigem simulação; minha experiência me diz que os desenvolvedores geralmente permitem a simulação pelos motivos errados. Aqui está o porquê.
Os desenvolvedores geralmente habilitam a representação em aplicativos ASP.NET para que as permissões do sistema de arquivos possam ser usadas para restringir o acesso às páginas. Se Bob não tiver permissão para visualizar Salaries.aspx, o desenvolvedor ativará a representação para que Bob possa ser impedido de visualizar Salaries.aspx configurando a lista de controle de acesso (ACL) para negar permissão de leitura a Bob. Mas existe o seguinte perigo oculto: a representação é desnecessária para a autorização da ACL. Quando você habilita a Autenticação do Windows em um aplicativo ASP.NET, o ASP.NET verifica automaticamente a ACL para cada página .aspx solicitada e nega solicitações de chamadores que não têm permissão para ler o arquivo. Ele ainda se comporta assim mesmo se a simulação estiver desabilitada.
Às vezes é necessário justificar a simulação. Mas geralmente você pode evitá-lo com um bom design. Por exemplo, suponha que Salaries.aspx consulte um banco de dados em busca de informações salariais que apenas os gerentes conhecem. Com a representação, você pode usar permissões de banco de dados para negar ao pessoal não gerencial a capacidade de consultar dados da folha de pagamento. Ou você pode ignorar a representação e limitar o acesso aos dados da folha de pagamento definindo uma ACL para Salaries.aspx para que não administradores não tenham acesso de leitura. A última abordagem oferece melhor desempenho porque evita totalmente a zombaria. Também elimina o acesso desnecessário ao banco de dados. Por que a consulta ao banco de dados é negada apenas por motivos de segurança?
A propósito, uma vez ajudei a solucionar problemas de um aplicativo ASP herdado que era reiniciado periodicamente devido a um consumo irrestrito de memória. Um desenvolvedor inexperiente converteu a instrução SELECT alvo em SELECT * sem considerar que a tabela consultada continha imagens, que eram grandes e numerosas. O problema é agravado por um vazamento de memória não detectado. (Minha área de código gerenciado!) Um aplicativo que funcionava bem há anos parou de funcionar repentinamente porque as instruções SELECT que costumavam retornar um ou dois quilobytes de dados agora retornavam vários megabytes. Acrescente a isso o problema do controle de versão inadequado, e a vida de uma equipe de desenvolvimento terá que ser “hiperativa” — e por “hiperativa” é como ter que assistir seus filhos jogarem um jogo chato enquanto você está jogando. cama à noite.
Em teoria, os vazamentos de memória tradicionais não podem ocorrer em aplicativos ASP.NET compostos inteiramente de código gerenciado. Mas o uso insuficiente de memória pode afetar o desempenho, forçando a coleta de lixo a ocorrer com mais frequência. Mesmo em aplicativos ASP.NET, tome cuidado com SELECT *
Voltar para parte superior Não confie inteiramente nele — configure o arquivo de configuração do banco de dados!
Como consultor, muitas vezes me perguntam por que os aplicativos não apresentam o desempenho esperado. Recentemente, alguém perguntou à minha equipe por que um aplicativo ASP.NET completava apenas aproximadamente 1/100 da taxa de transferência (solicitações por segundo) necessária para solicitar um documento. Os problemas que descobrimos antes são exclusivos dos problemas que vimos em aplicações Web que não funcionavam corretamente — e são lições que todos deveríamos levar a sério.
Executamos o SQL Server Profiler e monitoramos a interação entre este aplicativo e o banco de dados back-end. Em um caso mais extremo, apenas um clique de botão causou a ocorrência de mais de 1.500 erros no banco de dados. Você não pode criar aplicativos de alto desempenho dessa maneira. Uma boa arquitetura sempre começa com um bom design de banco de dados. Não importa o quão eficiente seja o seu código, ele não funcionará se for pesado por um banco de dados mal escrito.
A arquitetura de acesso a dados ruim geralmente resulta de um ou mais dos seguintes:
• Design de banco de dados ruim (geralmente projetado por desenvolvedores, não administradores de banco de dados).
• Uso de conjuntos de dados e dataAdapters - especialmente dataAdapter.Update, que funciona bem para aplicativos do Windows Forms e outros clientes ricos, mas geralmente não é ideal para aplicativos da Web.
• Uma camada de acesso a dados mal projetada (DAL) que tem cálculos mal programados e consome muitos ciclos de CPU para executar operações relativamente simples.
O problema deve ser identificado antes de poder ser tratado. A maneira de identificar problemas de acesso a dados é executar o SQL Server Profiler ou uma ferramenta equivalente para ver o que está acontecendo nos bastidores. O ajuste do desempenho é concluído após a verificação da comunicação entre o aplicativo e o banco de dados. Experimente - você pode se surpreender com o que encontra.
Voltando à conclusão principal, agora você conhece alguns dos problemas e suas soluções que você pode encontrar ao criar um aplicativo de produção do ASP.NET. O próximo passo é examinar mais de perto seu próprio código e tentar evitar alguns dos problemas que descrevi aqui. O ASP.NET pode ter diminuído a barreira à entrada dos desenvolvedores da Web, mas seus aplicativos têm todos os motivos para serem flexíveis, estáveis e eficientes. Por favor, considere isso cuidadosamente para evitar erros iniciantes.
A Figura 8 fornece uma lista de verificação curta que você pode usar para evitar as armadilhas descritas neste artigo. Você pode criar uma lista de verificação de defeito de segurança semelhante. Por exemplo:
• Você criptografou seções de configuração que contêm dados confidenciais?
• Você está verificando e validando a entrada usada nas operações do banco de dados e está usando a entrada codificada HTML como saída?
• Seu diretório virtual contém arquivos com extensões desprotegidas?
Essas perguntas são importantes se você valorizar a integridade do seu site, os servidores que o hospedam e os recursos de back -end em que eles confiam.
Jeff Prosise é um editor contribuinte da MSDN Magazine e autor de vários livros, incluindo a programação da Microsoft .Net (Microsoft Press, 2002). Ele também é co-fundador da Wintellect, uma empresa de consultoria e educação de software.
Da edição de julho de 2006 da MSDN Magazine.