Localizar Enumerações

2010-07-05

Continuando no desafio de localizar a minha aplicação, estou agora a atacar as enumerações. Esta também é uma óptima oportunidade de efectuar algum refactoring às classes e estruturas existentes.

Parte das enumerações usadas são automaticamente escritas com uma estrutura que era óptima.. até agora. A aplicação é N-layer, dividido em camadas lógicas. Por enquanto, e porque ainda não foi necessário mais, mantém-se monotier. As camadas lógicas neste caso são UI, BLL (Business Lógic Layer) e DAL (Data Access Layer), mais os repositórios de dados. Comum a todas as camadas há duas bibliotecas – MAAPPFramework que é um conjuntos de classes base, interfaces base, e muitos helpers comuns a várias aplicações (O MAAPP significa Miguel Alho Application : D ) e a camada de BO (Business Objects) que inclui as classes conceptuais e DTOs (Data Transfer Objects) do domínio. Os objectos nesta biblioteca são “dummy objects”, no sentido que não tem funcionalidade (senão algumas ordenações e extensões). Apenas suportam dados e estão devidamente decoradas para serem serializadas. Esta biblioteca é transformada em dll e utilizada por todas as camadas. Esta arquitectura está muito bem exemplificado pelo Imar Spaanjars nos seus artigos Building Layered Web Applications with Microsoft ASP.NET 2.0.

As enumerações que eu tinha incluíam sempre um valor numérico definido explicitamente (evitando súbitas alterações de referências para com a base de dados numa nova geração de código) e uma descrição com texto legível, em vez de um texto colado em CamelCase. um exemplo:

A descrição escrita como atributo, recorrendo ao System.ComponentModel.DescriptionAttribute permite em runtime ter uma versão legível do nome da enumeração, óptima para apresentação na UI, em vez do tipo valor devolvido pelo método ToString(). No exemplo em cima, O caso do “Outro” demonstra a possível diferença. Para obter os valores, na minha framework tenho um método numa helper class que permite obter o valor da descrição, bem como uma extension method para ajudar:

Considerando que o código é gerado a partir de um modelo que controlo, é uma óptima solução, desde que localizado a uma definição linguística. Seria possível criar múltiplos atributos (costumizados) para suportar descrições localizadas, por exemplo, mas a manutenção e separação não me parece optimizado. Procurei outra solução que me permitiria utilizar ficheiros de recursos (.resx).

Uma das técnicas que encontrei parece simples e óptima. A descrição passa a ser definida no ficheiro de recursos, e a chave é o nome qualificado da enumeração. Optei pelo nome completo porque é me fácil de gerar com T4. Assim, a minha enumeração fica simplificada:

e o meu ficheiro .resx terá um conjunto de chaves valores qualificados, que pode ser localizado na versão do ficheiro para outra língua:

Para obter o valor da chave, a operação é ligeiramente diferente:

Neste exemplo, tipo.GetType() devolve Namespace.BO.EnumTipoContacto, permitindo construir a chave. é necessário ter em atenção que linha

obriga-me a que a assemblagem gerada seja o Namespace.BO.dll e o ficheiro de recursos é o Enum.resx ou Enum.xx.resx onde xx representa o código cultural (“es”, por exemplo). Novamente, porque o meu código é em boa parte autogerado, tenho controlo sobre esse formato.

Outro método muito útil, para usar como DataSource para preencher DropDownLists e controlos do género é a seguinte:

Semelhante à anterior, permite obter uma lista de KeyValuePairs aceites como DataSource dos controlos.

Este foi, numa fase inicial, o tipo de estrutura que mais me preocupava, no sentido que os atributos poderiam complicar a globalização, e obrigar a um refactoring interno profundo. Felizmente, um simples workaround e refactoring evitou grandes mexidas. Também, porque nem os nomes dos métodos (nomeadamente o GetDDLList() ) nem os tipos de dados foram alterados, a camada superior continua a funcionar sem ter sido afectado.