wc_logo[1]

Injeção de dependência com Castle.Windsor


E ai galera, tudo bom?

Eu sou um leitor do excelente blog do @elemarjr(link), esse post é uma alternativa que prefiro a um framework de injeção de dependência, o meu amigo Elemar Jr fez um artigo explicando como fazer isso usando o framework Ninject, esse foi o primeiro framework que tive contanto, muito bom por sinal, mas felizmente conheci um bem legal, é o Castle.Windsor, de inicio o que mais me atraiu nele foi a configuração que você poderia fazer e posteriormente foi a performace que ele proporciona se comparado ao Ninject.

PS: Outra coisa que também me deixa bastante contente com o Windsor é que o cara que ta por de tras dele é o Ayende Rahien.

Dependencias

PM> Install-Package Castle.Windsor

Dependencias Windsor

Dependências Windsor

Começando com Castle.Windsor

Para fins de demonstração vou mostrar cinco exemplos de uso do mesmo:

  1. Resolução unitaria, IFoo to Foo.
  2. Resolução de todas, IFoo to Foo ou Foo2.
  3. Resolução Genérica.
  4. Resolução Genérica(sim repetido, vocês vão entender quando verem o código).
  5. Resolução por dependência(no construtor).

Sem mais delongas o código:

[csharp]
using Castle.Windsor;
using Castle.Windsor.Installer;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Windsor
{
[TestClass]
public class Teste
{
private WindsorContainer container;

#region Initialize and CleanUp
[TestInitialize]
public void Initialize()
{
container = new WindsorContainer();
container.Install(FromAssembly.This());
}

[TestCleanup]
public void CleanUp()
{
container.Dispose();
}
#endregion

[TestMethod]
public void ResolucaoUnitaria()
{
var somadorDecimal = container.Resolve<ISomador>();
var dois = somadorDecimal.UmMaisUm();

Assert.AreEqual(2, dois);
}

[TestMethod]
public void ResulucaoDeTodasInterfaces()
{
var somadorBinario = container.ResolveAll<ISomador>()[1];
var dois = somadorBinario.UmMaisUm();

Assert.AreEqual(10, dois);
}

[TestMethod]
public void ResolucaoGenerica()
{
var generico = container.Resolve<IGenerico<SomadorBinario>>();
var deQuemEuSou = generico.DeQuemEuSou();

Assert.AreEqual("SomadorBinario", deQuemEuSou);
}

[TestMethod]
public void ResolucaoGenerica2()
{
var generico = container.Resolve<IGenerico<SomadorDecimal>>();
var deQuemEuSou = generico.DeQuemEuSou();

Assert.AreEqual("SomadorDecimal", deQuemEuSou);
}

[TestMethod]
public void ResolucaoPorConstrutor()
{
var generico = container.Resolve<ClasseDependenteDeISomador>();
var umMaisUm = generico.ExecutaUmMaisUm();

Assert.AreEqual("2", umMaisUm);
}
}
}
[/csharp]

Como vocês podem ver tenho 5 testes cada um testando uma funcionalidade em particular.

  • O primeiro teste ele resolve uma implementação unitariamente eu simplesmente digo, “ei me da uma implementação dessa interface”, e é isso que ele me da.
  • No segundo teste eu tenho o cenário que tenho varias implementações para uma mesma interface, ele também sabe resolver isso usando o ResolveAll, onde ele vai me retornar um Array com todas as soluções que ele encontro
  • O terceiro e quarto são bem parecidos, o que acontece nesse caso é que eu tenho uma interface que é genérica, e eu tenho a implementação dessa interface genérica também, ou seja, no meu código não tem nenhuma classe que implemente a interface para o tipo especifico, um exemplo pratico disso é um repositório.
  • No ultimo caso eu tenho uma classe que em seu construtor depende de uma interface, e ele sabe injetar essa minha dependência nela!

La no inicio do código, no TestInitialize eu tenho a inicialização do Windsor, que por sinal é bem simples, eu simplesmente crio uma instancia da classe WindsorContainer e digo para ele instalar a partir do assembly atual.

Como a mágica acontece

Windsor é magica, é lindo :D.

Para que tudo isso aconteça baste eu cria uma implementação de IWindsorInstaller, como a seguinte:

[csharp]
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;

namespace Windsor
{
public class SampleInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
var allInterfaces = Classes.FromThisAssembly().
Pick().
WithService.AllInterfaces().
LifestyleTransient();

container.Register(allInterfaces);
}
}
}
[/csharp]

Usando as classes de auxilio dele eu passo uma regra de registro, que é a minha variável allInterfaces, como sua interface fluente é fácil configurar. Quando eu digo Classes From This Assembly Pick estou pegando qualquer tipo para sua solução, e informo qual serviço de injeção ele vai usar, nesse caso digo que para todas as implementações das interfaces e por ultimo e não menos importante informo que quero uma instancia transiente(poderia ser singleton).

Abaixo segue as classes e interfaces que usei no exemplo.

[csharp]
namespace Windsor
{
public interface ISomador
{
int UmMaisUm();
}

public class SomadorDecimal : ISomador
{
#region ISomador Members

public int UmMaisUm()
{
return 2;
}

#endregion
}

public class SomadorBinario : ISomador
{
#region ISomador Members

public int UmMaisUm()
{
return 10;
}

#endregion
}

public interface IGenerico<out T>
{
string DeQuemEuSou();
}

public class SouDeString<T> : IGenerico<T>
{
public string DeQuemEuSou()
{
return typeof(T).Name;
}
}

public class ClasseDependenteDeISomador
{
private readonly ISomador somadorBinario;

public ClasseDependenteDeISomador(ISomador somadorBinario)
{
this.somadorBinario = somadorBinario;
}

public string ExecutaUmMaisUm()
{
return somadorBinario.UmMaisUm().ToString();
}
}
}
[/csharp]

Para saber mais

Documentação do Windsor

Fonte: https://github.com/AlbertoMonteiro/WindsorStudies


sobre Alberto Monteiro

Desenvolvedor no Grupo Fortes, cuja principal área de conhecimento são em tecnologias Microsoft, como Windows Forms / Services, WPF, ASP.NET(MVC/WEB API), Windows Phone, EF. Gosta de sopa de letrinhas(SOLID, DDD, TDD, BDD, IoC, SoC, UoW), possui aplicações de Windows Phone publicadas no marketplace, já contribuiu no jQuery UI. Atualmente trabalha com ASP.NET MVC / Web API, Windows Azure, Amazon AWS, jQuery/UI, Knockout, EF, Ninject, AutoMapper, Restfulie, SignalR, KendoUI.