Índice |
---|
Existem muitas estruturas/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 lida com o resto! Em uma boa aplicação, suas classes são independentes mesmo do framework DI. Haverá algumas linhas de código ou classes que interagem explicitamente com o framework DI em toda a sua aplicação. O TNF usa faz todo resto!
No TNF usamos a estrutura do Castle Windsor para Injeção de Dependência. É um dos Frameworks de DI mais maduros.
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.
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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.
O TNF registra automaticamente todos os Repositórios (IRepository), Serviços de Domínio (IDomainService), Serviços de Aplicação (IApplicationService), Controladores MVC e Controladores da Web API (TnfController) por convenção.
Por exemplo, você pode ter uma interface IPersonAppService e uma classe PersonAppService que implementa:
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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:
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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.
...
Você pode registrar uma classe especifica (instancia) fazendo o uso de convenções através da herança das interfaces ITransientDependency e ISingletonDependency:
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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.
...
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:
No exemplo a seguir registramos um serviço de forma transiente passando seu enum com o Life Scope desejado.
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
IocManager.Register<IMyService, MyService>(DependencyLifeStyle.Transient); |
Voce pode usar a propriedade IIocManager.IocContainer para acessar o Castle Windsor Container e registrar as dependências:
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
IocManager.IocContainer.Register(Classes.FromThisAssembly(). |
...
BasedOn<IMySpecialInterface>().LifestylePerThread().WithServiceSelf()); |
Toda classe registrada no injetor de DI pode ser resolvida automaticamente através da injeção pelo construtor de uma classe ou usando o objeto de IocManager do TNF:
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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 objeto de IocManager possui métodos de extensão que possibilitam a criação e a liberação desses recursos:
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
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:
Bloco de código |
---|
...
| ||||||||
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.
As vezes precisamos executar determinada tarefa (ação) ao criar um objeto.
IShouldInitialize interface
Algumas classes precisam ser inicializadas antes da primeira utilização. A interface IShouldInitialize tem um método Initialize() . Se você implementá-lo, então o método Initialize() é que se implementado será chamado automaticamente apenas após a criação do objeto (antes de ser usado).
Bloco de código | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
using Tnf;
public interface IMyService : IShouldInitialize
{
}
public class MyService : IMyService
{
public void Initialize()
{
// After initialization
}
} |