Ninject - как и когда вводить
Я новичок, когда дело доходит до DI и ninject, и я немного борюсь
о том, когда произойдет фактическая инъекция, и как начать
связывание.
Я использую его уже в своем веб-приложении, и он отлично работает там,
но теперь я хочу использовать инъекции в библиотеке классов.
Скажем, у меня есть класс вроде этого:
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
}
}
Я хочу использовать инъекцию свойств здесь, потому что мне нужно передать
имя файла в моем конструкторе. Правильно ли я говорю, что если мне нужно
передать в конструкторе параметр, я не могу использовать конструктор инъекции?
Если я могу использовать инъекцию конструктора с дополнительными параметрами, как это сделать
Я передаю эти параметры в?
У меня есть консольное приложение, которое потребляет класс Test, который выглядит как
следующим образом:
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();
}
}
Моя проблема в том, что когда я вызываю test.ImportData()
, мои репозитории равны нулю - в них ничего не вводилось. Я попытался создать еще один модуль и вызвать
Bind<TestClass>().ToSelf();
поскольку я думал, что это может разрешить все свойства инъекции в TestClass
, но я никуда не буду.
Я уверен, что это тривиальная проблема, но я просто не могу понять
как это сделать.
Ответы
Ответ 1
Вы непосредственно вносите TestClass
, который у Ninject не имеет возможности перехватить - помните, что нет никакой магии, как преобразование кода, перехватывающее ваш new
и т.д.
Вместо этого вы должны делать kernel.Get<TestClass>
.
В противном случае вы можете ввести его после new
с помощью kernel.Inject( test);
Я думаю, что есть статья в вики, в которой говорится о Inject
vs Get
и т.д.
Обратите внимание, что в общем случае прямые вызовы Get
или Inject
- это неправильный запах местоположения службы, который является антипаттерном. В случае вашего веб-приложения NinjectHttpModule
и PageBase
- это крюк, который перехватывает создание объекта - существуют аналогичные перехватчики/логические места для перехвата в других стилях приложения.
Re Bind<TestClass>().ToSelf()
, обычно StandardKernel
имеет ImplicitSelfBinding = true
, что сделало бы это ненужным (если вы не хотите влиять на его область действия как нечто отличное от .InTransientScope()
).
Конечная точка стиля: - вы используете инъекцию свойств. Для этого редко бывают веские причины, поэтому вместо этого вы должны использовать инъекцию конструктора.
И действительно, купите Injection Dependency в .NET от @Mark Seemann, у которого есть стопки отличных сообщений, которые здесь охватывают множество важных, но тонкие соображения в области Инъекции зависимостей и вокруг нее.
Ответ 2
OK
Я узнал, как делать то, что мне нужно, частично благодаря вашим комментариям Рубена. Я создал новый модуль, который в основном содержит конфигурацию, которую я использую в библиотеке классов. Внутри этого модуля я могу либо Связывание с использованием интерфейса-заполнителя, либо я могу добавить параметр конструктора в CustomerLoader.
Ниже приведен код из фиктивного консольного приложения для демонстрации обоих способов.
Это может помочь кому-то начать работу с 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; }
}
}