Árvore de páginas

Framework de DI

Existem muitos frameworks de injeção de dependência que automatizam a resolução de dependências. Eles podem criar objetos com todas as dependências (e dependências de dependências recursivamente). Então, basta escrever sua classe com padrões de injeção de construtor e/ou propriedades que o framework de DI faz todo resto!

No TNF usamos a estrutura do Castle Windsor para Injeção de Dependência. 

Para suporte a injeção de dependência instale o Tnf via nuget em nosso package source: https://www.myget.org/F/tnf/api/v3/index.json

Abaixo temos um exemplo de utilização do Castle Windsor para registro da dependência fazendo seu uso através de injeção via construtor e utilizando o container para resolver a dependência manualmente.

Exemplo manual
public class PersonAppService
{
    private IPersonRepository _personRepository;

    public PersonAppService(IPersonRepository personRepository)
    {
        _personRepository = personRepository;
    }

    public void CreatePerson(string name, int age)
    {
        var person = new Person { Name = name, Age = age };
        _personRepository.Insert(person);
    }
}

var container = new WindsorContainer();
container.Register(
        Component.For<IPersonRepository>().ImplementedBy<PersonRepository>().LifestyleTransient(),
        Component.For<IPersonAppService>().ImplementedBy<PersonAppService>().LifestyleTransient()
    );

var personService = container.Resolve<IPersonAppService>();
personService.CreatePerson("Yunus Emre", 19);

O TNF quase torna invisível o uso da estrutura de injeção de dependência ao escrever sua aplicação seguindo as práticas recomendadas e algumas convenções.

Registro de dependências por convenções

O TNF registra automaticamente todos os Repositórios (IRepository), Serviços de Domínio (IDomainService), Serviços de Aplicação (IApplicationService), Controladores MVC (TnfController) por convenção.

Por exemplo, você pode ter uma interface IPersonAppService e uma classe PersonAppService que implementa:

Conveções para serviços de aplicação
using Tnf.Dependency;
 
public interface IPersonAppService : IApplicationService
{
    //...
}

public class PersonAppService : IPersonAppService
{
    //...
}

A injeção através de convenção acontece pela herança da interface IApplicationService. Essa dependência é registrada como transient (instância criada por uso).

Quando você injeta (usando a injeção do construtor) IPersonAppService para uma classe, um objeto PersonAppService é criado e passado para o construtor automaticamente.

O registro de dependência por convenções se dá pela configuração do módulo, onde no método Initialize conterá o seguinte trecho de código:

Configuração de módulo
public class AppModule : TnfModule
{
    public override void Initialize()
    {
        IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
    }
}

Para mais detalhes sobre criação e configuração de módulos no TNF.

Helper Interfaces

Você pode registrar uma classe (instancia) fazendo o uso de convenções através da herança das interfaces ITransientDependency e ISingletonDependency:

Injeção via convenção
public interface IPersonManager
{
    //...
}

public class MyPersonManager : IPersonManager, ISingletonDependency
{
    //...
}

A interface ITransientDependency registra os objetos no container como transientes (que poderiamos definir como instancia por utilização, pois é criada e finalizada ao final do processo).

A interface ISingletonDependency registra os objetos no container como singletons tendo apenas uma instancia em todo ciclo de vida da sua aplicação.

Registro de tipos de forma direta em módulos

Quando criamos uma módulo no TNF temos disponível o objeto IocManager para registrar as dependências daquele assembly. 

No método Initialize de nosso módulo podemos fazer o registro da depedência:

Usando IocManager

No exemplo a seguir registramos um serviço de forma transiente passando seu enum com o Life Scope desejado.

Registrando manualmente dependências
IocManager.Register<IMyService, MyService>(DependencyLifeStyle.Transient);

Usando o Castle Windsor API

Voce pode usar a propriedade IIocManager.IocContainer para acessar o Castle Windsor Container e registrar as dependências:

Registrando manualmente pelo Windsor API
IocManager.IocContainer.Register(Classes.FromThisAssembly().BasedOn<IMySpecialInterface>().LifestylePerThread().WithServiceSelf());

Resolvendo dependências

Toda classe registrada no injetor de DI pode ser resolvida automaticamente através da injeção pelo construtor ou usando o objeto de IocManager do TNF:

Singleton IocManager
IMyService instance = IocManager.Resolve<IMyService>();

Existem cenários onde é necessário criar uma instancia e após seu uso realizar sua liberação. Para esses casos o IocManager possui métodos de extensão que possibilitam a criação e a liberação desses recursos:

Injeção do IIocResolver
public class MySampleClass : ITransientDependency
{
    private readonly IIocResolver _iocResolver;

    public MySampleClass(IIocResolver iocResolver)
    {
        _iocResolver = iocResolver;
    }

    public void DoIt()
    {
        // Resolving, using and releasing manually
        var personService1 = _iocResolver.Resolve<PersonAppService>();
        personService1.CreatePerson(new CreatePersonInput { Name = "Yunus", Surname = "Emre" });
        _iocResolver.Release(personService1);

        // Resolving and using in a safe way
        using (var personService2 = _iocResolver.ResolveAsDisposable<PersonAppService>())
        {
            personService2.Object.CreatePerson(new CreatePersonInput { Name = "Yunus", Surname = "Emre" });
        }
    }
}

No cenário acima foi realizada a injeção do objeto IIocResolver para resolver o serviço e após finalizar sua instancia, usando manualmente o método Release e também utilizando a estrutura de using do C#

O mesmo cenário pode ser criado usando diretamente o objeto IocManager como no exemplo abaixo:

Resolvendo dependências manualmente
using Tnf.Dependency;

public class MySampleClass : ITransientDependency
{
    public void DoIt()
    {
        // Resolving, using and releasing manually
        var personService1 = IocManager.Instance.Resolve<PersonAppService>();
        personService1.CreatePerson(new CreatePersonInput { Name = "Yunus", Surname = "Emre" });
        _iocResolver.Release(personService1);

        // Resolving and using in a safe way
        using (var personService2 = IocManager.Instance.ResolveAsDisposable<PersonAppService>())
        {
            personService2.Object.CreatePerson(new CreatePersonInput { Name = "Yunus", Surname = "Emre" });
        }
    }
}

Note que o IocManager foi acessado desta forma neste exemplo: IocManager.Instance. Isso se dá porque este objeto implementa o pattern Singleton e está acessivel em qualquer ponto de sua aplicação.

IShouldInitialize interface

As vezes precisamos executar determinada tarefa (ação) ao criar um objeto.

 A interface IShouldInitialize tem um método Initialize() que se implementado será chamado automaticamente após a criação do objeto (antes de ser usado).

IShouldInitialize.cs
using Tnf;
 
public interface IMyService : IShouldInitialize
{
}

public class MyService : IMyService
{
	public void Initialize()
	{
		// After initialization
	}
}
  • Sem rótulos