Есть ли лучший способ сделать IOC в asp.net-mvc?
У меня есть сайт asp.net-mvc, и я использую LinFu для выполнения IOC. Я столкнулся с проблемой, когда у ряда действий есть зависимость, которую я хочу внедрить в контроллер, но я хочу инициализировать зависимость, только если я вызываю действие, которое зависит от него.
поэтому в моем контроллере у меня есть этот код в моем контроллере:
public PersonController
{
private IPeopleImporter _peopleImporter;
public override void Initialize(LinFu.IoC.Interfaces.IServiceContainer source)
{
_peopleImporter= source.GetService<IPeopleImporter>();
base.Initialize(source);
}
public JsonResult GetDetails(int id)
{
var p = _peopleImporter.Get(id);
var personDetails = new {p.Id, p.FirstName, p.LastName, StandardId = p.StandardIdLogin, p.PersonNumber};
return Json(personDetails);
}
}
инициализировать PeopleImporter довольно дорого, поэтому моя проблема в том, что я хочу решить две вещи:
Я хочу сделать реализацию IPeopleImporter "подключаемой", чтобы я мог IOC в интерфейсе в контроллер
У меня много действий, поэтому я не хочу затрат на инициализацию IPeopleImporter, если пользователь никогда не вызывает конкретное действие, которое нуждается в нем. Кажется, что в приведенном выше коде я делаю эту инициализацию при каждом вызове PersonController
Мой код инициализации такой:
this.AddService(typeof(IPeopleImporter), typeof(DatabaseImporter), LifecycleType.Singleton);
Это похоже на общую модель / проблему. Есть ли рекомендуемое решение? в то же время, альтернатива (чтобы избежать снижения производительности, заключается в простом "новом" внедрении концепции внутри контроллера (и во избежание IOC)?
3 ответа
Вы можете использовать следующий трюк для вашей задачи. Вы можете внедрить не экземпляр IPeopleImporter, а фабрику этого типа:
private readonly Func<IPeopleImporter> _peopleImporterFactory;
и используйте эту фабрику в своих действиях там, где она вам нужна:
var peopleImporter = __peopleImporterFactory();
пример:
public PersonController
{
private readonly Func<IPeopleImporter> _peopleImporterFactory;
public PersonController(Func<IPeopleImporter> peopleImporterFactory)
{
_peopleImporterFactory = peopleImporterFactory;
}
public JsonResult GetDetails(int id)
{
var peopleImporter = _peopleImporterFactory();
var p = peopleImporter.Get(id);
var personDetails = new {p.Id, p.FirstName, p.LastName, StandardId = p.StandardIdLogin, p.PersonNumber};
return Json(personDetails);
}
}
Первым шагом было бы поместить это действие в отдельный контроллер. Так что вам не придется платить цену инициализации за другие действия в этом контроллере.
Затем используйте реальный шаблон IoC, а не сервисный локатор, который вы используете в настоящее время, который рассматривается как анти-шаблон. В IoC контроллер не должен ничего знать о конкретной используемой инфраструктуре DI. В IoC контроллер получает все зависимости в качестве аргументов конструктора
public PersonsController: Controller
{
private readonly IPeopleImporter _peopleImporter;
public PersonsController(IPeopleImporter peopleImporter)
{
_peopleImporter = peopleImporter;
}
public ActionResult GetDetails(int id)
{
var p = _peopleImporter.Get(id);
var personDetails = new { p.Id, p.FirstName, p.LastName, StandardId = p.StandardIdLogin, p.PersonNumber };
return Json(personDetails, JsonRequestBehavior.AllowGet);
}
}
Обычно я решаю эту проблему, вводя зависимости в качестве параметров моих действий. Обычные или дешевые зависимости могут быть введены на уровне класса, в то время как необычные или дорогие зависимости входят в качестве параметров.
Вот пример кода для Enterprise Library Unity, но вы можете его адаптировать:
public class UnityActionInvoker : ControllerActionInvoker
{
readonly IUnityContainer container;
public UnityActionInvoker(IUnityContainer container)
{
this.container = container;
}
protected override object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
{
Type parameterType = parameterDescriptor.ParameterType;
if (parameterType != typeof(string) && !parameterType.IsValueType && container.IsRegistered(parameterType))
{
return container.Resolve(parameterType).AssertNotNull();
}
return base.GetParameterValue(controllerContext, parameterDescriptor);
}
}
Вы можете зацепить это ControllerActionInvoker
в настройке Controller.ActionInvoker
в конструкторе вашего контроллера (или в общем базовом классе всех ваших контроллеров).
А вот как может выглядеть ваш контроллер:
public PersonController
{
public JsonResult GetDetails(int id, IPeopleImporter _peopleImporter /*injected*/)
{
...
}
}