dependency injection - injection - Ninject-cómo y cuándo inyectar
inyeccion de dependencias c# (2)
DE ACUERDO,
Descubrí cómo hacer lo que necesito, gracias en parte a tus comentarios, Ruben. He creado un nuevo módulo que básicamente contiene la configuración que uso en la biblioteca de clases. Dentro de este módulo puedo enlazar usando una interfaz de marcador de posición o puedo agregar un parámetro de constructor al CustomerLoader. A continuación se muestra el código de una aplicación de consola ficticia para demostrar ambas formas.
¡Esto podría ayudar a alguien más a comenzar con Ninject!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject.Core;
using Ninject.Core.Behavior;
namespace NinjectTest
{
public class Program
{
public static void Main(string[] args)
{
var kernel = new StandardKernel(new RepositoryModule(), new ProgramModule());
var loader = kernel.Get<CustomerLoader>();
loader.LoadCustomer();
Console.ReadKey();
}
}
public class ProgramModule : StandardModule
{
public override void Load()
{
// To get ninject to add the constructor parameter uncomment the line below
//Bind<CustomerLoader>().ToSelf().WithArgument("fileName", "string argument file name");
Bind<LiveFileName>().To<LiveFileName>();
}
}
public class RepositoryModule : StandardModule
{
public override void Load()
{
Bind<ICustomerRepository>().To<CustomerRepository>().Using<SingletonBehavior>();
}
}
public interface IFileNameContainer
{
string FileName { get; }
}
public class LiveFileName : IFileNameContainer
{
public string FileName
{
get { return "live file name"; }
}
}
public class CustomerLoader
{
[Inject]
public ICustomerRepository CustomerRepository { get; set; }
private string _fileName;
// To get ninject to add the constructor parameter uncomment the line below
//public CustomerLoader(string fileName)
//{
// _fileName = fileName;
//}
public CustomerLoader(IFileNameContainer fileNameContainer)
{
_fileName = fileNameContainer.FileName;
}
public void LoadCustomer()
{
Customer c = CustomerRepository.GetCustomer();
Console.WriteLine(string.Format("Name:{0}/nAge:{1}/nFile name is:{2}", c.Name, c.Age, _fileName));
}
}
public interface ICustomerRepository
{
Customer GetCustomer();
}
public class CustomerRepository : ICustomerRepository
{
public Customer GetCustomer()
{
return new Customer() { Name = "Ciaran", Age = 29 };
}
}
public class Customer
{
public string Name { get; set; }
public int Age { get; set; }
}
}
Soy un novato en lo que se refiere a DI y ninject y me cuesta un poco saber cuándo debería producirse la inyección real y cómo comenzar la encuadernación.
Ya lo estoy usando en mi aplicación web y funciona bien allí, pero ahora quiero usar la inyección en una biblioteca de clases.
Digamos que tengo una clase como esta:
public class TestClass
{
[Inject]
public IRoleRepository RoleRepository { get; set; }
[Inject]
public ISiteRepository SiteRepository { get; set; }
[Inject]
public IUserRepository UserRepository { get; set; }
private readonly string _fileName;
public TestClass(string fileName)
{
_fileName = fileName;
}
public void ImportData()
{
var user = UserRepository.GetByUserName("myname");
var role = RoleRepository.GetByRoleName("myname");
var site = SiteRepository.GetByID(15);
// Use file etc
}
}
Quiero usar la inyección de propiedades aquí porque necesito pasar un nombre de archivo en mi constructor. ¿Tengo razón al decir que si necesito pasar un parámetro de constructor, no puedo usar la inyección de constructor? Si puedo usar la inyección de constructor con parámetros adicionales, ¿cómo puedo pasar esos parámetros?
Tengo una aplicación de consola que consume por clase de prueba que tiene el siguiente aspecto:
class Program
{
static void Main(string[] args)
{
// NinjectRepositoryModule Binds my IRoleRepository etc to concrete
// types and works fine as I''m using it in my web app without any
// problems
IKernel kernel = new StandardKernel(new NinjectRepositoryModule());
var test = new TestClass("filename");
test.ImportData();
}
}
Mi problema es que cuando llamo test.ImportData()
mis repositorios son nulos, no se ha inyectado nada en ellos. He intentado crear otro módulo y llamar
Bind<TestClass>().ToSelf();
Como pensé, esto podría resolver todas las propiedades de inyección en TestClass
pero no estoy llegando a ninguna parte.
Estoy seguro de que este es un problema trivial, pero parece que no puedo encontrar la forma de hacerlo.
Estás haciendo una TestClass
versión de TestClass
, que Ninject no tiene forma de interceptar; recuerda que no hay magia como la transformación de código que intercepta tus new
TestClass
, etc.
Debería estar haciendo kernel.Get<TestClass>
lugar.
Si kernel.Inject( test);
, puede inyectarlo después de haberlo kernel.Inject( test);
con un kernel.Inject( test);
Creo que hay un artículo en la wiki que habla sobre Inject
vs Get
etc.
Tenga en cuenta que, en general, las llamadas directas a Get
o Inject
son un olor a Doing It Wrong de Service Location, que es un antipattern. En el caso de su aplicación web, NinjectHttpModule
y PageBase
son el gancho que intercepta la creación de objetos: hay interceptores / lugares lógicos similares para interceptar en otros estilos de aplicaciones.
Re your Bind<TestClass>().ToSelf()
, generalmente un StandardKernel
tiene ImplicitSelfBinding = true
que lo haría innecesario (a menos que desee influir en su Ámbito para que sea algo distinto de .InTransientScope()
).
Un último punto de estilo: - estás usando la inyección de propiedades. Rara vez hay buenas razones para esto, por lo que debería estar usando la inyección de constructor.
Y vaya a comprar Dependency Injection en .NET por @Mark Seemann , quien tiene montones de publicaciones excelentes por aquí que cubren muchas consideraciones importantes pero sutiles dentro y alrededor del área de Inyección de Dependencia.