Como acompanho o Blog da BCLTeam, Mike Rousos está escrevendo uma série de artigos sobre o Tracing (System.Diagnostics) do .NET que vale a pena dar uma olhada. Até então, os links são:
Arquivo da categoria: .NET Framework
Operador ??
Dando uma olhada no Help do Visual Studio 2005 Beta 2, encontrei um novo operador dentro da linguagem C#. Trata-se do ??. Um exemplo do uso do mesmo, é mostrado abaixo:
int x = 2;
int? y = null;
int z = x ?? y;
Console.WriteLine(z.ToString());
O sintaxe retorna o valor que está definido do lado esquerdo do operador se o mesmo não for nulo. Caso contrário, o valor que está definido do lado direito do operador é retornado. O output do código acima retorna 2, pois y está definido como nulo. Se alterarmos o valor de y de nulo para algum outro valor válido (inteiro), o valor dele será atribuido à z e, consequentemente exibido.
Para quem quiser saber mais sobre Nullable Types, acesse este link.
Parâmetros para Thread
Fiquei curioso para saber como podemos passar parametros para Threads, ou seja, quando definimos um método de uma determinada classe que nossa Thread vai executar quando a mesma for iniciada. Atualmente não se tem muitos recursos para fazer isso e para conseguirmos suprir essa necessidade definimos o construtor da classe para receber o parametro que desejamos passar para que o método o utilize. Em código, fica mais ou menos como:
class MinhaApp
{
[STAThread]
static void Main(string[] args)
{
Teste t = new Teste(“Meu Parametro”);
Thread tr = new Thread(new ThreadStart(t.Escreve));
tr.Start();
}
}
public class Teste{
private string _param;
public Teste(string param){
this._param = param;
}
public void Escreve(){
Console.WriteLine(this._param);
}
}
Como podemos ver, a classe “Teste” recebe em seu construtor o(s) parametro(s) que vamos utilizar dentro do método “Escreve” e, quando o método for executado, teremos a certeza que os parametros estarão disponíveis para a utilização.
Andei analisando e, na versão 2.0 do .NET Framework temos um novo delegate do tipo ParameterizedThreadStart, que recebe um parametro do tipo Object e, para o ideal funcionamento deste, o método Start da classe Thread agora tem um overload, que recebe o parametro do tipo Object que será passado para o delegate. Em conjunto com os métodos anonimos, o código fica bastante elegante:
class Program
{
static void Main(string[] args)
{
ParameterizedThreadStart pts = new ParameterizedThreadStart(delegate(object o)
{
Console.WriteLine(o.ToString());
});
Thread t = new Thread(pts);
t.Start(“Meu Parametro”);
}
}
Magic Numbers
Depois que deixei de utilizar Magic Numbers em minhas aplicações, as coisas se tornaram muito mais fáceis e flexiveis, inclusive para as outras pessoas que trabalham comigo. Além deixar de utilizá-los em controles de laços For Next, mudei a minha forma de recuperar os dados de meus DataReaders, onde defino as constantes com os nomes das colunas e seus respectivos valores que, são números inteiros que correspondem ao índice do campo que desejamos recuperar.
Um exemplo disso é exibido no código abaixo:
#region ” Colunas da DB “
const int ID = 0;
const int NOME = 1;
const int EMAIL = 2;
#endregion
Cliente c = new Cliente();
//….
c.Email = dr.GetString(EMAIL);
c.ID = dr.GetInt32(ID);
c.Nome = dr.GetString(NOME);
Isso também é uma das boas práticas que é explicado no bom livro chamado Code Complete. E antes que me perguntem se isso causa impacto na performance, eu já adianto que não, pois quando trabalhamos com constantes, ao compilar o código o compilador substitui automaticamente essas constantes pelo seu valor correspondente.
Documentação XML
Há neste post um conteúdo bastante interessante onde o autor exemplifica o uso de cada tag XML e sua respectiva utilidade na documentação de código .NET.
Múltiplos Arquivos de Configuração
Essas configurações geralmente são valores que precisamos para a aplicação, como por exemplo: string de conexão com a base de dados, servidor de SMTP, entre outras. Veremos abaixo como definimos no arquivo Web.Config da aplicação para que o ASP.NET leia as configurações de um outro arquivo:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings file="Settings.config"/>
<!-- outras configurações aqui... -->
</configuration>
|
Como podemos reparar, no atributo file definimos o arquivo chamado “Settings.config” que será o arquivo responsável por armazenar as configurações que nosso projeto utilizará. A estrutura do mesmo é exibida abaixo:
<?xml version="1.0" encoding="utf-8" ?>
<appSettings>
<add key="TesteKey" value="123456" />
</appSettings>
|
Feito isso, podemos normalmente utilizar a forma tradicional que fazemos para recuperar e utilizar um valor que está definido no arquivo Web.Config, utilizando a classe ConfigurationSettings. Abaixo é mostrado como recuperar e escrever este valor:
using System.Configuration; //... Response.Write(ConfigurationSettings.AppSettings["TesteKey"]); |
Acessando uma URL segura (HTTPS) através de System.Net
Hoje tive uma experiencia interessante, ou seja, um Windows Service que criei para automatizar alguns processos aqui na empresa utiliza as classes de HttpWebRequest e HttpWebResponse para enviar e recuperar dados de um serviço prestado por terceiros, que corre em cima de HTTPS, depois de migrado para um servidor Windows Server 2003, ao executá-lo e fazer uma requisição a este link seguro, o seguinte erro era retornado:
An unhandled exception of type ‘System.Net.WebException’ occurred in system.dll
Additional information: The underlying connection was closed: Could not establish trust relationship with remote server.
Fui então fazer alguns testes tentando acessar este mesmo link através do browser, e resultou com sucesso. Pude reparar que ao digitá-lo no browser, uma janela é aberta, perguntando ao usuário se ele quer ou não confiar no certificado digital que é fornecido pelo servidor que estamos tentando acessar. Com certeza optei por sim, e o retorno foi imediato. Mas a questão estava aí, ou seja, se a requisição é feita por um processo automático (Windows Service), que clicaria no botão? 😛 Nesse caso, criamos uma classe que implementa a interface ICertificatePolicy para gerir e autorizar que o certificado seja aceito. O código para isso é:
Imports System.Net
Public Class TrustAllCertificatePolicy
Implements ICertificatePolicy
Public Function CheckValidationResult(ByVal srvPoint As System.Net.ServicePoint, ByVal certificate As System.Security.Cryptography.X509Certificates.X509Certificate, ByVal request As System.Net.WebRequest, ByVal certificateProblem As Integer) As Boolean Implements System.Net.ICertificatePolicy.CheckValidationResult
Return True
End Function
End Class
No código acima, através do método CheckValidationResult retornamos True para sempre aceitar e confiar neste certificado. Depois disso, antes de efetuar a requisição devemos definí-la na propriedade CertificatePolicy, como é exibido abaixo:
System.Net.ServicePointManager.CertificatePolicy = New TrustAllCertificatePolicy
Para conseguir a encontrar essa solução, li o artigo do Jan Tielen, e pode ser visto aqui. Lembrando que isso também é valido para Web Services que rodam em ambiente seguro, como ele explica.
Analisando o Microsoft PetShop 3.0
Para quem não conhece, o Microsoft PetShop 3.0 é uma aplicação de uma loja virtual fictícia que vende pequenos animais que a Microsoft construiu com o propósito de um caso de estudo baseado no Java Pet Store da Sun, mas construindo-a sob a Plataforma .NET. Mas este artigo não tem o intuito de mostrar esta comparação e sim abordar a estrutura em que o mesmo foi construído. Para quem se interessar, poderar ver o estudo/análise desta comparação através deste link: http://www.middleware-company.com/buzz/buzz0703.jsp.
Microsoft PetShop 3.0 é uma aplicação Web, construída em ASP.NET, que comunica com componentes de negócios escritos em C#, quais compõem a Camada de Negócios (Business Logic Layer). ADO.NET é utilizado para acessar as Base de Dados, acesso qual está abstraído em uma camada de acesso a dados (Data Access Layer (DAL)), separando-se da camada de negócios.
|
|
| Figura 1 – Microsoft PetShop 3.0. |
Estrutura da Solução
Como podemos ver abaixo na Figura 2, a solução da Aplicação e constituída de diversos projetos, sendo 1 projeto do tipo WebApplication (User Interface), 8 projetos do tipo Class Library e 2 projetos “Build” para efetuar algumas configurações no ato da instalação. Veremos abaixo, a descrição de cada um deles.
|
|
| Figura 2 – Estrutura da Solução. |
|
Aplicação Distribuída
Para que a solução atenda o cenário de uma aplicação distrubuída, são criados duas Base de Dados (MSPetShop e MSPetShopOrders). Em uma delas, são armazenadas as informações referentes aos Produtos, Clientes, Banners, entre outros. Já na segunda Base de Dados, são armazenados os pedidos dos clientes e seus repectivos Items. Veremos abaixo a finalidade de cada uma das tabelas:
|
OBS.: As linhas que estão com o fundo cinza, pertencem a Base de Dados “MSPetShopOrders”.
Arquitetura
|
|
| Figura 3 – Arquitetura da Solução. |
Como vemos na figura acima, a aplicação está dividida em três grandes camadas: Camada de Apresentação, Camada de Negócios e Camada de Acesso a Dados. O COM+ entra no jogo para garantir a integridade dos pedidos, ou seja, quando efetuamos uma compra, se algo falhar durante a transação, o mesmo será desfeito, mantendo assim a integridade dos dados.
Os componentes de negócios fazem as devidas manipulações para serem exibidas no Front-End, mas tudo começa a ficar interessante a partir daqui.
A idéia é de deixar a aplicação independente de Base de Dados. Isso não seria tão difícil, pois poderíamos utilizar OleDb para acessar qualquer uma das Bases de Dados, criando assim uma única classe genérica de acesso. Mas isso torna a aplicação pouco performática, pois não utiliza os Providers específicos para cada Base de Dados. Um dos requirementos ao construir esta aplicação era justamente obter alta performance, e em virtude disso, é indiscutível o uso dos Providers nativos: SqlClient e OracleClient.
Com essa meta, uma forma de simplicar o acesso aos dados, foi utilizar a Pattern Abstract Factory [Gof], juntamente com Reflection que cria e instancia o objeto correto em runtime. É criado uma Interface genérica, que contém os métodos necessários que deve ser implementada em cada classe de acesso a dados. Para cada classe de acesso a dados, você deve implementar esta Interface, utilizando as classes e o Provider específico para o Banco de Dados que está sendo requisitado.
Depois das classes de acesso a dados de Bases diferentes, cria-se uma Factory, que em runtime, utilizando Reflection, vai identificar qual das classes de acesso a dados irá instanciar, baseando-se em uma chave definida no arquivo Web.Config da aplicação Web. A figura abaixo, ilustra o processo:
|
|
| Figura 4 – Escolhendo qual DB utilizar. |
Se algum dia tivermos a necessidade de que a aplicação atenda a mais um tipo diferente de Base de Dados, por exemplo Microsoft Access, teríamos que seguir o seguinte procedimento:
1 – Criar classes de acesso a dados, utilizando OleDb e nela implementar as Interfaces IDAL;
2 – Compilar estas novas classes e fazer Deploy para o servidor;
3 – Alterar a configuração no arquivo Web.Config para chamá-la.
DAO – Data Access Objects
Data Access Objects ou DAO, é um padrão de projeto que é utilizado para abstrair o acesso a dados da camada de negócios (Business Layer), que por sua vez, acessa a Base de Dados através de Data Access Objects, tornando assim transparente o acesso. A DAO é uma DAL específica para um determinado objeto, fazendo com que a camada de negócios fique sem conhecimento da Base de Dados, pois isto está tudo encapsulado.
Esta abstração encapsula a implementação utilizada para acesso, gravação, gerenciamento da conexão referente a fonte de dados, que realmente não interessa a Business Layer qual o tipo de armazenamento de dados está sendo utilizado.
|
|
| Figura 5 – Diagrama de Classe da Pattern DAO. |
Abstract Factory
Abstract Factory fornece uma Interface ou Classe Abstrata para criar uma família de objetos relacionados dependentes sem especificar a classe concreta. Sendo assim, no projeto PetShop 3.0, é criado uma Interface chamada IDAL, que contém os métodos de manipulação de dados, qual é implementada em todas as classes concretas para accesso à diferentes Base de Dados.
Com isso, ao invés do Cliente instanciar a classe concreta, chama-se método de criação (Factory Method), que invoca a Abstract Factory, e utiliza este objeto para chamar os métodos e/ou propriedades da classe concreta. Ganhamos bastante flexibilidade, pois baseado em uma Key (no caso, chamada de WebDAL) dentro do arquivo Web.Config, em runtime ele saberá qual dos objetos concretos deverá instanciar.
Traduzindo em Código
Para que as coisas fiquem um pouco mais claras, veremos abaixo, como é feito isso. Sabendo que temos que implementar as Interfaces contidas na IDAL, veremos uma delas (Account):
|
|
| Código 1 – Interface IAccount. |
Na camada de acesso a dados, deve-se implementar esta interface (IAccount) e codificar utilizando o Provider específico para aquela Base de Dados. No caso do PetShop, a implementação foi realizada utilizando o DAAB (Data Access Application Block) que utiliza SqlClient e também implementando para acesso a Base de Dados Oracle, utilizando OracleClient.
|
|
| Código 2 – Implementado Interface IAccount. |
Feito isso, a Factory se encarrega de verificar qual dos objetos instanciar, verificando no Web.Config qual a Base de Dados a ser utilizada, e através de Reflection, carrega a cria a instância da mesma através do Factory Method:
|
|
| Código 3 – Factory Method. |
E finalmente em nossa camada de negócios, utilizamos nossa Factory para que ela gere a instância correta da classe de acesso e manipulação da Base de Dados a ser utilizada no Projeto:
|
|
| Código 4 – “Chamando” o Factory Method na Camada de Negócios. |
Conclusão: Como vimos a utilização da Orientação à Objetos no Design de nossas aplicações, nos traz grande produtividade e facilidade em uma posterior manutenção. No caso do PetShop que vimos acima, é um ótimo exemplo, onde precisamos gerar independência de Base de Dados, mas existem vários cenários onde podemos utilizar Abstract Factory e principalmente DAO.
Formatando valores em colunas do DataGrid
Frequentemente quando utilizamos um controle do tipo DataGrid, e inserimos valores do tipo Data, Dinheiro, Inteiros ou Decimais, precisamos formatar esse valor de acordo com a finalidade desse Campo. Nesse artigo apresentarei a propriedade DataFormatString da BoundColumn de um DataGrid.
Existem dois tipos de formatação: Standard Formats e Custom Formats. Como o objetivo do artigo é mostrar as formatações mais utilizadas em aplicativos para serem executados nos padrões brasileiros, então deixarei claro o seguinte: O padrão para valores numéricos será adotado o Stardand Format. Já a formatação para datas, será utilizado o Custom Format.
A propriedade DataFormatString fornece uma formatação customizada para o valor inserido na BoundColumn. Esta propriedade consiste em duas partes separadas por dois pontos estando dentro de um par de chaves da seguinte forma: {:}. Isso é válido apenas quando estiver inserindo na BoundColumn valores numéricos ou do tipo data.
A sintaxe é a seguinte: {0:[Formato][Qtde. Casas Decimais]}. O caracter que vem após os dois pontos, é o formato em que o valor será exibido. Você também poderá optar por definir a quantidade de casas decimais da seguinte forma: {0:C2}. A seguir uma lista com os valores possíveis:
|
Observações: Os caracteres acima que especificam o formato a ser exibido não são case-sensitive, exceto para o X, pois se ele for minúsculo, os valores serão apresentados em minúsculo, do contrário, serão exibidos em maiúsculo.
Para configurar os valores no DataGrid, clique com o botão direito do mouse em cima do mesmo, e selecione Property Builder. Em seguida, vá até a aba Columns e ao incluir uma nova BoundColumn, a propriedade DataFormatString será habilitada para que você possa definir a formatação customizada. A imagem abaixo ilustra o processo:
|
|
| Figura 1 – Configurando a propriedade DataFormatString do DataGrid. |
A figura abaixo exibe os valores no DataGrid de acordo com a formatação pré-definida na propriedade DataFormatString.
|
|
| Figura 2 – Configurando a propriedade DataFormatString do DataGrid. |
Aqui chamo a atenção para a coluna onde é exibido o valor no formato moeda e o separador de casas decimais. Como não foi definido nenhuma cultura no arquivo Web.Config, por padrão ele adota as configurações regionais definidas no servidor. Se acrescentar a cultura pt-BR nas configurações de nossa aplicação, verão que os valores passarão a serem exibidos no formato brasileiro. Abaixo a ilustrução deixará claro:
|
|
| Código 1 – Alterando a cultura no arquivo Web.Config. |
Com essa mudança, agora temos os valores sendo exibidos no padrão brasileiro, conforme mostrado na figura 3:
|
|
| Figura 3 – Valores sendo exibidos no formato brasileiro. |
Além das configurações para valores numéricos, ainda podemos utilizar a propriedade DataFormatString para formatarmos datas que são inseridas no DataGrid. Abaixo uma tabela as as possibilidades de formatação para datas:
|
OBSERVAÇÕES: Devemos nos atentarmos para o MM e para o mm, pois maiúsculo significa Mês, já o minúsculo significa Minutos.
Há ainda vários outros padrões para a formatação de datas, quais não optei por colocar aqui por que utilizamos na maioria das vezes o formato brasileiro. Mas para quem se interessar pode encontrar maiores informações no link direto da fonte da Microsoft: Standard DateTime Format Strings.
Como dito anteriormente, a configuração da formatação para data, funciona da mesma forma que a formatação para valores numéricos, ou seja, você define na propriedade DataFormatString da BoundColumn do DataGrid, como por exemplo: {0:dd/MM/yyyy hh:mm:ss}. A Figura 4 ilustra os possíveis formatos para datas:
|
|
| Figura 4 – Formatando Datas. |
IMPORTANTE: Você poderia também ao invés de barras “/” utilizar o hífen “-” como separador para as Datas, ficando a String de formatação da seguinte forma: {0:dd-MM-yyyy hh:mm:ss}.
Poderá também fazer a formatação diretamente no HTML, utilizando a propriedade DataItem em conjunto com a método Format. Exemplo:
|
|
| Código 2 – Formatando valores diretamente no código HTML. |
CONCLUSÃO: Com este artigo mostrei as possíveis formas de formatação para valores do tipo numéricos de datas que são inseridos nas BoundColumns do DataGrid. Aconselho a darem uma olhada também no link que coloquei um pouco mais acima que diz respeito à outros tipos de formatação bastante utilizados pelas aplicações.
Trabalhando com Arquivos
Frequentemente necessitamos trabalhar com arquivos, sejam eles “*.txt”, “*.xml”, “.csv”, etc. Nesse artigo mostrarei como fazemos para criar, excluir, ler arquivos e diretórios a partir de uma Aplicação Web (ASP.NET).
Separarei por tópicos, onde cada um abordará sobre um assunto. Começaremos pelo Diretórios:
Criando um Diretório
|
|
| Código 1 – Criando um Diretório. |
Basta criarmos uma variável do tipo System.IO.Directory e executar a função CreateDirectory passando o caminho e nome do diretório a ser criado.
Excluindo um Diretório
|
|
| Código 2 – Excluindo um Diretório. |
É sempre boa prática antes de chamarmos a Sub Delete verificarmos se o diretório existe ou não. Para isso utilize a Function Exists do objeto Directory, que retornará um valor boleano indicando se existe ou não o diretório especificado.
Listando Arquivos e SubDiretórios
|
|
| Código 3 – Listando Arquivos e SubDiretórios. |
No Código 3 chamo a atenção para a Linha 9. Como o método GetFiles é sobrecarregado, podemos especificar qual o tipo de arquivos queremos listar. No exemplo acima, optei por mostrar todos os arquivos. Se quisessemos exibir apenas arquivos “*.txt”, poderíamos fazer: GetFiles(“*.txt*”).
Vamos agora as operações que podemos realizar com os arquivos.
Criando um Arquivo
|
|
| Código 4 – Criando um arquivo. |
Escrevendo em um Arquivo
|
|
| Código 5 – Escrevendo em um arquivo. |
Na instância do StreamWriter já informamos o caminho e o nome do arquivo que deverá ser escrito. Depois chamamos o método WriteLine e passamos a variável “strTexto” para que que ela seja escrita no arquivo.
Excluindo um Arquivo
|
|
| Código 6 – Excluindo um arquivo. |
Aqui fazemos da mesma forma qual fizemos para os Diretórios, ou seja, antes de excluirmos verificamos se o arquivo existe. Caso positivo, chamamos o método Delete().
Lendo Arquivos
|
|
| Código 7 – Lendo Arquivos. |
Utilizando o método ReadToEnd da classe StreamReader não precisamos fazer um loop para percorrer todo o arquivo.
Bem, é importante lembrar que nesse artigo quero apenas dar exemplos de “como fazer para…”. É extremamente importante utilizarmos os tratamentos de erros (Try…Catch…Finally) para executar as rotinas acima, pois não sabemos se os arquivos e/ou diretórios existem ou não, se temos permissão para executar, etc.
CONCLUSÃO: A Plataforma .NET nos trouxe uma grande facilidade na manipulação de arquivos. Sejam eles “*.txt”, “*.xml”, etc. Além de prático as classes como StreamReader e StreamWriter fornece aos desenvolvedores grandes recursos em poucas linhas de código. E para quem está “encantado” na facilidade, espere até conhecer as “My” do Visual Studio .NET Whidbey (http://download.microsoft.com/download/c/7/f/c7f7a575-79ac-4399-9535-3ed57bc292f2/mynamespace.doc).








