Data limite de entrega: 27 de Abril 3 de Maio
Objectivos: meta-programação -- análise e manipulação programática de código
intermédio com API de System.Reflection.Emit
.
No seguimento do Trabalho 1 desenvolvido na biblioteca Webao pretende-se
desenvolver uma nova classe WebaoDynBuilder
com o mesmo comportamento de
WebaoBuilder
, mas que pretende simplificar a definição e obter
melhor desempenho dos web access objects.
Para tal os web access objects passam a poder ser definidos por interfaces, conforme o exemplo seguinte:
Request req = new HttpRequest();
WebaoDynArtist webaoDyn =
(WebaoDynArtist) WebaoDynBuilder
.Build(typeof(WebaoDynArtist), req); |
[BaseUrl("http://ws.audioscrobbler.com/2.0/")]
[AddParameter("format", "json")]
[AddParameter("api_key", "************")]
public interface WebaoDynArtist
{
[Get("?method=artist.getinfo&artist={name}")]
[Mapping(typeof(DtoArtist), ".Artist")]
Artist GetInfo(string name);
[Get("?method=artist.search&artist={name}&page={page}")]
[Mapping(typeof(DtoSearch), ".Results.ArtistMatches.Artist")]
List<Artist> Search(string name, int page);
} |
WebaoDynBuilder
deve gerar em tempo de execução (dinamicamente)
uma implementação de uma classe que implementa a interface especificada.
ATENÇÃO a classe gerada dinamicamente:
- NÃO pode fazer operações de reflexão.
- NÃO estende de
AbstractAccessObject
.
Por exemplo, a obtenção das propriedades Results
, ArtistMatches
e Artist
que é feita para o método Search()
por via de reflexão em
AbstractAccessObject
será feita com acesso directo às respectivas propriedades
em código IL gerado dinamicamente por via de System.Reflection.Emit
.
APENAS será usada reflexão na própria classe WebaoDynBuilder
e suas auxiliares e
NÃO nas classes que este gera dinamicamente.
Todos os testes unitários elaborados no Trabalho 1 devem ter uma versão correspondente
para web access objects gerados dinamicamente com WebaoDynBuilder
.
Certifique-se que no Trabalho 1 implementou pelo menos um caso de um web access
object com um método que receba mais que um parâmetro e incluindo de tipo
valor, como o método Search()
exemplificado na introdução a este trabalho.
Caso não o tenha feito, então adicione um método nessas condições.
ATENÇÃO: A meta-programação requer abordagens diferentes no desenvolvimento de software. Repare que vai ter necessidade de fazer debug NÃO apenas do código que escreve mas também de código que foi gerado dinamicamente. Para tal, o processo de desenvolvimento de meta-programação deve ser rigoroso e cauteloso, recomendando-se que cumpra a abordagem descrita.
Como suporte ao desenvolvimento de WebaoDynBuilder
deve usar as ferramentas:
ildasm
peverify
Em cada etapa deve desenvolver em C# uma classe dummy num projecto à parte com
uma implementação semelhante àquela que pretende que seja gerada através da API
de System.Reflection.Emit
.
Compile a classe dummy e use a ferramenta ildasm
para visualizar as instruções
IL que servem de base ao que será emitido através da API de System.Reflection.Emit
.
Recomenda-se que implemente a geração dinâmica dos métodos de web access objects incrementalmente pela seguinte ordem, verificando em cada etapa que os métodos retornam o valor correcto:
- Retornar apenas o URL do pedido a realizar com base nos parâmetros que foram
passados ao método. Ou seja, para o exemplo da introdução verificar numa
primeira etapa que a instância gerada de
WebaoDynArtist
na chamada deSearch("muse", 1)
, retornahttp://ws.audioscrobbler.com/2.0/?method=artist.search&artist=muse&page=1&format=json&api_key=************
- Retornar apenas o DTO correspondente. Ou seja, para o exemplo da introdução
que a instância gerada de
WebaoDynArtist
na chamada deSearch("muse", 1)
, retorna uma instância válida deDtoSearch
- Retornar a instância de
List<Artist>
.
Exemplo da classe dummy em cada etapa:
class WebaoDummy {
public object Search(object name, int page) {
String path = "?method=artist.search&artist={name}&page={page}";
path = path.Replace("{name}", name.ToString());
path = path.Replace("{page}", page.ToString());
return path;
}
} |
class WebaoDummy {
public object Search(object name, int page) {
// 1. processamento do path...
DtoSearch dto = (DtoSearch) req.Get(path, typeof(DtoSearch));
return dto;
}
} |
class WebaoDummy {
public List<Artist> Search(object name, int page) {
// 1. processamento do path...
// 2. obter dto
return dto.Results.ArtistMatches.Artist;
}
} |
ATENÇÃO que o código que implementar no WebaoDynBuilder
não deve ser hard
coded mas sim com base na informação anotada nos respectivos custom
attributes.
Ou seja, o path
, o tipo do DTO e as propriedade acedidas são as que tiverem
especificadas nos custom attributes
Para cada uma das etapas anteriores deve realizar TODOS os passos seguintes sem saltar nenhum deles.
- a) implementar a classe dummy em c# com o código equivalente ao que pretende gerar.
- b) compilar essa classe com
csc \optimize+ \debug-
- c) visualizar com o
ildasm
as instruções IL correspondentes e implementar a geração dinâmica dessas instruções viaEmit
. ATENÇÃO esta implementação NÃO deve estar comprometida com nenhum caso concreto deArtist
,Team
, etc. - d) executar o
Build()
doWebaoDynBuilder
e gravar a dll com a classe gerada dinamicamente. - e) verificar a correcção da dll gerada através da ferramenta
peverify
. - f) executar os métodos do web access object gerado e verificar que retorna o valor esperado na respectiva etapa 1, 2 ou 3.
REPITA todos as etapas e passos para um web access object que lide com
tipos valor, e.g. DtoGeoTopTracks
.
Use sempre a ferramenta peverify
para despistar os erros/excepções ocorridas
na utilização das classes geradas.
Grave sempre as DLLs com as classes geradas pelo WebaoDynBuilder
e valide
através da ferramenta peverify
a correcção do código IL.
Adicione à solução um novo projecto de uma aplicação consola (.Net Framework) designado WebaoBench que compare o desempenho entre as implementações dos Trabalhos 1 e 2.
Na medição de desempenho não deve ser realizado IO e nem conversão de JSON em objectos.
Para tal use uma versão mock de IRequest
que tenha o menor processamento possível
na execução do método Get()
.
O método Get()
deste mock deve obter os objectos duma cache interna em memória.
Para as medições de desempenho use a abordagem apresentada nas aulas (atenção que testes de desempenho NÃO são testes unitários). Registe e comente os desempenhos obtidos entre as duas abordagens.