Num dos nossos produtos “flagship”, que é composto por vários módulos, temos aquilo a que podemos chamar “namespace nightmare” se não houver documentação e o pessoal não andar atento. Basicamente cada módulo tem o seu namespace próprio e dentro de cada módulo, cada layer tem o seu namespace. A aplicação segue o padrão comum das 3 layers, pelo que o q acontece é algo do género:
Modulo1.GUI
Modulo1.BLL
Modulo1.DAL
Modulo2.GUI
Modulo2.BLL
Modulo2.DAL
Modulo3.GUI
Modulo3.BLL
Modulo3.DAL
Existem alguns interfaces comuns, que especificam as funcionalidades que todos os serviços, por exemplo, devem apresentar para haver uma certa coerência nas implementações das funçoes comuns (Save, Get, Search, Dispose, etc.) a todos os serviços. As interfaces são, obviamente, contratos.
Nota: os serviços são as classes da layer BLL, que tratam da lógica de negócio do sistema e não serviços WCF ou web services SOPA ou algo semelhante.
Sempre mantivemos isto sem referências circulares o que nos colocou alguns desafios. Nomeadamente em relação à partilha de informação e, mas “bicudo” de funcionalidades e serviços. Assim, decidimos confiar na “magia” do Reflection para ajudar nestes casos. Criamos alguns métodos auxiliares que nos permitem utilizar os serviços em qualquer ponto do sistema, mesmo quando uma referência directa não é possível.
Com este post, pretendo mostrar o que foi criado em termos genéricos e o que se pretendeu atingir.
Problema 1 – Como utilizar as funções “Search” de um serviço sempre que necessário ?
A solução é simples de encontrar e simples de implementar, desde que se tenha um conhecimento normal da framework. Assim, foi criado um método que o que faz é criar e devolver ao caller uma instância do serviço pedido.
public static IService GetServicoPeloNome(string modulo, string nomeServico)
{
// Carregar o assembly onde se encontra o serviço
System.Reflection.Assembly myAssembly = System.Reflection.Assembly.LoadFrom(string.Format(@"{0}\{1}.BLL.dll"
, Application.StartupPath
, modulo));
// Obter o tipo do serviço
Type myType = myAssembly.GetType(string.Format("{0}.BLL.{1}", modulo, nomeServico));
// Obter e retornar a instância
return (IBusinessService)(myType.InvokeMember(nomeServico, System.Reflection.BindingFlags.CreateInstance, null, new object(), null));
}
Com este método, conseguimos obter instâncias de qualquer serviço do sistema, desde que o mesmo implemente o interface IService. Obviamente que só nos permite depois chamar os métodos que são definidos pelo Interface, mas é essa a ideia.
Obviamente, isto não é o “real martelo dourado” e não se pode tratar tudo como um prego. Daí que recentemente me surgiu a necessidade de criar um outro método, com outra finalidade. No entanto, recorri de novo ao Reflection.
Problema 2 – Como invocar, de um determinado serviço com interface conhecido, um método que não é definido pelo interface ?
Mais uma vez, a solução é simples. Neste caso obtemos uma instância do serviço recorrendo ao Activator e depois invocamos um método dessa mesma instância, retornando o resultado do mesmo. É uma solução bastante genérica e, penso eu, bastante elegante:
public static object InvokeMetodoServico(string assembly, string fullNamespace, string metodo, object[] argumentos)
{
// Carregar o assmebly que contém o serviço
System.Reflection.Assembly myAssembly = System.Reflection.Assembly.LoadFrom(assembly);
// Obter o tipo do serviço
Type servicoType = myAssembly.GetType(fullNamespace);
// Criar uma instância do serviço (todos os serviço implementam IDisposable, por isso a utilização da keyword "using"
using (IBusinessService servico = Activator.CreateInstance(servicoType) as IBusinessService)
{
// Invocar o método e devolver o resultado do mesmo
return servicoType.InvokeMember(metodo, System.Reflection.BindingFlags.InvokeMethod, null, servico, argumentos);
}
}
No entanto, existe um senão com esta abordagem. Se o desenvolvimento for feito por equipas “estanques”, digamos que quem desenvolve o módulo 1 sabe pouco ou nada sobre o módulo 2 e precisar de algo do módulo 2 e naõ existir referência directa, das duas, uma: ou existe documentação de qualidade ou tem haver muita comunicação de qualidade entre as equipas.
Se tiverem opiniões ou reparos a fazer, força nisso. Sou todo ouvidos. Quer dizer, mais ou menos…
41.215439
-8.623724