sexta-feira, 23 de outubro de 2009

Modelo anêmico realmente é ruim?

Eu sempre costumo dizer para o pessoal que se você não enxerga o problema não tem por que aplicar uma solução.

O mesmo vale para o modelo anêmico, se você não consegue enxergar os problemas dessa modelagem com certeza você não vê valor nenhum nas outras formas de modelar o domínio.

Com certeza você vai dizer que conhece aplicações desenvolvidas com modelo anêmico e que funcionam muito bem, e com certeza isso é verdade, da mesma forma que conheço aplicações procedurais que funcionam muito bem. Então o que nos motiva a não ter modelos anêmicos?

Quando você decide pelo modelo anêmico você tem que estar ciente que você esta abrindo mão de vários benefícios que o OO pode lhe proporcionar. Fowler diz o seguinte:

“The fundamental horror of this anti-pattern is that it's so contrary to the basic idea of object-oriented design; which is to combine data and process together. The anemic domain model is really just a procedural style design, exactly the kind of thing that object bigots like me (and Eric) have been fighting since our early days in Smalltalk. What's worse, many people think that anemic objects are real objects, and thus completely miss the point of what object-oriented design is all about.”

http://martinfowler.com/bliki/AnemicDomainModel.html

Ele reforça a idéia de que o modelo anêmico não é OO, podemos ver essa afirmação na prática trazendo o clássico exemplo do carro para o mundo do modelo anêmico. Quando você modela um carro geralmente você faz algo assim:


public class Carro
{
private int _posicaoAtual;

public void Andar()
{
this._posicaoAtual += 10;
}
}

Agora no mundo anêmico o carro ficaria assim:

public class Carro
{
private int _posicaoAtual;

public int PosicaoAtual
{
get{ return this._posicaoAtual;}
set{ this._posicaoAtual = value;}
}
}

public class BLCarro
{
publicvoid Andar(Carro carro)
{
carro.PosicaoAtual += 10;
}
}


Perceba que agora criamos um “andador de carro” isso faz algum sentido? Creio que não.
Mas se o modelo anêmico é um anti-padrão, por que temos o DTO (Data Transfer Object Pattern)?

Modelo anêmico não tem nada haver com DTO, eu falo que DTO é um mal necessário, ele foi criado para realizar transporte de dados, não para transporte entre as camadas lógicas como se tem feito com os modelos anêmicos.

Um fator que motiva a utilização do modelo anêmico é o acesso a dados, mas acredite esse problema tem solução, você pode utilizar o Repository ou Active Record.

Active Record with Castle
http://www.castleproject.org/activerecord/index.html

Using Repository and Unit of Work patterns with Entity Framework 4.0

http://blogs.msdn.com/adonet/archive/2009/06/16/using-repository-and-unit-of-work-patterns-with-entity-framework-4-0.aspx

NHibernate and the Unit of Work Pattern
http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/04/10/nhibernate-and-the-unit-of-work-pattern.aspx

Outro fator que confunde um pouco a cabeça do pessoal é que se tem um caminho fixo para se chegar ao banco de dados com o modelo anêmico sendo UI > BL > DL utilizando o modelo o anêmico para trafegar os dados entre a camada, com uma entidade como fica esse caminho?

Pra não fugir do costume vou soltar a frase “cada caso é um caso” você pode por exemplo utilizar um façade para ter acesso ao domínio. Ex.: UI > Façade > Domínio.

sexta-feira, 9 de outubro de 2009

Personalizando o toString em enums

Os enumerados são, sem dúvida, grandes ferramentas para auxiliar e padronizar o desenvolvimento de aplicativos. Eu, por exemplo, o considero excelente para sumir com os famosos números mágicos (magic numbers) dentro das entidades, ex:


//Antes
productionOrder.State = 3;
//Depois
productionOrder.State = ProductionOrderState.Canceled;

Porém, uma desvantagem do enum é quando você quer exibir o texto do valor contido no enum. Ex.:
ProductionOrderState productionOrderState = ProductionOrderState.Canceled;
Console.Writeline(productionOrderState);
//Print: Canceled


Como fazer para imprimir "Production Order is Canceled" no lugar de Canceled?
Abaixo vai uma dica bem simples de como fazer isso. Primeiro copie o código abaixo:
public struct Described<T> where T : struct
{

private T _value;

public Described(T value)
{
_value = value;
}

public override string ToString()
{
string text = _value.ToString();
object[] attr = typeof(T).GetField(text).GetCustomAttributes(typeof(DescriptionAttribute), false);

if (attr.Length == 1)
text = ((DescriptionAttribute)attr[0]).Description;

return text;
}

public static implicit operator Described<T>(T value)
{
return new Described<T>(value);
}

public static implicit operator T(Described<T> value)
{
return value._value;
}
}
Fonte: Post StackOverFlow (http://stackoverflow.com/questions/796607/how-do-i-override-tostring-in-c-enums)
Agora no seu enum implemente o atributo Description, do namespace System.ComponentModel em cada item do enum:
public enum ProductionOrderState
{
[Description("Production Order is Open")]
Open,
[Description("Production Order is Canceled")]
Canceled
}

Para obter a descrição do valor do enum faça o seguinte:
Described<ProductionOrderState> status = ProductionOrderState.Open;
Console.WriteLine(status);
//Print: Production Order is Open

Veja que a classe Described não influencia em nada o funcionamento do enum, veja o teste abaixo:

Described<ProductionOrderState> status = ProductionOrderState.Open;
Console.WriteLine(status);
ProductionOrderState other = status;
Console.WriteLine(other);

Mas ainda não é o suficiente certo? A descrição esta hard code como vamos fazer quando tiver que alterar a descrição? E para multi idioma como ficaria?

Para resolver esse problema crie um Resource no seu projeto e coloque nele as descrições que precisa para o seu enum.

resource

Para obter o valor do Resource nos items do enum podemos utilizar uma classe disponível no Enterprise Library (http://msdn.microsoft.com/en-us/library/cc467894.aspx), faça referência no seu projeto a dll Microsoft.Practices.EnterpriseLibrary.Configuration.Design, dentro dela você contará com o atributo SRDescription.

Troque no seu enum o attribute Description pelo SRDescription informando o nome do Resource que você criou e o nome da chave do resource que você criou, ex:
public enum ProductionOrderState
{
[SRDescription("Sts_Open", typeof(Resources))]
Open,
[SRDescription("Sts_Canceled", typeof(Resources))]
Canceled
}

Prontinho, agora temos disponível um meio prático de obter e personalizar as descrições dos nossos enums.

DDD: Como validar entidades de negócio

Eu respondi uma pergunta sobre como é realizada as validações de entidade de negócio no DDD.
No mesmo post eu coloquei um exemplo obtido no livro do Tim McCarthy.

No exemplo abaixo é gerado uma exceção caso a entidade não esteja válida.


public sealed class Address
{
private string street;
private string city;
private string state;
private string postalCode;

public Address(string street, string city, string state, string postalCode)
: this(street, city, state, postalCode, true)
{
}

private Address(string street, string city, string state, string postalCode,
bool validate)
{
this.street = street;
this.city = city;
this.state = state;
this.postalCode = postalCode;
if (validate)
{
this.Validate();
}
}

private void Validate()
{
if (string.IsNullOrEmpty(this.street)
string.IsNullOrEmpty(this.city)
string.IsNullOrEmpty(this.state)
string.IsNullOrEmpty(this.postalCode))
{
throw new InvalidOperationException("Invalid address.");
}
}
}

Já no exemplo abaixo é verificado se a lista de regras quebradas esta preenchida, caso ela esteja vazia a entidade esta ok.
public class Company : EntityBase
{
private string name;
private string abbreviation;
private Address headquartersAddress;
private List<Address> addresses;
private string phoneNumber;
private string faxNumber;
private string url;
private string remarks;

public Company()
: this(null)
{
}

public Company(object key)
: base(key)
{
this.name = string.Empty;
this.abbreviation = string.Empty;
this.headquartersAddress = null;
this.addresses = new List<Address>();
this.phoneNumber = string.Empty;
this.faxNumber = string.Empty;
this.url = string.Empty;
this.remarks = string.Empty;
}

public string Name
{
get { return this.name; }
set { this.name = value; }
}

protected override void Validate()
{
// The Company must have a name
if (!string.IsNullOrEmpty(this.name))
{
this.BrokenRules.Add(new BrokenRule("", ""));
}
}
public ReadOnlyCollection<BrokenRule> GetBrokenRules()
{
this.Validate();
return this.brokenRules.AsReadOnly
}
}

Mas os exemplos acima demonstram uma forma mais "genérica" de como realizar essa validação. Por que genérica? Por que podemos ter vários tipos de validações para uma mesma entidade dentro de processos específicos.

Imagine por exemplo que para cadastrar um cliente você não precise do CPF, mas para finalizar um processo de compra essa informação passe a ser necessário, se você chamar o método "Validate" da classe cliente com o CPF em branco ele vai dizer que esta válido, mas para o processo de compra isso não é verdade.

As validações colocadas na própria entidade de negócio, geralmente são utilizadas nos processos de CRUD para garantir que todos os campos obrigatórios estejam preenchidos e de forma adequada. Mas cada processo de negócio necessita de informações específicas de cada entidade, por tanto, ele deve se preocupar em garantir essas informações.

Na prática o que eu quero dizer com isso? Que se o processo precisa do campo CPF do cliente, o processo (ou um serviço referente ao processo) é responsável em verificar se o CPF esta preenchido e não o cliente.

Conclusão, nossos métodos referentes a processos como o de compra, por exemplo, devem fazer mais do que simplesmente persistência de banco de dados (Insert e Update) mas também
garantir com que as entidades estejam com estado adequado.

quinta-feira, 10 de setembro de 2009

DDD: Enviar e-mail, onde vai esse negócio?

Já me deparei com essa pergunta em vários lugares e por isso mereceu um post no meu blog.

A resposta a essa pergunta é: Por que você precisa enviar e-mail?
Eu sei que é muito feio responder uma pergunta com outra pergunta mas nesse caso isso se faz necessário por que ela tem relação direta na solução do problema.

Geralmente eu sugiru como solução a criação de um serviço, mas eu percebo que as pessoas ainda não sabem direito como seria esse serviço e isso acontece por que a primeira coisa que vem a mente são os serviços de domínio;

No capítulo sobre “Services” do livro Domain-Driven Design: Tackling Complexity in the Heart of Software do Evans ele define os serviços da seguinte forma:

“This pattern is focused on those SERVICES that have an important meaning in the domain in their own right, but of course SERVICES are not used only in the domain layer. It takes care to distinguish SERVICES that belong to the domain layer from those of other layers, and to factor responsibilities to keep that distinction sharp.”

Ele deixa claro que podemos ter serviços em outras camadas fora a camada do domínio, mas ele ressalta a importância de se distinguir exatamente a atividade necessária para decidir o melhor local para o serviço.

Por exemplo, se para o seu envio de e-mail tiver envolvendo regras de negócio você provavelmente terá um serviço de negócio interagindo com um serviço de infraestrutura.

No livro Evans cita como exemplo o processo de transferêcia bancária. Após a camada de aplicação solicitar para a camada de domínio a transferência e obter confirmação que o processo foi realizado com sucesso, ela invoca um serviço da camada de infraestrutura para notificar os usuários envolvidos.

Resumindo, enviar e-mail é uma atividade de infraestrutura mas o motivo, ou negócio, influência diretamente na solução adotada.

quarta-feira, 9 de setembro de 2009

Boas práticas também serve para camada de interface sabia?

Quando falamos em padrões e boas práticas é muito comum pensarmos nas camadas de persistência, negócio, infraestrutura etc. Mas será que aplicamos esses mesmos princípios na camada de interface?
Alguns exemplos simples de como deixamos essas práticas de lado quando estamos trabalhando na camada de interface:


1) Preciso popular um ComboBox com uma lista de cidades. O procedimento mais comum nesse caso é o de "arrastarmos" o controle na tela, chamamos o método apropriado para trazer as cidades e preenchemos o controle com as cidades. Só que se tivermos 10 telas que precisamos exibir um ComboBox com cidades nós iremos repetir essa operação 10 vezes. Não teremos duplicidade de código? E se a forma de popular o ComboBox com as cidade por qualquer motivo tiver que ser alterada? Quanto tempo vamos ter que desprender para fazer essa alteração?

2) Muitas vezes precisamos gerar uma quantidade de código muito grande para tratar um determinado controle (TextBox, ComboBox, Button, etc...) e todo esse código fica misturado com o código de processo da tela (Cadastrar Cliente, Cadastrar Usuário, etc...). Se isolássimos o controle como um novo controle, será que não seria mais fácil a manutenção?

Com os recursos de interface rica, estamos produzindo cada vez mais código nas interfaces para proporcionar uma melhor experiência do usuário com o nosso sistema, e com isso também temos uma maior dificuldade em gerenciar esse código todo, por tanto, é cada vez maior a necessidade de se aplicar todos esses princípios e boas práticas na camada de interface.

Você já parou para analisar quanto tempo você desprende só para construir uma interface? Você já parou para analisar o quanto das alterações solicitadas pelo usuário estão diretamente ligadas a recursos de interface?

Utilizar MVC e criar controles personalizados são excelentes ferramentas para nos ajudar a gerenciar essa complexa estrutura que é a camada de interface, pense nisso quando estiver construindo suas interfaces.

quarta-feira, 17 de junho de 2009

MVC ou MVP?

Eu conheço uma galera interessada em padrões de projeto mas como acontece com todo mundo (inclusive comigo) algumas dúvidas e confuções acabam surgindo.

Foi me passado o seguinte trecho do livro Patterns of Enterprise Application Architecture do Fowler. (Se você não leu, deveria!)

“The view is only about display of information; any changes to the information are handled by the third member of the MVC trinity: the controller. The controller takes user input, manipulates the model, and causes the view to update appropriately”

Combinado com a definição dada no livro o seguinte exemplo de MVC foi passado:

http://www.codeproject.com/KB/tips/ModelViewController.aspx?fid=1194034&select=2878520#xx2878520xx

Bom, qual o problema?
O problema é que o exemplo passado no link não é MVC, pelo menos não na versão purista, mas sim MVP.

Mas como eu sei a diferença?

Analise comigo o trecho do código no exemplo da classe CalcController:

public void OnClick( int number )
{
    view.Total = model.SetInput(number).ToString();
}

No exemplo, a controller além de atualizar a Model ainda informa a View da alteração que a Model sofreu.

Apesar do trecho citado do livro do Fowler não ser incisivo em relação a interação entre a Model e a View, no MVC a Model notifica a View através de eventos quando seu estado é alterado.

O exemplo na verdade utiliza uma versão adaptada do MVC conhecida como MVP (Model View Presenter), cuja principal diferença é a controller alterando a Model e notificando a View das alterações feitas.

Veja o exemplo apresentado no site da SUN sobre MVC:
http://java.sun.com/developer/technicalArticles/javase/mvc/

Mas existe algum problema com isso?
Tecnicamente não, os dois funcionam muito bem, o problema esta na fato de estar usando um padrão achando que esta usando outro.

Eu particularmente prefiro utilizar o MVP, principalmente quando se pensa em DDD eu procuro deixar somente código referente a regras de negócio no domínio.

Fica esse alerta pra quem esta começando a utilizar esses padrões, principalmente por que outros estão surgindo como MVVM por exemplo, já pensou se a gente não saber a diferença entre eles?

sexta-feira, 5 de junho de 2009

Definições – Assunto importante

 

Olá pessoal,
Já faz algum tempo que fico sem postar no meu blog, minha volta para o Coral Kemuel tem tomado bastante tempo :)

Se você estuda ou estudou em qualquer lugar com certeza você já usufruiu da maravilha que é pesquisar assuntos no Google.

Um professor amigo meu chamado Renato (vulgo “Presuntinho”) me dizia que a internet é uma grande fonte de informação mas não de conhecimento, pode ter certeza, isso é verdade.
Quando você resolve aproveitar conteúdo de internet para seus trabalhos, acadêmicos ou não, você deve tomar muito cuidado.

Uma situação muito comum para consultar o “pai” Google é para se pegar definições, pois bem, eu me deparei com algumas que particularmente considero bizarras. Não vou colocar QUEM escreveu, isso não é o importante, o importante é o QUE.

“O flex é um jquery com recursos de transferencia assycornima de dados, suporte a chat sem a necessidade de um código fonte pesado, inumeros controles não disponiveis em ajax, de código aberto e compativel com asp.net (e java, e php, e ruby).”

Pois é, no texto acima o cara tentou dar uma definição do adobe flex. Abaixo segue a definição dada pela própria adobe:

“O Flex é uma estrutura de código aberto altamente produtiva e gratuita para a criação e manutenção de aplicativos Web expressivos implantados de maneira consistente nos principais navegadores, desktops e sistemas operacionais.”
Fonte: http://www.adobe.com/br/products/flex/?promoid=BOZRZ

Agora vamos ver uma definição que foi dada para OOP:

“O OOP é uma técnica de programação de forma estruturada com a finalidade de reaproveitar o código existente e implementar novas classes quando necessário.”

Usar o termo “programação estruturada” na definição de programação orientada a objetos na minha opinião é um equívoco, veja o que eu peguei do wikipéia:

“(A programação estruturada) Foi a forma dominante na criação de software entre a programação linear e a programação orientada por objetos.” Fonte: http://pt.wikipedia.org/wiki/Programa%C3%A7%C3%A3o_estruturada

Agora imagina o “campeão” apresentando um trabalho na faculdade na frente “do professor” dando definição de OOP e no meio na frase ele solta “programação estruturada”. O que será que ele vai dizer?

Minha recomendação para evitar esses problemas? Leia, leia muito. Leia livros cite referências, autores …

Comunidade .Net precisamos evoluir nesse sentido leiam as discussões no GUJ sobre DDD e veja como eles se preocupam com definições.

segunda-feira, 11 de maio de 2009

DDD: Repositório Genéricos

 

A utilização de repositórios genéricos em sistemas que aplicam Domain Driven Design é uma prática que vem sendo bastante utilizada.

As vantagens e desvantagens dessa prática foram levantadas pelo Greg Young com um post em seu blog.

O que realmente gostaria de descatar do blog é sobre a relação entre o repositório e o DDD. Greg diz o seguinte.

“Looking back to [DDD, Evans] one will see that it is to represent a series of objects as if they were a collection in memory so that the domain can be freed of persistence concerns. In other words the goal is to put collection semantics on the objects in persistence.”

O repositório deve representar os dados como se estivessem em memória sem se preocupar com um armazenador de dados específico.

Quando utilizamos o repositório como um contrato entre todos os armazenadores de dados, garantimos que, independente da forma que o mesmo se utilizará para realizar a ação, os dados serão manipulados da forma esperada.

O trecho de código exemplifica exatamente a diferença entre uma ação genérica e uma específica:

Repository<T>.FindAllMatching(QueryObject o);
CustomerRepository.FindCustomerByFirstName(string);

Quanto mais genérico for uma “ação” maiores serão suas dificuldades em manter o “contrato” entre o seu domínio e armazenadores de dados.

Mas mesmo assim, Greg Young demostra como ter um bom reaproveitamento de código através de composição utilizando repositório genéricos conforme trecho abaixo:

Public class CustomerRepository {
    private Repository<Customer> internalGenericRepository;
    Public IEnumerable<Customer> GetCustomersWithFirstNameOf(string _Name) {
         internalGenericRepository.FetchByQueryObject(new CustomerFirstNameOfQuery(_Name)); //could be hql or whatever
     }
}

Este post é apenas um resumo do post do Greg das coisas que me chamaram mais a atenção, recomendo que você leia o post original e principalmente os comentários do pessoal.

segunda-feira, 4 de maio de 2009

VO (Value Object) vs DTO (Data Transfer Object)

Eu percebo que várias pessoas têm confundido o Value Objects com Data Transfer Objects, pra muita gente tudo isso quer dizer uma coisa, mas na prática não são, e isso é independente se você esta utilizando DDD.


Eu não quero ousar ficar dando definições para esses caras, por tanto, vou basear as minhas explicações nas definições do Martin Fowler:


Value Object
“A small simple object, like money or a date range, whose equality isn't based on identity.”


Um exemplo muito comum de Value Object que utilizamos no nosso dia-a-dia é a classe DateTime, perceba que a classe por si só não nos diz nada, na verdade as propriedades que a constituem é que são realmente importantes (dia, mês, ano, etc...).


Um exemplo muito comum em que se utiliza o Value Object é no caso de endereço, imagine aplicar as informações de endereço para Cliente e Fornecedor:


public class Cliente
{
        private string logradouro;
        private string bairro;
        private string cidade;
        private string estado;


        public string Logradouro
        {
                get{ return logradouro;}
                set{ logradouro = value;}
        }


        public string Bairro
        {
                get{ return bairro;}
                set{ bairro = value;}
        }


        public string Cidade
        {
                get{ return cidade;}
                set{ cidade = value;}
        }


        public string Estado
        {
                get{ return estado;}
                set{ estado = value;}
        }
}


public class Fornecedor
{
        private string logradouro;
        private string bairro;
        private string cidade;
        private string estado;

        public string Logradouro
        {
                get{ return logradouro;}
                set{ logradouro = value;}
        }

        public string Bairro
        {
                get{ return bairro;}
                set{ bairro = value;}
        }

        public string Cidade
        {
                get{ return cidade;}
                set{ cidade = value;}
        }

        public string Estado
        {
                get{ return estado;}
                set{ estado = value;}
        }
}


Agora veja uma outra forme de disponibilizar as informações de endereço para as classes:

public class Cliente
{
        private Endereco endereco;
        public Endereco Endereco
        {
                get{ return endereco;}
                set{ endereco = value;}
        }
}


public class Fornecedor
{
        private Endereco endereco;


        public Endereco Endereco
        {
                get{ return endereco;}
                set{ endereco = value;}
        }
}


public struct Endereco
{
        private string logradouro;
        private string bairro;
        private string cidade;
        private string estado;


        public string Logradouro
        {
                get{ return logradouro;}
                set{ logradouro = value;}
        }


        public string Bairro
        {
                get{ return bairro;}
                set{ bairro = value;}
        }


        public string Cidade
        {
                get{ return cidade;}
                set{ cidade = value;}
        }


        public string Estado
        {
                get{ return estado;}
                set{ estado = value;}
        }
}


Pronto, agora temos um Value Object de negócio aplicado em nosso sistema, mas existem mais detalhes que precisamos nos preocupar quando utilizar um Value Object.
Um Value Object tem características importantes que o diferenciam:


1) Construção:
No .Net geralmente o Value Object é criado como um struct, mas tome cuidado quando você estiver pensando em DDD, nem toda classe struct é um Value Object.

2) Determinar igualdade:
No mundo real quando lidamos com uma classe cliente para saber se um cliente é igual a outro podemos simplesmente comparar a o campo referente a chave primário do nosso banco de dados (ex.: cliente1.Id == cliente2.Id), isso já seria o suficiente para determinar se uma entidade é igual, mas na comparação entre value object é necessário comparar todas as propriedades que o compõe, no caso do DateTime, por exemplo, para saber se uma pessoa nasceu no mesmo dia de outra é necessário comparar o Dia, mês e ano dos objetos.

3) Contexto:
Apesar de ter usado a classe Endereço como um exemplo de Value Object de negócio, o contexto é um fator diferencial para determinar se um object deve ser tratado como um Value Object ou não, no caso dos Correios, por exemplo, cada endereço é tratado de forma única existe um identificador único para ele, pois é necessário calcular taxas como o sedex, nesse cenário o Endereço não será um Value Object e sim Entity.


Data Transfer Object
“An object that carries data between processes in order to reduce the number of method calls.”


Redução de chamadas é palavra chave na definição do DTO.
Na empresa em que trabalho nós utilizamos .Net com Adobe Flex, imagine um exemplo de uma classe simples de pedido:


namespace Domain.Entities
{
    public enum TipoEntrega
    {
        CorreioSedex,
        CorreioNormal,
        Transportadora
    }


    public class Pedido
    {
        private List<IItemPedido> items;
        private Cliente cliente;
        private Nullable<TipoEntrega> tipoEntrega;
        private Endereco destinoEntrega;
        private bool freteCalculado;
        private decimal frete;
        private decimal desconto;
        private decimal total;


        public Pedido()
            : this(null)
        {
        }


        public Pedido(Nullable<decimal> frete)
        {
            items = new List<IItemPedido>();
            if (frete != null)
            {
                this.frete = (decimal)frete;
                this.freteCalculado = true;
            }
        }


        public int AdicionarProduto(Produto produto)
        {
            return this.AdicionarProduto(produto, 1);
        }


        public int AdicionarProduto(Produto produto, int quantidade)
        {
            if (produto == null)
                throw new ArgumentNullException("produto");
            if (quantidade < 1)
                throw new ArgumentException("Não pode ser adicionar um produto cuja quantidade seja inferior a 1");
            if (items.Exists(delegate(IItemPedido item) { return item.Nome.Equals(produto.Nome); }))
                throw new InvalidOperationException("O produto já se encontra no pedido");
            IItemPedido novoItem = new ItemPedido(produto, quantidade);
            items.Add(novoItem);
            //retorna o indice do produto adicionado
            return items.Count - 1;
        }


        public ReadOnlyCollection<IItemPedido> Items
        {
            get { return items.AsReadOnly(); }
        }


        public Cliente Cliente
        {
            get { return cliente; }
            set { cliente = value; }
        }


        public Nullable<TipoEntrega> TipoEntrega
        {
            get { return tipoEntrega; }
            set { tipoEntrega = value; }
        }


        public decimal Frete
        {
            get
            {
                if (!freteCalculado)
                {
                    CalculadorFreteFactory factory = CalculadorFreteFactory.GetInstance();
                    CalculadorFrete calculador = factory.ObterCalculador(this.tipoEntrega);
                    frete = calculador.CacularFrete(destinoEntrega.Cidade);
                    freteCalculado = true;
                }
                return frete;
            }
        }


        public Endereco DestinoEntrega
        {
            get { return destinoEntrega; }
            set { destinoEntrega = value; }
        }


        public decimal Desconto
        {
            get { return desconto; }
            set { desconto = value; }
        }


        public decimal Total
        {
            get
            {
                if (total == 0)
                {
                    foreach (IItemPedido item in items)
                    {
                        total += item.ValorParcial;
                    }
                    total += Frete - desconto;
                }
                return total;
            }
        }


        private class ItemPedido : IItemPedido
        {
            private Produto produto;
            private int quantidade;
            public ItemPedido(Produto produto)
                : this(produto, default(int))
            { }


            public ItemPedido(Produto produto, int quantidade)
            {
                this.produto = produto;
                this.quantidade = quantidade;
            }


            public int Quantidade
            {
                get { return quantidade; }
                set { quantidade = value; }
            }


            public string Nome
            {
                get { return produto.Nome; }
            }


            public decimal ValorUnitario
            {
                get { return produto.ValorUnitario; }
            }


            public decimal ValorParcial
            {
                get { return produto.ValorUnitario * quantidade; }
            }
        }
    }


    public class CalculadorFreteFactory
    {
        private static CalculadorFreteFactory uniqueInstance = new CalculadorFreteFactory();


        private CalculadorFreteFactory()
        { }


        public static CalculadorFreteFactory GetInstance()
        {
            return uniqueInstance;
        }


        public CalculadorFrete ObterCalculador(Nullable<TipoEntrega> tipoEntrega)
        {
            if (tipoEntrega == null)
                throw new ArgumentNullException("tipoEntrega");
            CalculadorFrete retorno;
            if (tipoEntrega == TipoEntrega.CorreioNormal)
                retorno = new CalculadorCorreioNormal();
            else if (tipoEntrega == TipoEntrega.CorreioSedex)
                retorno = new CalculadorFreteSedex();
            else
                retorno = new CalculadorFreteTransportadora();
            return retorno;
        }
    }


    public interface IItemPedido
    {
        string Nome { get;}
        decimal ValorUnitario { get; }
        decimal ValorParcial { get; }
        int Quantidade { get; set; }
    }


    public class Produto
    {
        private string nome;
        private decimal valorUnitario;


        public string Nome
        {
            get { return nome; }
            set { nome = value; }
        }


        public decimal ValorUnitario
        {
            get { return valorUnitario; }
            set { valorUnitario = value; }
        }
    }


    public class Cliente
    {
        private int id;
        private InformacoesPessoais informacoesPessoais;
        private Endereco endereco;


        public InformacoesPessoais InformacoesPessoais
        {
            get { return informacoesPessoais; }
            set { informacoesPessoais = value; }
        }


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


        public Endereco Endereco
        {
            get { return endereco; }
            set { endereco = value; }
        }
    }


    public struct InformacoesPessoais
    {
        private string nome;
        private string sobreNome;
        private DateTime dataNascimento;


        public InformacoesPessoais(string nome)
            : this(nome, String.Empty, default(DateTime))
        { }


        public InformacoesPessoais(string nome, string sobreNome)
            : this(nome, sobreNome, default(DateTime))
        { }


        public InformacoesPessoais(string nome, string sobreNome, DateTime dataNascimento)
        {
            this.nome = nome;
            this.sobreNome = sobreNome;
            this.dataNascimento = dataNascimento;
        }


        public string Nome
        {
            get { return nome; }
            set { nome = value; }
        }


        public string SobreNome
        {
            get { return sobreNome; }
            set { sobreNome = value; }
        }


        public string NomeCompleto
        {
            get { return nome + " " + sobreNome; }
        }


        public DateTime DataNascimento
        {
            get { return dataNascimento; }
            set { dataNascimento = value; }
        }


        public int Idade
        {
            get
            {
                TimeSpan diff = DateTime.Now - dataNascimento;
                return diff.Days / 364;
            }
        }
    }


    public struct Endereco
    {
        private string logradouro;
        private string numero;
        private string bairro;
        private string cidade;
        private string estado;
        private string cep;


        public string Logradouro
        {
            get { return logradouro; }
            set { logradouro = value; }
        }


        public string Numero
        {
            get { return numero; }
            set { numero = value; }
        }


        public string Bairro
        {
            get { return bairro; }
            set { bairro = value; }
        }


        public string Cidade
        {
            get { return cidade; }
            set { cidade = value; }
        }


        public string Estado
        {
            get { return estado; }
            set { estado = value; }
        }


        public string Cep
        {
            get { return cep; }
            set { cep = value; }
        }
    }
}


namespace Domain.Services
{
    public abstract class CalculadorFrete
    {
        public abstract decimal CacularFrete(string cidadeDestino);
    }


    public class CalculadorFreteSedex : CalculadorFrete
    {
        public override decimal CacularFrete(string cidadeDestino)
        {
            if (cidadeDestino.Equals("Sorocaba"))
                return 15;
            else
                return 25;
        }
    }


    public class CalculadorCorreioNormal : CalculadorFrete
    {
        public override decimal CacularFrete(string cidadeDestino)
        {
            if (cidadeDestino.Equals("Sorocaba"))
                return 10;
            else
                return 25;
        }
    }


    public class CalculadorFreteTransportadora : CalculadorFrete
    {
        public override decimal CacularFrete(string cidadeDestino)
        {
            if (cidadeDestino.Equals("Sorocaba"))
                return 30;
            else
                return 35;
        }
    }
}


No momento de realizar a comunicação com o Adobe Flex teremos vários problemas:

  • Não podemos replicar as regras de negócio que estão contidas nelas para o Adobe Flex;
  • Existem algumas características nas classes que são complicadas de serem implementadas no Flex (Ex.: classes tipos enum e aplicação de Lazy Loading);

Nesse caso uma solução simples para realizar a comunicação seria criar uma classe DTO somente para realizar a comunicação entre o .Net e Adobe Flex:


namespace ComunicacaoAdobeFlex.ValueObjects
{
public class Pedido
{
private Cliente cliente;
private decimal frete;
private decimal desconto;
private decimal total;


public Cliente Cliente
{
get { return cliente; }
set { cliente = value; }
}


public decimal Frete
{
get { return frete; }
set { frete = value; }
}


public decimal Desconto
{
get { return desconto; }
set { desconto = value; }
}


public decimal Total
{
get { return total; }
set { total = value; }
}
}


public class Cliente
{
private string nome;
private string sobreNome;
private DateTime dataNascimento;
private int idade;


public string Nome
{
get { return nome; }
set { nome = value; }
}


public string SobreNome
{
get { return sobreNome; }
set { sobreNome = value; }
}


public DateTime DataNascimento
{
get { return dataNascimento; }
set { dataNascimento = value; }
}


public int Idade
{
get { return idade; }
set { idade = value; }
}
}
}


Perceba que no exemplo acima, além de criar uma classe mais simples, ela contém somente as informações que o Adobe Flex irá utilizar (isso foi de propósito).


Com certeza existem outras características que podem ser elencadas para diferenciar Value Object e Data Transfer Object, mas espero que as características levantadas possam iluminar o seu caminho.

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

terça-feira, 31 de março de 2009

Imprimir código fonte da aplicação

Se você precisa imprimir o código da sua aplicação (acredite, as vezes isso é necessário), abaixo vai uma dica de como fazer isso com Visual Studio.

Abra todos os arquivos que deseja imprimir no Visual Studio, em seguida vá em View > Other Windows > Macro Explorer > Samples > DevStudio6Editor > PrintAllOpenDocuments.

Até a próxima.

quinta-feira, 26 de março de 2009

Utilizando o MSBuild

Quantas vezes você já teve que colocar todo o código fonte da aplicação em um arquivo *.zip mas sem os arquivos *.dll e *.exe para que o arquivo fique mais leve, e com isso teve que remover todos manualmente das pastas bin?
Abaixo vai uma dica bem simples de como resolver esse problema.

Na pasta onde se encontra a solution crie dois arquivo *.bat: Build.bat e Clean.bat (ver imagem)

No arquivo Build.bat insira o seguinte:

%windir%\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe [mySolution].sln /t:Build
Pause

No arquivo Clean.bat insira o seguinte:

%windir%\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe [mySolution].sln /t:Clean
Pause


Agora, se alguma vez já compilou sua aplicação execute o arquivo Clean.bat, caso contrário execute o arquivo Build.bat e veja o resultado.

A dica é simples mas essas são algumas das várias funcionalidades do utilitário MSBuild. Para conhece-lo melhor:

http://blogs.msdn.com/msbuild/archive/2006/01/06/508981.aspx

segunda-feira, 16 de março de 2009

Free ebook ASP.NET MVC Framework


Scott Guthrie disponibilizou recentemente o capítulo escrito por ele no livro ASP.NET MVC 1.0.
Na verdade o capítulo se trata de um tutorial passo-a-passo da criação de um projeto utilizando o ASP.NET MVC Framework.


Vale a pena conferir.


domingo, 15 de março de 2009

Vulnerabilidades de segurança no web.config

Bryan Sulivan, gerente de projetos na SPI Dynamics, fez uma publicação sobre dicas de segurança no web.config, dicas essas que são importantes nos atentarmos no desenvolvimento de aplicações web. Abaixo vamos ter um resumão de seu artigo:

Custom Erros:

Ao desativar os erros personalizados o ASP.Net fornece ao usuário mensagem de erros detalhada, disponibilizando assim informações importantes para um hacker. Ao modificar a tag para “RemoteOnly” ASP.Net é instruído a exibir uma mensagem de erro genérica para chamadas externas, porém nas chamadas locais ele continua exibindo as mensagens de erros detalhada.

Configuração Vulnerável:
(web.config) configuration > system.web > configurationErros mode="Off"
Configuração Segura:
(web.config) configuration > system.web > configurationErros mode="RemoteOnly"

Tracing Enabled

O Tracing, sem dúvida nenhuma, é uma excelente ferramenta para realizar o debug e profiling de uma aplicação web, porém pode oferecer informações preciosas a um hacker para atacar sua aplicação caso esteja habilitado.

Configuração Vulnerável:
(web.config) configuration> system.web > trace enable="true" localOnly="falso"
Configuração Segura:
(web.config) configuration > system.web > trace enable="false" localOnly="true"

Debug Enabled

Um erro muito comum no processo de implantação de aplicações web é a publicação do sistema com o debug habilitado, além de ocasionar em problemas de performance você pode estar fornecendo informações privilegiadas as quais os clientes finais não deveriam ter acesso. Por exemplo se você não tiver desabilitado o debug e desabilitado custom erros, então qualquer mensagem de erro exibida para um usuário final incluirá não somente as informações do servidor, mensagem de erro detalhada e statck trace, mas também o próprio código fonte da página onde ocorreu o erro.

Configuração Vulnerável:
(web.config) configuration > system.web > Debug="true"
Configuração Segura:
(web.config) configuration > system.web > Debug="false"

Cookies acessíveis através de client-side script

Ao alterar a propriedade httpOnlyCookies para "true" você garante, globalmente para sua aplicação, que os cookies gerados estarão acessíveis somente no lado do servidor e que não estarão disponíveis a linguages client-side script como o Javascript.

Configuração Vulnerável:
(web.config) configuration > system.web > httpCookies httpOnlyCookies="false"
Confiuração Segura:
(web.config) configuration > system.web > httpCookies httpOnlyCookes="true"

Cookieless Session State Enabled

Na primeira versão 1.0 do ASP.Net para se manter o estado de uma sessão ela era armazenada em um cookie. Isso porém causava problemas quando o usuário não aceitava a criação de cookies. Para resolver esse problema a Microsoft adicionou o suporte a cookieless session tokens.

Embora essa solução tenha resolvido o problema da persistência de sessões, ela pode causar uma brecha de segurança muito grande já que o token gerado é adicionado a barra de endereço do navegador (ex.: http://myserver/myapplication/(1234567ABCDEFG)/default.aspx)

Configuração Vulnerável:
(web.config) configuration > system.web > cookieless="UseUri"
Configuração Segura:
(web.config) configuration > system.web > cookieless="UseCookies"

Outras opções de configuração podem ser realizadas no web.config para garantir segurança a sua aplicação, inclusive Bryan Sulivan disponibilizou a parte II de seu artigo que em breve pretendo colocar no blog.

Fonte: http://www.developerfusion.com/article/6745/top-10-application-security-vulnerabilities-in-webconfig-files-part-two/2/