terça-feira, 28 de abril de 2009

Navegação de pastas em FTP

Você por acaso já se deparou com o seguinte problema?
Você tem que se conectar em uma determinada pasta de FTP (ex.: ftp://myfp.com.br/myfolder/test) só que quando você se conecta no servidor de FTP a pasta myfolder não esta lá.

Pois bem, um dos motivos desse problema é por que a pasta default do usuário utilizado na conexão não é a pasta raiz do servidor (ex.: ftp://myftp.com.br/users/myuser).

Agora, como fazer para acessar a pasta raiz (ex.: ftp://myftp.com.br) e depois acessar a pasta desejada (ex.: ftp://myftp.com.br/myfolder/test)?

O truque é simples, basta colocar os caracteres %2F/ na quantidade necessário até localizar a raiz e depois colocar o endereço da pasta desejada

(ex.: ftp://myftp.com.br/%2F/%2F/myfolder/test)

quarta-feira, 15 de abril de 2009

terça-feira, 14 de abril de 2009

Realizando uma análise mais apurada de exceções

Você já parou para analisar como tem tratado exceções nas suas aplicações?
Eu já tive vários problemas em projetos cujo tratamento de exceções foi ignorado ou pior foi mal feito, isso mesmo, quando não tratamos uma exceção de forma adequada geralmente os resultados são piores do que não tratando.

Mas isso não significa que você nunca tenha que aprender a fazer um eficiente tratamento de exceção.

Quando uma exceção é lançada ela contém várias informações importantes que lhe servem como base para solucionar o problema.
O tipo da exceção já pode lhe fornecer algumas dicas da causa do problema, por exemplo:

ArgumentNullException: Essa exceção é disparada quando um método recebe um argumento com um valor nulo.
DivideByZeroException: Essa exceção é disparada quando é realizada uma divisão por zero.
InvalidCastException: Exceção disparada quando não foi possível realizar a conversão do objeto.

Para quem trabalha com aplicações em camadas, o tipo da exceção pode especificar exatamente em qual camada foi disparada a exceção, por exemplo: System.Data.SqlCliente.SqlException só deve ser disparada na camada de acesso a dados.

A mensagem contida na exceção também fornece informações importantes para a resolução do problema, como exemplo, a mensagem “Login failed for user xyz” na exceção System.Data.SqlCliente.SqlException indica que não foi possível conectar no banco de dados com o usuário xyz. Já a mensagem “Value cannot be null. Parameter name: myObject” contida na exceção ArgumentNullException indica que ao chamar um método que contém o argumento myObject esta sendo passado valor nulo.

Por enquanto tudo o que foi escrito parece óbvio, mas acredite, perdi a conta de quantas vezes me deparei com programador sofrendo por horas para tentar resolver um problema e quando perguntava qual era a mensagem de erro o “campeão” não sabia dizer.

Outro recurso muito útil que uma exceção fornece para lhe ajudar a resolver o problema é o StackTrace. O StackTrace contém o rastro da exceção dentro da aplicação desde o momento em que foi gerada até o ponto em que foi tratada, exemplo:

************** Exception Text **************
System.ArgumentNullException: Value cannot be null.
Parameter name: view
at Cronos.CreationDbFiles.Application.ConnectToServerPresenter..ctor(IConnectToServerView view) in C:\Projetos\Cronos.CreationDatabaseFiles\src\Cronos.CreationDbFiles\Application\ConnectToServerPresenter.cs:line 22
at Cronos.CreationDbFiles.frmConnectServer.frmConnectServer_Shown(Object sender, EventArgs e) in C:\Projetos\Cronos.CreationDatabaseFiles\src\Cronos.CreationDbFiles\frmConnectServer.cs:line 133
at System.Windows.Forms.Form.OnShown(EventArgs e)
at System.Windows.Forms.Form.CallShownEvent()
at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
at System.Threading.ExecutionContext.runTryCode(Object userData)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
at System.Windows.Forms.Control.InvokeMarshaledCallbacks()

No StackTrace acima ele mostra claramente em que classe, método, e linha foi disparado o erro, neste caso em específico ocorreu na linha 22 do arquivo ConnectToServerPresenter.cs. Repare que o StackTrance contém informações inclusive das classes do .Net Framework como por exemplo at System.Windows.Forms.Form.OnShown(EventArgs e). Isso é muito útil para identificar até que ponto a exceção foi trafegada dentro das classes da aplicação ou do .Net Framework.

Por tanto, antes de ficar tentando debugar a aplicação para identificar o ponto onde ocorreu o erro procure verificar o StackTrace ele pode te ajudar com essa tarefa.

Mas precisamos entender que algumas informações só são úteis para equipe de desenvolvimento / suporte não para o usuário. Exibir mensagens de erros do tipo “Login failed for user xyz” ou “Attempted to divide by zero” em uma tela amarela, não ajuda ao usuário a identificar o que ele deve fazer para resolver esse problema.

Bom, é ai que começa a surgir os grandes problemas, quando o “campeão” fica sabendo que não é agradável para o usuário se deparar com uma tela amarela contendo mensagens que não fazem sentido algum, o que ele faz? Ele escreve um código conforme abaixo:

try
{
// minha rotina
}
catch(Exception)
{
MessageBox.Show(“Não foi possível realizar a operação, favor contactar o administrador do sistema”);
}

Ou pior:

try
{
// minha rotina
}
catch
{

}

No primeiro exemplo nós continuamos exibindo uma mensagem que não é útil ao usuário, a mensagem não contém informações que ajude a resolver o problema. E se o usuário tiver a sorte de encontrar o administrador do sistema, que informação estamos dando a ele para resolver o problema?

No segundo exemplo o problema esta resolvido..... Nunca teremos o problema, a aplicação nunca dará erro. Pelo menos é isso que o programador deve estar achando quando escreve um código desses.

Não podemos ignorar a exceção devido a importância das informações que nela esta contida, mas também não podemos exibir informações ao usuário que não tem relevância ao negócio.

Abaixo contém alguns links úteis contendo melhores práticas para tratamento de exceções:

Exception Handling Best Practices in .NET


Design Guidelines for Exceptions


User-Friendly Exception Handling


Exceptions and Performance in .NET

segunda-feira, 13 de abril de 2009

DDD: Manter regras de negócio em coleções

Havendo necessidade de utilizar coleções dentro do seu domínio, você pode se deparar com alguns problemas para garantir a implementação de regras de negócio.

Vamos pegar como exemplo as salas de uma escola, dentro da sala estão contidos vários utensílios como cadeiras, carteiras e lousa, uma forma bem simples de modelar ficaria como abaixo:

public class Sala
{
private float areaLousa;
private List utensilios;

public Sala()
{
utensilios = new List();
}

public float AreaLousa
{
get { return areaLousa; }
set { areaLousa = value; }
}

public List Utensilios
{
get { return utensilios; }
set { utensilios = value; }
}
}

public abstract class Utensilio
{
private int id;

public int Id
{
get { return id; }
set { id = value; }
}
}

public class Cadeira : Utensilio
{
private bool almofadada;

public bool Almofadada
{
get { return almofadada; }
set { almofadada = value; }
}
}

public class Lousa : Utensilio
{
private float altura;
private float largura;

public float Altura
{
get { return altura; }
set { altura = value; }
}

public float Largura
{
get { return largura; }
set { largura = value; }
}

public float Area
{
get { return altura * largura; }
}
}

Para adicionar um utensílio a sala, faríamos da seguinte forma:

Lousa lousa = new Lousa();
lousa.Altura = 10;
lousa.Largura = 20;
lousa.Id = 1;

Sala sala = new Sala();
sala.Utensilios.Add(lousa);

Mas vamos começar a aplicar algumas regras simples, como exemplo eu irei aplicar regras somente ao utensílio do tipo lousa:

  • A sala só pode ter uma lousa;
  • A lousa tem que caber na sala;

Para atender as regras acima a nossa classe Sala ficaria da seguinte forma:

public class Sala
{
private float areaLousa;
private List utensilios;
private bool temLousa;

public Sala()
{
utensilios = new List();
}

public void AdicionarUtensilio(Utensilio utensilio)
{
if (utensilio == null)
throw new ArgumentNullException("utensilio");

if (utensilio is Lousa)
{
if (temLousa)
throw new ArgumentException("Já existe uma lousa para a sala", "utensilio");

if ((utensilio as Lousa).Area > this.areaLousa)
throw new ArgumentException("A lousa não cabe na sala", "utensilio");

temLousa = true;
}

utensilios.Add(utensilio);
}

public float AreaLousa
{
get { return areaLousa; }
set { areaLousa = value; }
}

public List Utensilios
{
get { return utensilios; }
set { utensilios = value; }
}

public bool TemLousa
{
get { return temLousa; }
}
}

Agora para adicionar um utensílio a sala, temos que realizar o seguinte procedimento:

Lousa lousa = new Lousa();
lousa.Altura = 10;
lousa.Largura = 20;
lousa.Id = 1;

Cadeira cadeira = new Cadeira();
cadeira.Almofadada = false;

Sala sala = new Sala();
sala.AreaLousa = 250;
sala.AdicionarUtensilio(lousa);
sala.AdicionarUtensilio(cadeira);

Mas temos uma brecha no nosso código, a propriedade “Utensílios” permite que criemos outra lista de utensílios e substitua ao da classe, por isso a tornaremos somente leitura:

public List Utensilios
{
get { return utensilios; }
}

Ainda temos brecha, podemos adicionar um Utensílio na coleção como fizemos no primeiro exemplo sem passar pelo método AdicionarUtensilio e com isso permitindo que sejam burladas as regras para adicionar utensílios.

Para resolver esse problema, temos que fazer com que o atributo “utensilios” também seja exposto somente como readonly, bloqueando o atributo de receber valores sem que seja pelo método AdicionarUtensilio, para que isso seja garantido a propriedade ficará da seguinte forma:

public ReadOnlyCollection Utensilios
{
get { return utensilios.AsReadOnly(); }
}

Desta forma, o método Add da coleção Utensílios não estará disponível para classes externas, tornando o método AdicionarUtensilios() a única forma de adicionar um Utensílio a classe Sala.

sábado, 11 de abril de 2009

Tirando um print screen da tela.

Olá pessoal,

Abaixo vai um exemplo de como obter o preent screen da tela do usuário. Basta copiar o método abaixo no seu formulário windows e executar quando precisar
.


Referências:

http://social.msdn.microsoft.com/Forums/pt-BR/vsvbasicpt/thread/b82cc605-3ec4-48ba-b865-df39113f9d25/

Jeff Atwood

SendKeys (Class)


private void GetScreenSnapshot(bool activeWindowOnly)
{
if (activeWindowOnly)
{
SendKeys.SendWait("%{PRTSC}");
Image img = (Image)Clipboard.GetDataObject().GetData(DataFormats.Bitmap);
img.Save(@"C:\telaAtiva.jpg");
}
else
{
Rectangle rectangle = Screen.PrimaryScreen.Bounds;
Bitmap bitmap = new Bitmap(rectangle.Right, rectangle.Bottom, PixelFormat.Format32bppArgb);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
bitmap.Save(@"C:\telaCompleta.jpg", ImageFormat.Jpeg);
}
}


Considerações:

  • Eu não consegui realizar o print de aplicações console, somente de aplicações windows forms;
  • É necessário tomar muito cuidado onde será utilizado esse tipo de procedimento, pois o usuário pode considerar que sua privacidade esta sendo violada.

Valeu pela ajuda Adnilson.

terça-feira, 7 de abril de 2009

DDD: DTO vs Entity

Utilizando DDD em meus projetos, fiquei tão satisfeito com os resultados que resolvi compartilhar a experiência aqui no meu blog.

O primeiro paradigma que eu tive que me defrontar foi em relação ao “Entity”. Em outros sistemas, eu costumava utilizar uma classe exatamente como uma estrutura de tabela do banco de dados (http://martinfowler.com/eaaCatalog/associationTableMapping.html) e, em uma classe separada, colocava as regras de negócio. Ex.: Cliente (somente com Gets e Sets) e BLCliente (com o CRUD + Regras de Negócio, por exemplo calcular crédito). Também é conhecida como “Arquitetura BOLOVO”.

Quando eu comecei a ler sobre os princípios no qual o DDD foi concebido, me vieram à mente as aulas básicas de OO que você aprende na faculdade. Imagine que você tenha uma classe Carro, nela contém as propriedades referentes à quantidade de portas, cor, placa, etc. Você também colocaria o método andar() certo? Mas no modelo que eu utilizava se em vez da classe Cliente fosse a classe Carro, o modelo ficaria da seguinte forma: Carro (Gets e Sets) BLCarro (método andar()). Meio estranho isso você não concorda?

Você não precisa de um outro objeto para fazer o seu carro andar, se você simplesmente ligar o seu carro e acelerar o carro vai andar.

Se você reparar com cuidado o exemplo que eu dei logo no começo do tópico em relação a classe Cliente, você vai reparar que existe um procedimento calcular crédito dentro da classe BLCliente. No modelo que eu trabalhava se precisa-se saber o crédito de um cliente provavelmente este método estaria dentro da classe BLCliente, mas será que, como exemplo de cartão de crédito, o cliente sabe o quanto ele tem de crédito? Se você analisar no mundo real, o cliente geralmente só conhece ou precisa saber o seu crédito quando efetua uma compra ou quando pede de alguma forma o extrato para a operadora de cartão, certo? Por mais organizado e controlado que o cliente possa ser a operadora de cartão sempre deverá ter disponíveis meios para informar o cliente do seu crédito sempre que ele precisar.

Essa é uma outra característica marcante do DDD, as classes e métodos têm que estar o mais relacionado possível com a linguagem do negócio e para isso o DDD utiliza o Ubiquitous Language, fazer com que o sistema e profissionais com ele envolvidos falem a língua do cliente.

Bom, depois de resolver o problema das minhas classes anêmicas comecei a me deparar com o problema da persistência. As minhas BL’s eram responsáveis em chamar as minhas DAO’s para persistir os meus DTO’s, como fazer isso no DDD? Poderia fazer com que a minha entidade se persistisse no banco de dados, que tal?


Existe um padrão muito conhecido chamado Active Record que trata justamente da entidade de negócio se persistindo no banco de dados, eu não tenho nada contra quem o utiliza mas eu particularmente não costumo usar. Um cliente não se “insere” em algum lugar, certo? Um cliente não se “salva”, só Jesus salva, correto? Por esses motivos não estava querendo colocar essa responsabilidade na própria entidade de negócio. Eu resolvido esse problema utilizando um padrão também abordado no DDD chamado Repository Pattern, através dele é possível tirar a responsabilidade de persistência das entidades de negócio.

Mesmo depois de “organizar a casa“ em relação as minhas entidades de negócio, não significa que o padrão DTO não é útil, na verdade eu continuo utilizando o DTO, mas exercendo o papel no qual ele foi concebido.

No projeto que eu utilizei DDD a interface foi desenvolvida com o front-end em Adobe Flex e ela contém as classes específicas para comunicação entre back-end em .Net e o front-end. Ex.: Se a minha entidade de negócio Cliente tivesse um método ObterCredito() com todo o processo de negócio para obter o crédito do cliente, o meu DTO Cliente já teria a propriedade Credito, portanto, o meu front-end já teria essa informação calculada.

Nos próximos posts eu estarei publicando trechos de um projeto que estou criando somente para fins de estudo do DDD e estarei discutindo passo-a-passo os conceitos da disciplina aplicada no projeto, mas já espero com esse tópico aguçar a sua vontade de conhecer mais a fundo o DDD.

Algumas fontes de estudo:
http://domaindrivendesign.org/
http://www.infoq.com/minibooks/domain-driven-design-quickly
http://weblogs.asp.net/arturtrosin/archive/2009/02/09/domain-driven-design-learning.aspx

segunda-feira, 6 de abril de 2009

Cronos - Creation database files (new version)


Já está disponível a nova versão Creation database files (aplicativo que gera scripts de dados sql) no Code MSDN.
As novas funcionalidades são:

  • Geração de multiplos arquivos com quantidade de registros pré-definidas (Sugestão dada pelo Adnilson);
  • Persistência de conexão do banco de dados (favoritos);
  • Usabilidade melhorada;

Agora além da aplicação também esta disponível o código fonte.

Peço que vocês postem aqui no blog os seus comentários e sugestões sobre a aplicação para que possamos melhorá-la e assim se tornar uma ferramenta mais eficiente para o nosso cotidiano.

https://code.msdn.microsoft.com/Release/ProjectReleases.aspx?ProjectName=CronosCreationDbFile&ReleaseId=2477