Есть ли лучший способ сделать 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 довольно дорого, поэтому моя проблема в том, что я хочу решить две вещи:

  1. Я хочу сделать реализацию IPeopleImporter "подключаемой", чтобы я мог IOC в интерфейсе в контроллер

  2. У меня много действий, поэтому я не хочу затрат на инициализацию 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*/)
    {
        ...
    }
   }
Другие вопросы по тегам