Как называется конструктор контроллера веб-API?

Согласно этой статье Контроллер должен иметь конструктор, который получает реализуемый интерфейс, а-ля:

public class DuckbillsController : ApiController
{
    IDuckbillRepository _platypiRepository;

    public DuckbillsController(IDuckbillRepository platypiRepository)
    {
        if (platypiRepository == null)
        {
            throw new ArgumentNullException("platypiRepository is null");
        }
        _platypiRepository = platypiRepository;
    }
}

Но как называется этот конструктор? Расчет через клиент, вызывающий метод веб-API, содержащийся в этом классе, но как ему передается тип интерфейса? Или этого не должно происходить (конструктор явно не вызывается никем/ниоткуда)?

ОБНОВИТЬ

В канонических примерах перед объявлением интерфейса указано «private readonly», но это не обязательно для его компиляции. Есть ли компиляция, я имею в виду убедительную причину, по которой я добавляю «частное только для чтения»?


person B. Clay Shannon    schedule 23.12.2013    source источник
comment
Как правило, код для явного вызова конструктора не пишется. Вам нужно подключить контейнер DI к платформе WebApi, и контейнер будет использовать отражение для создания экземпляра контроллера. Эта статья может помочь объяснить вещи.   -  person default.kramer    schedule 24.12.2013
comment
На самом деле это вполне разумный вопрос, и в предоставленном ответе просто говорится: «Посмотрите на это (сторонний инструмент)». Печальный факт заключается в том, что Microsoft очень плохо документирует это, и, к сожалению, отвечать на эти вопросы приходится сообществу.   -  person George Mauer    schedule 14.06.2016


Ответы (3)


Фабрика контроллеров создает их для вас... Вам нужно взглянуть на внедрение зависимостей.

Попробуйте Autofac, у него хорошая интеграция с MVC.

person Sam    schedule 23.12.2013
comment
Это было добавлено для DI; мы используем Виндзорский замок. - person B. Clay Shannon; 24.12.2013

Поскольку нигде нет документации по этому поводу (официальная документация просто обсуждает это с Unity). Вот как вы это делаете.

Свойство HttpConfiguration.DependencyResolver является экземпляром IDependecyResolver, который по сути является локатором сервисов (вы запрашиваете экземпляр типа, и он знает, как его создать). Я хотел бы предоставить свой собственный экземпляр контроллера.

Используйте так:

config.DependencyResolver = 
   new OverriddenWebApiDependencyResolver(config.DependencyResolver)
   .Add(typeof(ScoreboardController), () => 
                                    new ScoreboardController(Messages) 
   ); 

Реализовано так:

/// <summary>
/// The standard web api dependency resolver cannot inject dependencies into a controller 
/// use this as a simple makeshift IoC
/// </summary>
public class OverriddenWebApiDependencyResolver : WebApiOverrideDependency<IDependencyResolver >, IDependencyResolver {
    public OverriddenWebApiDependencyResolver Add(Type serviceType, Func<object> initializer) {
        provided.Add(serviceType, initializer);
        return this;
    }
    public IDependencyScope BeginScope() => new Scope(inner.BeginScope(), provided);
    public OverriddenWebApiDependencyResolver(IDependencyResolver inner) : base(inner, new Dictionary<Type, Func<object>>()) { }
    public class Scope : WebApiOverrideDependency<IDependencyScope>, IDependencyScope {
        public Scope(IDependencyScope inner, IDictionary<Type, Func<object>> provided) : base(inner, provided) { }
    }
}
public abstract class WebApiOverrideDependency<T> : IDependencyScope where T : IDependencyScope {
    public void Dispose() => inner.Dispose();
    public Object GetService(Type serviceType) {
        Func<Object> res;
        return provided.TryGetValue(serviceType, out res) ? res() : inner.GetService(serviceType);
    }

    public IEnumerable<Object> GetServices(Type serviceType) {
        Func<Object> res;
        return inner.GetServices(serviceType).Concat(provided.TryGetValue(serviceType, out res) ? new[] { res()} : Enumerable.Empty<object>());
    }
    protected readonly T inner;
    protected readonly IDictionary<Type, Func<object>> provided;
    public WebApiOverrideDependency(T inner, IDictionary<Type, Func<object>> provided) {
        this.inner = inner;
        this.provided = provided;
    }

}

Хитрость заключается в том, что на самом деле вам нужно реализовать IDependencyScope дважды — один раз для IDependencyResolver и один раз для области действия, которую он создает при каждом запросе.

person George Mauer    schedule 14.06.2016
comment
Спасибо; если бы я еще не отметил другой как ответ, я бы выбрал этот. - person B. Clay Shannon; 16.06.2016
comment
Вы всегда можете изменить это :), но ничего страшного. Просто поместил сюда для потомков - person George Mauer; 16.06.2016

Вы должны использовать инъекцию зависимостей (structuremap, ninject) что угодно. Если вы не хотите использовать DI, вы должны предоставить конструктор перегрузки, как показано ниже.

public DuckbillsController():this( new DuckbillRepository())
{
}
person Dan Hunex    schedule 23.12.2013
comment
Это предполагается, чтобы представить DI в классе; поэтому я добавил конструктор и его аргумент. Так что еще нужно? Мы используем Castle Windsor. - person B. Clay Shannon; 24.12.2013
comment
Castle Windsor поддерживает MVC docs.castleproject.org/ - person Dan Hunex; 24.12.2013