Por dentro da Base Class Library

A Base Classe Library (também conhecida como BCL) é um conjunto de classes que o .NET disponibiliza para todas as linguagens que rodam sob o .NET Framework. Essa base encapsula várias funcionalidades que tornam o trabalho dos desenvolvedores muito mais fácil. As classes contidas dentro da BCL é comum para qualquer tipo de aplicação, ou seja, independentemente de tecnologia (ASP.NET, Windows Forns, WPF, etc.), você poderá consumir essas classes que, representam tarefas que são comumente utilizadas. A imagem abaixo exibe onde a BCL está encaixada dentro da plataforma .NET.

Figura 1Base Class Library (BCL).

A versão 2.0 adicionou novas tipos e namespaces, enriquecendo ainda mais esta estrutura. Essas classes vão desde novas coleções (tipadas) até novos namespaces, como é o caso do System.Transactions. Apesar do .NET Framework estar em sua versão 3.5, ele utiliza o .NET 2.0 como seu núcleo. A figura abaixo ilustra perfeitamente a posição do .NET 2.0 dentro do .NET 3.X.

Figura 2 – Gráfico que exibe a posição do .NET 2.0.

A BCL é composta por vários namespaces e, através dos capítulos abaixo, veremos detalhadamente cada um dos principais deles. A idéia é abordar o conteúdo mais útil ao dia-à-dia, mostrando exemplos em Visual Basic .NET e Visual C#. Sendo assim, nem todas as classes/funcionalidades serão cobertas aqui mas, para isso, poderá recorrer ao MSDN Library.

ATENÇÃO: Os arquivos estão em formato XPS. Caso não tenha o visualizador, você poderá baixá-lo aqui.

Conteúdo

  • Capítulo 1 – Tipos de dados e Interfaces Este capítulo abordará a arquitetura de tipos fornecido pelo .NET Framework, onde na primeira parte do capítulo, será abordado os tipos padrões padrões e veremos como identificar se trata-se de um tipo valor ou referência. Além disso, analisaremos um problema grave, deixado de lado por muitos desenvolvedores, que é a questão do boxing e unboxing. Ainda nessa primeira parte, analisaremos alguns novos tipos introduzidos nesta versão do .NET Framework, em principal, os Generics. Na segunda e última parte do mesmo, vamos abordar as várias Interfaces que estão disponíveis para serem implementados em tipos customizados, fornecendo funcionalidades adicionais ao tipo criado.

  • Capítulo 2 – Trabalhando com Coleções As coleções são componentes importantes em qualquer tipo de aplicação e, para isso, esse capítulo abordará extensamente o uso das mesmas, começando pelas coleções primárias, fornecidas desde as primeiras versões do .NET Framework até as novas coleções, introduzidas na versão 2.0 do .NET Framework, quais fazem uso dos Generics. Além das coleções, analisaremos as Interfaces disponíveis para podermos estender as funcionalidades existentes e customizarmos para o nosso cenário.
  • Capítulo 3 – Utilização de Assemblies Um Assembly é a menor unidade de reutilização, segurança e controle de versão. O Assembly é algo importante que deve ser analisado cuidadosamente, pois toda aplicação .NET depois de compilada gerará um Assembly. Este capítulo abordará a sua criação desde um utilitário de linha de comando até o Visual Studio .NET. Além disso, abordaremos também outros assuntos relacionados a Assemblies, como por exemplo, strong names, GAC (Global Assembly Cache), instaladores e arquivos de configuração.
  • Capítulo 4 – Monitoramento e depuração de aplicações Toda e qualquer aplicação necessita de algum tipo de monitoramento de seu código para detectar possíveis problemas que possam acontecer e que devem ser analisados. O .NET Framework fornece várias classes que ajudam nesse monitoramento e, este capítulo, é responsável por apresentar essas classes que vão desde a manipulação do Event Log do Windows até classes que interagem com o WMI – Windows Management Instrumentation.
  • Capítulo 5 – Manipulando o sistema de arquivos Grande parte das aplicações comerciais que temos atualmente manipulam arquivos. Esses arquivos são arquivos de bancos, arquivos de parceiros e fornecedores que servem para troca de informações. Enquanto os XML Web Services ainda não são uma realidade para muitas empresas, a manipulação de arquivos e seus respectivos conteúdos é ainda muito utilizado. Tendo esse cenário, o capítulo em questão abordará as principais classes contidas dentro do namespace System.IO para exemplificar e facilitar a manipulação de arquivos do disco e streams de dados.
  • Capítulo 6 – Serialização A serialização de dados é cada dia mais utilizada em aplicações. Por mais que isso aconteça nos bastidores, esse capítulo abordará desde o seu conceito até como implementá-la; e ainda, em seus diversos formatos, utilizando as classes fornecidas pelo .NET Framework 2.0. Além disso, analisaremos classes e Interfaces que temos disponíveis, que proporcionaram o processo de serialização e deserialização mais flexível, onde podemos customizar e interceptar cada um desses processos de acordo com a nossa necessidade.
  • Capítulo 7 – Globalização de Aplicações Cada vez mais se desenvolve softwares que podem ser acessados por várias pessoas de diferentes idiomas e de diferentes locais do mundo. Tendo esse cenário, é importante que a aplicação que estamos desenvolvendo seja possível ao usuário poder customizar o idioma que deseja visualizar os dados e ainda, poder criar a aplicação independente de qualquer cultura. Essa capítulo tem justamente essa finalidade, ou seja, de exibir o que o .NET Framework é capaz de fazer para atender essa necessidade que, torna-se cada vez mais comum.
  • Capítulo 8 – Criptografia Criptografia de dados é um ponto muito importante nos mais diversos tipos de aplicações. Geralmente, em aplicações onde alguns dos dados são muito sigilosos, como é o caso de aplicações financeiras, quais mantém os dados de seus clientes, é necessário que se mantenha esses dados seguros pois, se esses dados cairem em mãos erradas, essas pessoas com más intenções, não consigam entender e/ou recuperar esses dados em sua forma legível. Esse capítulo abordará extensamente as classes responsáveis por criptografia e hashing que o .NET Framework disponiliza, bem como utilizá-las e como aplicá-las ao dia-à-dia.
  • Capítulo 9 – Utilizando Code Access Security – CAS Toda aplicação que utiliza o Common Language Runtime (CLR) obrigatoriamente deve interagir com o sistema de segurança do mesmo. Quando a aplicação é executada, automaticamente é avaliado se ela tem ou não determinados privilégios. Dependendo das permissões que a aplicação tem, ela poderá rodar perfeitamente ou gerar erros relacionados a segurança. Code Access Security (também conhecido como CAS), é um mecanismo que ajuda limitar/conceder o acesso que o código que está querendo realizar, protegendo recursos e operações. Este capítulo abordará como utilizar o CAS, que é fornecido juntamente com o SDK do .NET Framework e, como configurar devidamente a aplicação para evitar problemas relacionados a segurança.
  • Capítulo 10 – Envio de Mensagens (E-mails) Envio de e-mails é muito comum em qualquer tipo de aplicação, seja ela uma aplicação para internet, uma aplicação para Windows ou até mesmo serviços que rodam sem uma intervenção do usuário. O .NET Framework fornece um namespace contendo classes e muitos outros tipos que podemos utilizar nas aplicação para habilitar o envio de e-mails e, conseqüentemente, torná-las muito mais dinâmicas e inteligentes.
  • Capítulo 11 – Criando Serviços do Windows Os Serviços do Windows (Windows Services), permitem-nos criar aplicações que rodam em “background” no sistema operacional. Estes serviços podem ser automaticamente inicializados quando o sistema operacional inicializar, podendo ainda ser pausado e reinicializado, sem apresentar nenhuma interface com o usuário. Esses serviços são ideais para ser usado em servidores ou em funcionalidades de longa duração que necessitem ser executadas de forma totalmente independente, sem a intervenção de um usuário. O capítulo corrente abordará desde a sua criação, depuração e instalação do mesmo.
  • Capítulo 12 – Interoperabilidade com componentes COM A Microsoft criou a plataforma .NET e, em pouco tempo, essa plataforma foi adotada por muitas e muitas empresas. Algo importante é que muitas dessas empresas, já tinham componentes COM que eram utilizados em massa nas aplicações e que são inviáveis para serem reescritos imediatamente. Felizmente a Microsoft pensou no legado e possibilita a interoperabilidade de componentes COM, interagindo com aplicações baseadas na plataforma .NET e vice-versa. Este capítulo mostrará os passos necessários para efetuar essa interoperabilidade entre as novas aplicações e o que já existe em código legado.
  • Capítulo 13 – Reflection Reflection é a habilidade de extrair informações de metadados de um determinado tipo, ou seja, quais parâmetros, métodos, entre outros membros um determinado tipo possui. Isso torna a aplicação bastante flexível, onde podemos extrair informações necessárias para podermos customizar e automatizar a criação de ferramentas e utilitários que auxiliam os próprios desenvolvedores. Além disso, permite a criação em runtime de Assemblies e como instanciar classes via programação. Esse capítulo propõe-se a explicar como criar esse tipo de funcionalidade dentro da aplicação.
  • Capítulo 14 – Threading A criação de threads permitem aumentar consideravelmente a performance das aplicações. Elas fornecem a habilidade de conseguirmos delegar processamentos em diversas unidades de execução, aumentando a capacidade de processamento de uma aplicação. Mas utilizando isso de forma errada, poderá piorar ao invés de melhorar, consumindo mais recursos do que o necessário, tendo um comportamento inesperado e retornando valores diferentes do esperado. O .NET Framework fornece várias classes que podemos utilizar para criação e gerenciamento de threads, bloqueio de recursos em um ambiente multi-threading e sincronização. Este capítulo irá ajudá-lo a conhecer alguns problemas existentes em aplicações que fazem o uso de threads e como contorná-los.

Referências Bibliográficas

  • Code Complete – Second Edition
    Autor: Steve McConnell
    Editora: Microsoft Press
    ISBN: 0-7356-1967-0
  • Writing Secure Code – Second Edition
    Autores: Michael Howard e David LeBlanc
    Editora: Microsoft Press
    ISBN: 0-7356-1722-8
  • CLR via C# – Second Edition
    Autor: Jeffrey Richter
    Editora: Microsoft Press
    ISBN: 0-7356-2163-2
  • Programming Visual C# 2005: The Language
    Autor: Donis Marshall
    Editora Microsoft Press
    ISBN: 0-7356-2181-0
  • Expressões Regulares – Uma abordagem divertida
    Autor: Aurélio Marinho Jargas
    Editora: Novatec
    ISBN: 85-7522-100-0
  • Collection 5160: Core Development with Microsoft .NET Framework 2.0
    Autor/Editor: Microsoft
  • Collection 5161: Advanced Development with Microsoft .NET Framework 2.0
    Autor/Editor: Microsoft

Barrando acesso à elementos/atributos

Recentemente estive envolvido em um projeto simples mas um tanto quanto interessante. Estávamos fazendo a configuração de um servidor Web e todas as aplicações ASP.NET 2.0 que ali seriam hospedadas devem utilizar uma mesma base de dados para as funcionalidades de Membership, Roles e Profile.

A questão é que as aplicações podem ter um arquivo Web.Config e customizar essas configurações em cada uma delas. Só que isso não seria permitido. A solução foi até mais simples do que imaginava e, me forçou a olhar para alguns atributos que nunca prestei a devida atenção até então. Todos os elementos que são colocados nos arquivos de configuração herdam (diretamente ou indiretamente) da classe ConfigurationElement. Essa classe possui 5 principais propriedades (ou atributos):

  • lockAllAttributesExcept: Informa que todos os atributos de um elemento serão bloqueados, com exceção dos atributos que estão contidos nesta lista.
  • lockAllElementsExcept: Informa que todos os elementos de um elemento “pai” serão bloqueados, com exceção dos elementos que estão contidos nesta lista.
  • lockAttributes: Bloqueia apenas os atributos contidos nesta lista.
  • lockElements: Bloqueia apenas os elementos contidos nesta lista.
  • lockItem: Bloqueia o item como um tudo e, conseqüentemente, todos os seus “filhos”.

Para que fosse possível isso, editamos o arquivo Machine.Config e lá colocamos todas as configurações necessárias para que as aplicações pudessem herdar as configurações estipuladas para Membership, Roles e Profile e não ter direito de sobrescreve-las. Com isso, o arquivo Machine.Config fica definido como:

<?xml version=”1.0″>
<configuration>
  <connectionStrings>
    <add
        name=”LocalSqlServer”
        connectionString=”CONN_STRING”
        providerName=”System.Data.SqlClient”
        lockItem=”true” />
  </connectionStrings>
  <system.web>
    <membership defaultProvider=”AspNetSqlMembershipProvider” lockAttributes=”defaultProvider”>
      <providers lockElements=”clear”>
        <add
            name=”AspNetSqlMembershipProvider”
            type=”System.Web.Security.SqlMembershipProvider, System.Web”
            connectionStringName=”LocalSqlServer”
            enablePasswordRetrieval=”false”
            enablePasswordReset=”true”
            requiresQuestionAndAnswer=”true”
            applicationName=”/”
            requiresUniqueEmail=”false”
            passwordFormat=”Hashed”
            maxInvalidPasswordAttempts=”5″
            minRequiredPasswordLength=”7″
            minRequiredNonalphanumericCharacters=”1″
            passwordAttemptWindow=”10″
            passwordStrengthRegularExpression=””
            lockAttributes=”connectionStringName;enablePasswordRetrieval;passwordFormat” />
      </providers>
    </membership>
  </system.web>
</configuration>

Obviamente que algumas aplicações devem customizar algum desses atributos e, justamente por isso, que nem todos os elementos/atributos foram bloqueados. Por exemplo, se desejar customizar o nome da aplicação no arquivo Web.Config de cada uma das aplicações, então podemos fazer:

<?xml version=”1.0″?>
<configuration>
    <system.web>
      <membership userIsOnlineTimeWindow=”10″>
        <providers>
          <remove name=”AspNetSqlMembershipProvider”/>
          <add
            name=”AspNetSqlMembershipProvider”
            type=”System.Web.Security.SqlMembershipProvider, System.Web”
            applicationName=”Teste” />
        </providers>
      </membership>
    </system.web>
</configuration>

Nada impede do desenvolvedor poder adicionar novos providers de memberships, roles e profiles mas, ele não poderá habilitá-lo, já que o atributo defaultProvider está bloqueado a nível superior. Um outro detalhe importante é podemos remover o provider especificado no arquivo Machine.Config através do elemento remove mas, sendo assim, ele não poderá utilizar as funcionalidades de Membership definidas para o servidor.

Performance, Segurança e Boas Práticas

Recentemente um amigo me procurou para dar algumas dicas se segurança e performance para melhorar de uma aplicação Web. Depois da ajuda, eu decidi disponibilizar essa listagem aqui no blog. Compilei algumas dicas e disponibilizei abaixo essa listagem, com algumas guidelines que julgo necessárias para quando for desenvolver e disponibilizar a aplicação em um servidor Web. É importante dizer que isso pode ou não ser importante, dependendo de qual o cenário da aplicação/empresa.

  • Performance
    • Desabilite a Session nas páginas que você não precisa dela. Isso pode ser realizado através do atributo EnableSessionState da diretiva @Page. Se não for utilizar na aplicação como um tudo, então desabilite a nível de aplicação, através do elemento enableSessionState no elemento do arquivo Web.Config.
    • Sempre defina para false a propriedade EnableViewState para quando não precisar do ViewState na página Web. Através do atributo maxPageStateFieldlength definido dentro do elemento , você tem a possibilidade de “quebrar” o campo __VIEWSTATE. Mas isso não traz melhora em termos de performance. Essa configuração é útil para quando alguns proxies e firewalls barram o acesso a determinadas páginas, quando ela, por sua vez, contém campos ocultos com um grande conteúdo.
    • Defina o atributo debug do elemento compilation para false. Quando definido como true em ambiente de produção, é um dos grandes vilões de performance. Se desejar que todas as aplicações do servidor Web obrigatoriamente estejam com este mesmo comportamento, voce pode definir para true o valor do atributo retail do elemento deployment do arquivo Machine.config do servidor Web.
    • Considere a utilização de OutputCache. Para facilitar a manutenção, utilize o CacheProfile.
    • Remova todos os módulos que não estão sendo utilizados pela aplicação.
    • Reduza a quantidade de round-trips entre o cliente e o servidor. Para isso, utilize sempre que possível o método Server.Transfer.
    • A compactação da resposta a ser enviada para o cliente é sempre uma boa opção. Além disso, remova caracteres desnecessários, como espaços em branco e TABs.
    • Utilize sempre DataReader para ter uma forma eficiente de carga de dados.
    • Paginar os resultados é quase sempre uma necessidade para exibição de dados.
    • Defina para true o atributo enableKernelOutputCache do elemento httpRuntime (acredito que isso já seja o padrão). Isso tem um benefício a nível do IIS, mas atente-se que, em alguns cenários, essa configuração pode trazer resultados inesperados.
    • Remova todos os HTTP Headers desnecessários. Quando você instala o .NET Framework, é criado um valor por padrão dentro do IIS, que é enviado como resposta para todos os clientes que consomem a aplicação. Geralmente é algo como: “X-Powered by ASP.NET”. Você pode remover diretamente do IIS ou via atributo enableVersionHeader do elemento httpRuntime.
    • Considere a utilização de páginas assíncronas quando possível.
  • Segurança
    • Toda aplicação Web roda sempre em FullTrust. Considere utilizar Medium (partial trust) já que ela atende a maioria dos casos, inclusive na versão 2.0 do .NET Framework, esse nível já concede acesso ao SqlClientPermission, responsável por gerenciar o acesso à servidores SQL Server. Para alterar esse nível, você pode utilizar o atributo level do elemento trust no arquivo Web.Config da aplicação ou do servidor Web, se desejar que todas as aplicações que estão rodando ali sejam definidas como medium trust.
    • Não permita que outros desenvolvedores sobrescrevam a sua policy. Para isso, defina o atributo allowOverride como false.
    • Sempre crie uma página padrão de erros para que os usuários seja redirecionados para ela quando um problema ocorrer. Para isso, configure a seção do arquivo Web.Config. Neste mesmo local, evite que as exceções sejam enviadas para os usuários, definindo o atributo mode para RemoteOnly. Isso evitará exibir detalhes da exceção que ocorreu, qual pode comprometer a sua aplicação.
    • Todos os parâmetros fornecidos pelo usuário (Forms, QueryStrings, etc.) devem ser validados por tamanho, tipo e formato.
    • Páginas importantes, que podem sofrer alguma espécie de ataque, podemos configurar a sua propriedade ViewStateUserKey para um valor qualquer (Session.SessionID), evitando assim, ataques de “one-click”.
    • Nunca armazene informações sigilosas em QueryStrings ou ViewState. Se necessitar armazenar algo importante no ViewState, então criptografe-o. Para isso defina o valor “Auto” para o atributo viewStateEncryptionMode do elemento e invoque o método RegisterRequiresViewStateEncryption na página em que deseja armazenar o conteúdo sigiloso no ViewState.
    • Use SSL somente em páginas que realmente precisam deste nível de segurança.
    • Criptografe a seção de connectionStrings.
    • Criptografe a seção de appSettings quando ele possui informações confidenciais.
    • Quando for conectar com uma base de dados qualquer, utilize um usuário que só tenha permissão necessária para manipular os objetos que estão sendo utilizados pela aplicação, nada mais.
    • Sempre armazena password “hasheados” na sua base de dados. Quando utiliza a arquitetura do Membership, ele já fornece isso intrinsicamente.
    • Defina para true o atributo httpOnlyCookies do elemento httpCookies no arquivo Web.Config. Esse atributo permite ou não que código client-side acessem o cookie.
  • Boas Práticas
    • Sempre extraia strings de arquivos de Resources (*.resx). Mais cedo ou mais tarde, alguém sempre pede para você globalizar a aplicação. 🙂
    • Defina o nome da aplicação no atributo applicationName quando utilizar o Membership/RoleProvider/Profile.
    • Se estiver utilizando o tratamento global de exceções, habilite o Health Monitoring para logar as exceções e efetuar uma posterior análise.
    • Desabilite o Trace em produção. Quando precisar habilitá-lo para poder monitorar alguma requisição, então define o atributo localOnly como true, para que o output possa ser somente visualizado no próprio computador onde a aplicação está sendo executada.

Bem, de momento o que me veio a cabeça é isso. É muito provável que existe outros detalhes que devo ter esquecido ao colocar aqui.

mscorcfg.msc

Algumas ferramentas que são parte integrante do SDK do .NET Framework não estão disponíveis quando instalamos a versão do .NET Framework 2.0 (Redistributable Package (x86)), como é o caso do mscorcfg.msc. Essa ferramenta permite configurarmos o Runtime Security Policy (um caspol.exe visual) e também gerenciarmos os Assemblies que estão contidos dentro do GAC.

Apesar de somente a versão 1.x do .NET Framework disponibilizar essa ferramenta quando instalamos o Redistributable Package, há uma forma de habilitarmos na máquina. É importante dizer que quando fazemos essa configuração em uma máquina de desenvolvimento, é permitido que voce crie um arquivo do tipo *.msi, contendo a configuração necessário (de Enterprise, Machine e User) para que voce possa embutir durante a instalação do software nas máquinas clientes que exigem essa configuração de segurança.

APL/APC

Eu trabalho em uma empresa de ramo financeiro. Estamos automatizando alguns processos e, com isso, precisamos interagir com os mais diversos bancos. Toda essa comunicação com os mesmos é realizada através do protocolo APL/APC (Arquivos-Pra-Lá/Arquivos-Prá-Cá :)), infelizmente.

Com isso, um dos meus trabalhos aqui foi desenvolver uma arquitetura extensível para ler e gerar arquivos de padrão CNAB, customizando para cada um dos bancos. O primeiro passo foi desenvolver os parsers para interpretar cada um dos arquivos. Estes parsers são utilizados pelas aplicações e serviços. Como todo processo de desenvolvimento, é necessário correr em debug  as aplicações que consomem esses parsers, para analisar o conteúdo que está dentro do arquivo. “Cortar” a string (linha) é um trabalho bastante custoso (o parser já faz esse trabalho internamente), pois preciso abrir o layout do banco específico, ver as posições de um determinado campo e aplicar o método Substring para, via Watch, visualizar o resultado.

Como o parser extrai as devidas informações e transforma em objetos .NET customizados, eu decidi criar um debug visualizer para o repositório de títulos. Com isso, se desejar visualizar cada uma das propriedades do meu objeto, posso visualizar de forma amigável as informações, como é ilustrado através da imagem abaixo:

Como podemos notar, podemos extender também o Visual Studio, possibilitando uma maior produtividade, adequando-o de acordo com a nossa necessidade.