Mostrando postagens com marcador C#. Mostrar todas as postagens
Mostrando postagens com marcador C#. Mostrar todas as postagens

quarta-feira, setembro 12, 2012

Dois Bugs Incomuns com ASP.Net

Gostaria de compartilhar com a minha dupla de leitores dois bugs curiosos que eu enfrentei recentemente em uma aplicação ASP.NET. Foram duas pequenas besteiras, mas ilustram alguns aspectos curiosos do ASP.Net.

sábado, agosto 14, 2010

Resolvido: Could not load type 'System.Web.UI.ScriptReferenceBase'

Qual desenvolvedor nunca disse "Mas aqui roda direitinho"? Nas minhas aventuras recentes com o ASP.Net um aplicativo apresentou a "simpática" mensagem de erro abaixo quando foi instalado no servidor do cliente e se tentou navegar para uma página com uma pitada de Ajax:

Could not load type 'System.Web.UI.ScriptReferenceBase' from assembly 'System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.

Obviamente eu nem tinha ideia do que era o tal 'System.Web.UI.ScriptReferenceBase'.

Felizmente o Google mostrou que muita gente já enfrentou isto e não é difícil achar a solução: instalar o Service Pack 1 do .NET 3.5 no servidor. Um pouco mais trabalhoso foi achar alguém que explicasse o porque.

A classe 'System.Web.UI.ScriptReferenceBase' foi introduzida no SP1, daí não ser encontrada no .NET 3.5 "original". Sendo um desenvolvedor consciencioso, é claro que a minha máquina de desenvolvimento e o meu servidor de teste estão atualizados. Quando você compila uma aplicação ASP.Net em uma máquina com o .NET 3.5 SP1, são usadas as bibliotecas mais novas do .NET e as classes de referência a script (usadas para implementar o Ajax) tem como classe base a 'System.Web.UI.ScriptReferenceBase'.

Embora o Visual Studio deixe selecionar a versão alvo de .NET, ele não chega ao ponto de permitir distinguir entre .NET 3.5 e .NET 3.5 SP1. Portanto depois que você atualizou a máquina de desenvolvimento os seus programas (que usem o Ajax ou as classes de referência a script) só rodarão em servidores atualizados.

Achei esta explicação neste blog.

sexta-feira, agosto 21, 2009

Compactação - Parte 7

Fechando esta série de posts, vamos ver como usar a zlib no .Net. A empresa Component Ace adaptou a zlib para C#, gerando assim uma versão 100% gerenciada (managed). O resultado está disponível gratuitamente aqui, com uma licença BSD-like (semelhante à da própria zlib).

Para usar a zlib.net, basta copiar o arquivo zlib.net.dll e referenciá-lo no seu projeto.

O coração da zlib.net são as funções de compactação e descompactação com o encapsulamento zlib (o encapsulamento gzip não é suportado), que estão nas classes Deflate e Inflate. A classe ZStream por sua vez encapsula estas duas. Os métodos destas classes são equivalentes às funções que vimos na parte anterior e portanto o código fica muito semelhante:
// Compacta Arquivo
// Usa diretamente a classe Deflate
private void btnCompacta_Click(object sender, EventArgs e)
{
const int bufsize = 30000;
int flush = zlib.zlibConst.Z_NO_FLUSH;
byte[] bufIn = new byte[bufsize];
byte[] bufOut = new byte[bufsize];
FileStream inFS = new FileStream(txtOrigem.Text, System.IO.FileMode.Open);
FileStream outFS = new FileStream(txtDestino.Text, System.IO.FileMode.Create);
zlib.ZStream z = new zlib.ZStream();

z.deflateInit(zlib.zlibConst.Z_DEFAULT_COMPRESSION);
do
{
z.avail_in = inFS.Read(bufIn, 0, bufsize);
z.next_in_index = 0;
z.next_in = bufIn;
if (z.avail_in == 0)
flush = zlib.zlibConst.Z_FINISH;
do
{
z.avail_out = bufsize;
z.next_out_index = 0;
z.next_out = bufOut;
z.deflate(flush);
outFS.Write(bufOut, 0, bufsize - z.avail_out);
} while (z.avail_out == 0);
} while (flush != zlib.zlibConst.Z_FINISH);
z.deflateEnd();
outFS.Close();
inFS.Close();
}
A zlib.net implementa ainda uma classe ZOutputStream que deixa o processo de compactação ou expansão transparente, como mostra a rotina abaixo:
// Expande arquivo
// Utiliza ZOutputStream
private void btnExpande_Click(object sender, EventArgs e)
{
int len;
const int bufsize = 30000;
byte[] buffer = new byte[bufsize];
FileStream outFS = new FileStream(txtDestino.Text, System.IO.FileMode.Create);
zlib.ZOutputStream outZStream = new zlib.ZOutputStream(outFS);
FileStream inFS = new FileStream(txtOrigem.Text, System.IO.FileMode.Open);
while ((len = inFS.Read(buffer, 0, bufsize)) > 0)
{
outZStream.Write(buffer, 0, len);
}
outZStream.Flush();
outZStream.Close();
outFS.Close();
inFS.Close();
}
Obs.: para usar ZOutputStream para compactar, especifique o nível de compressão no construtor (o que não é muito intuitivo).

O projeto completo do teste, para o Visual Studio 2005, está aqui.

segunda-feira, junho 30, 2008

Desenvolvimento Multi-Camada Simplificado no C#

A expressão "desenvolvimento multi-camada" é usada com freqüência no
desenvolvimento de software, nem sempre com o mesmo significado. De uma forma geral, a idéia de quebrar um desenvolvimento em camadas tem por objetivos facilitar o desenvolvimento ("dividir para conquistar") e facilitar a substituição de partes do código em eventuais (inevitáveis?) versões futuras.

Neste post estou me referindo à divisão interna em camadas de um aplicativo PC tradicional do tipo client-server (ou seja, estou falando das camadas dentro da camada client). Vamos tomar como exemplo um aplicativo simples, que apresenta na tela uma tabela com as informações de livros que estão armazenadas em uma base de dados. Para simplificar o lado server, vou usar uma base de dados Access (é trivial substituí-la por uma base SQL Server ou qualquer outra acessível pelo ADO.Net).

Já faz bastante tempo que o desenvolvimento deste tipo de aplicação é facilitado pelos ambientes integrados. A maioria deles possui um componente visual que pode ser conectado diretamente a uma base de dados; a maior parte do desenvolvimento é feita clicando e arrastando, como pouca escrita de código. O problema desta solução é que fica tudo misturado em um único fonte. Se quisermos mudar a fonte dos dados ou o componente visual acabamos tendo que refazer tudo.

A solução clássica multi-camada para este tipo de aplicação envolve três camadas:

  • a camada de dados, responsável por acessar a base de dados recuperando e atualizando os dados necessários.
  • a camada de negócios, que define objetos que representam as entidades de negócio e implementam as regras de negócio. Esta camada faz a ligação entre as outras duas.
  • a camada de apresentação (ou interface com o usuário), responsável por apresentar os dados contidos em objetos de negócio.

Desta forma fica mais fácil alterar uma parte da aplicação sem interferir no resto.

A partir da versão 2 do C# e do .Net Framework existe uma forma bastante simples de desenvolver aplicações com estas três camadas. Segue abaixo um passo a passo para fazer a minha aplicação exemplo no Visual Studio 2005.

1) Selecionar File New Project Windows Application, chame a aplicação de AppCamadas.

2) Clicar com o botão direito em Appcamadas no Solution Explorer, selecionar Add Class. Chame a classe de Livro, ela será a nossa camada de negócios. Entre com o código abaixo:

    class Livro
{
private string _titulo;
public string Titulo
{
get { return _titulo; }
set { _titulo = value; }
}

private string _autor;
public string Autor
{
get { return _autor; }
set { _autor = value; }
}

private DateTime _dtEmprestimo;
public DateTime DtEmprestimo
{
get { return _dtEmprestimo; }
set { _dtEmprestimo = value; }
}

public bool Emprestado
{
get { return DtEmprestimo != DateTime.MaxValue; }
}

public Livro() { }
public Livro(string titulo, string autor, DateTime dtEmprest)
{
Titulo = titulo;
Autor = autor;
DtEmprestimo = dtEmprest;
}
}

Note que estamos usando propriedades para controlar o acesso aos membros. Isto permite encapsular uma regra de negócio: livros não emprestados são indicados por uma data de empréstimo igual a DateTime.MaxValue.

3) Build / Build Solution (para a IDE conhecer a classe e suas propriedades)

4) Clicar o botão direito em Form1.cs no Solution Explorer, selecionar Rename, alterar o nome para frmConsulta, confirmar. Esta é a nossa camada de apresentação.

5) Selecionar aba frmConsulta.cs [Design]. Selecionar o form. Alterar a gosto Text, Size, StartPosition.

6) Arrastar do toolbox um DataGridView. Mudar o tamanho para ocupar todo o form. Com o DataGrivView selecionado no form, mudar as propriedades

  • Name: dgvLicros
  • Anchor: Top, Bottom, Left, Right (faz resize automático do grid quando muda tamanho do form)
  • Read Only: True
  • Row Select Mode: FullRowSelect

7) Clicar na seta tinha no alto à direita do grid (smarttag).


Limpar Enable Adding e Enable Deleting. SelecionarChoose Data Source, Add Project DataSource, Object, Next, escolher Livro, Next e Finish.

Neste ponto é que é feita a mágica: o grid foi configurado para apresentar os membros e propriedades públicos da nossa classe. Um objeto BindingSource (criado automaticamente pela IDE) fará a ponte entre o componente de apresentação e a fonte dos dados.

8) Clicar novamente na seta tinha no alto à direita do grid, selecionar Edit Columns. Mover Emprestado para o inicio da lista e remover DtEmprestimo.

9) Clicar com o botão direito em Appcamadas no Solution Explorer, selecionar Add Class. Chamar a nova classe de AcessoBD, esta será a nossa camada de dados. Entre com o código abaixo:

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.OleDb;

namespace AppCamadas
{
class AcessoBD
{
private const string StrCnx =
@"Provider=Microsoft.Jet.Oledb.4.0;" +
@"Data Source=Livros.mdb;";

public static List GetLivros()
{
List lstLivros = new List();
OleDbConnection cnn = new OleDbConnection(StrCnx);
OleDbCommand cmd = new OleDbCommand
("SELECT * FROM LIVROS", cnn);
try
{
cnn.Open();
OleDbDataReader dr = cmd.ExecuteReader();
DateTime dtEmprest;
while (dr.Read())
{
if (dr["Emprestimo"].Equals(System.DBNull.Value))
dtEmprest = DateTime.MaxValue;
else
dtEmprest = Convert.ToDateTime(dr["Emprestimo"]);
lstLivros.Add (new Livro (dr["Titulo"].ToString(),
dr["Autor"].ToString(),
dtEmprest));
}
cnn.Close();
}
catch
{
// tratamento de erros fica como exercício ao leitor
}
return lstLivros;
}
}
}

Este código é bastante simplista. Os dados extraídos da base de dados são colocados em uma lista de objetos de negócio. O BoundingSource usado na camada de apresentação suporta vários tipos de origem para os dados, graças ao recurso de generics do C# criamos de forma fácil uma lista de livros.

10) Build / Build Solution (opcional, só para ver que não fizemos nada de errado até agora)

11) Selecionar a aba frmConsulta.cs [Design]. Selecionar o form, dar um duplo clique no form (para criar um tratamento para o evento Load). Adicionar a linha abaixo.

            dgvLivros.DataSource = AcessoBD.GetLivros();

12) Digitar F5 (Start) e ver o show!

Embora esta técnica requeira um pouco mais de digitação que uma ligação direta do datagrid à base de dados, ela organiza melhor o código e nós dá uma grande liberdade. Se, por exemplo, quisermos mover a base de dados do Access para o SQL Server, basta alterar a camada de dados, sem mexer nas demais.

O projeto completo, incluindo a base de dados, pode ser baixado daqui.

quarta-feira, março 14, 2007

Três Livros Sobre C#

No ano passado incluí na minha coleção três livros sobre C# de características bem difererentes:

Pro C# 2005 and the .NET 2.0 Platform, Third Edition
Andrew Troelsen

Um livro bastante ambicioso, que tenta descrever a linguagem C#, a plataforma .Net e o framework .Net (e ainda encontra espaço para falar de conceitos gerais sobre programação orientada a objetos). É um livro imenso (ainda estou no primeiro terço), mas que gostaria de ter lido quando comecei a aprender C#. É claro que alguns assuntos como Windows Forms, ASP.Net, ADO.Net e Web Services ficam um pouco corridos (afinal existem livros inteiros sobre estes assuntos), mas é um dos poucos livros que explica bem o básico (no sentido de fundamento não de simples).

ASP.NET 2.0 Website Programming
Marco Bellinaso

A idéia do livro é ótima: descrever a construção de um site real do começo ao fim. Cada capítulo possui três partes: o problema, o projeto e a solução. Na primeira são descritos os requisitos, na segunda são discutidas as alternativas para atendê-los e na terceira é apresentado com detalhes a implementação adotada. O código todo está disponível para download (inclusive foi adotado como um dos exemplos pela Microsoft, veja aqui) e o site está on-line em http://www.dotnet2themax.com/thebeerhouse/.

O autor não foge de assuntos complexos nem economiza linhas de código e utiliza os recursos mais recentes do C# e do ASP.Net. O único porém é a diagramação do livro que deixa a leitura cansativa.

Altamente recomendado para quem desenvolver uma aplicação Web mais real que os Next/Next/Finish dos wizards do Visual Studio.

Visual C# 2005 - A Developer´s Notebook
Jesse Liberty

Este é um livro curto, que se concentra em algumas poucas (e importantes) novidades do C# 2.0 e Visual Studio 2005 e se destaca pela parte visual (imita um caderno com direito a um quadriculado de fundo e manchas de copo). O texto é muito bom, mas nem sempre muito profundo ou completo. Resumindo, é um livro extremamente agradável de ler, voltado para quem já conhece as versões anteriores do C# e quer se atualizar.

PS: Para quem está aguardando a terceira parte da série sobre Compiladores, peço um pouco de paciência, pois as coisas estão um pouco corridas.

segunda-feira, outubro 02, 2006

Programando em C#

Como estou acostumado a programar principalmente em C, programar em C# traz algumas novidade e desafios para mim:
  • Excetos pelas variáveis numéricas, enumerações e estruturas (value types), tudo é alocado no heap gerenciado (reference types) e será liberado da memória pelo Garbage Collector. Ou seja, é um monte de X x = new X(). Para quem está acostumado a gerenciar os seus bytes um a um com muito carinho é um grande desafio de confiança.
  • No C# não existem variáveis globais e tudo tem que estar dentro de uma classe. Por exemplo, imagine que você queira criar uma rotina chamada Arredonda e uma constante chamada ValorMinimo, que você queira usar em vários lugares. Você precisa declará-los como membros estaticos de alguma classe. No C# 2005, é possível criar classes estáticas úteis justamente para este tipo de coisa.
  • Os controles geram eventos quando são alterados pelo operador ou pelo programa. Por exemplo, se você selecionar um item de um combox por programa, a rotina que trata o evento de mudança de seleção será chamada. Em alguns casos isto é muito útil mas em outros causa algumas complicações.
Como disse anteriormente um grande problema é que o framework é imenso e o help é muito sucinto. Estou toda hora procurando coisas no Google. Duas dicas das que acumulei até agora:

Pegar a versão do programa no assembly

No properties do projeto é definida a versão que será colocada no assembly. Para pegar este valor, por exemplo para o diálogo de Sobre, basta usar

Assembly.GetExecutingAssembly().GetName().Version.Major.ToString()
Assembly.GetExecutingAssembly().GetName().Version.Minor.ToString()

Usar um LinkLabel

Ainda no diálogo Sobre, imagine que você quer colocar um link para abrir a sua home page. O controle LinkLabel faz isto, se você souber usar. Coloque o LinkLabel no diálogo e altere a propriedade Text para o URL desejado (por exemplo, http://www.microsoft.com). No construtor do formulário, copie o texto para a lista de links:

linkHomepage.Links.Add(0, linkHomepage.Text.Length, linkHomepage.Text);

No evento LinkClicked, siga a receita abaixo (encontrada em http://www.peterritchie.com/Hamlet/Articles/63.aspx)

string strLink = e.Link.LinkData.ToString();
Process process = new Process();
process.StartInfo.FileName = "rundll32.exe";
process.StartInfo.Arguments = "url.dll,FileProtocolHandler " + strLink;
process.StartInfo.UseShellExecute = true;
process.Start();

A solução apresentada no help é mais simples

System.Diagnostics.Process.Start (e.Link.LinkData as string);

só que não funciona...

quinta-feira, setembro 14, 2006

Programando em C#

Embora eu já tenho obtido a certificação Microsoft em C#, não faz parte do meu dia-a-dia programar em C#. Normalmente programo em C, usando diretamente a API do Windows.

Surgiu agora uma "oportunidade" de mexer numa aplicação Windows em C# (na verdade, a necessidade de fazer os acertos finais em um projeto que eu estava coordenando e outro pessoa estava executando). Seguem algumas observações desta experiência.

Desempenho da IDE e da Aplicação

Um dos receios quanto ao Visual Studio e aplicações gerenciadas é quanto ao desempenho. A minha experiência até o momento foi boa, consigo desenvolver bem em um Pentium 4 2.8 HT, com 512M de Ram, mesmo com o Sql Server rodando na mesma máquina.

A Qualidade do Help

Minha impressão é que ao mesmo tempo que o Help do Visual Studio (MSDN Library) vem aumentado em tamanho (veio em DVD) a qualidade vem caindo. Eu fico lembrando toda hora da velha piada do piloto do helicoptero perdido que recebe a informação de que está... num helicóptero (informação tecnicamente correta e praticamente inútil).

A documentação está muitas vezes restrita ao tipo dos parâmetros (informação já apresentada pelo Intelisense). Alguns "exemplos" não fornecem muita coisa mais. Além disso, tem a mesma informação repetida na mesma página para VB.Net, C#, C++, J# e JScript.

Em comparação, a documentação da API do Windows contém muita informação sobre o quando usar, porque usar, etc.

Acessando Embedded Resources

Embedded Resources são informações, como textos e imagens, que são armazenadas dentro do próprio executável (mais precisamente, no Assembly .Net). No C + Windows API, o uso disto é quase sempre explícito. No .Net, a maior parte do tempo você não se preocupa com isto. Entretanto, se você precisar se preocupar, pode ter surpresas.

Basicamente o que eu precisei era acessar um ícone. Vou começar apresentando a solução:
  1. Acrescente o arquivo com o ícone na solução (clique com o botão direito no nome do projeto, selecione Add Existing Item, navegue até o arquivo)
  2. Selecione o arquivo e em Properties mude Build Action para Embedded Resource
  3. No ponto em que você precisar do ícone coloque (supondo que o arquivo foi "Icone.ico")
Icon meuIcone = new Icon (GetType(), "Icone.ico")

Parece óbvio, afinal como eu posso ter errado isto? Bem, o help não tinha muita informação além de

public Icon (Type type, string resource);

e os exemplos que eu vi no livro Programming Microsoft Windows Forms e em alguns sites foram

Icon meuIcone = new Icon (GetType(), "Projeto.Icone.ico")

para confundir um pouco mais, embora a linha acima dê erro durante a execução (não acha o resource), se você examinar o Assembly com o MSIL Disassembler, vai ver que o ícone está lá e com o nome "Projeto.Icone.ico".

O que ocorre (e eu só achei no adendo do post de um blog) é que na hora de procurar o resource o namespace da classe do primeiro parâmetro é colocado automaticamente na frente do nome informado no segundo parâmetro. E o nome do resource no assembly é feito colocando o namespace default na frente do nome do arquivo. No meu caso, os namespaces eram os mesmos, e acabava sendo procurado Projeto.Projeto.Icone.ico ao invés de Projeto.Icone.ico.

Se você criar uma pasta no projeto para os seus ícones, por exemplo Icones, o nome do resource vai ficar Projeto.Icones.Icone.ico e portanto de ser usado

Icon meuIcone = new Icon (GetType(), "Icones.Icone.ico")

Depois que a gente entende, fica fácil...