Простой инжектор не может входить в зависимости от параметров в контроллерах веб-API

Я пытаюсь создать базовый конструктор DI с помощью Simple Injector, и кажется, что он не может разрешить зависимости для контроллеров Web API.

  • У меня есть контроллер API в папке "API", которая находится вне папки "Контроллеры".
  • Я также попытался поместить его в папку "Контроллеры", но это, казалось, не имело большого значения. Трассировка стека Я получаю аналогично тому, который представлен в этом вопросе.
  • Я использую свежую установку пакета NuGet "Простой запуск MVC Integration" (версия 2.1.0).
  • У меня есть база SimpleInjectorWebApiDependencyResolver из документации, которая также совпадает с найденной здесь.
  • Я использую Entity Framework и посмотрел обсуждение нить об изменениях для правильной загрузки контекста.

Это не  похоже, проблема, но я все еще получаю следующую ошибку:

Тип "MyProject.API.ArticleController" не имеет значения по умолчанию конструктор

System.ArgumentException at

System.Linq.Expressions.Expression.New(Type type) в System.Web.Http.Internal.TypeActivator.Create [TBase] (Тип instanceType) в System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage запрос, введите controllerType, Func'1 & активатор) в System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage запрос, HttpControllerDescriptor controllerDescriptor, Тип controllerType)

Буду признателен, если кто-нибудь предложит мне несколько советов о том, следует ли что-либо изменить из текущего состояния/порядка вызовов.

ArticleController (базовая структура):

public class ArticleController : ApiController
{
    private readonly IArticleRepository articleRepository;
    private readonly IUserRepository userRepository;
    private readonly IReleaseRepository releaseRepository;

    public ArticleController(IArticleRepository articleRepository, IUserRepository userRepository, IReleaseRepository releaseRepository)
    {
        this.articleRepository = articleRepository;
        this.userRepository = userRepository;
        this.releaseRepository = releaseRepository;
    }

    // GET api/Article
    public IEnumerable<Article> GetArticles(){ // code }

    // GET api/Article/5
    public Article GetArticle(int id){ // code }

    // PUT api/Article/5
    public HttpResponseMessage PutArticle(int id, Article article){ // code }

    // POST api/Article
    public HttpResponseMessage PostArticle(ArticleModel article){ // code }

    // DELETE api/Article/5
    public HttpResponseMessage DeleteArticle(int id){ // code }
}

SimpleInjectorInitializer:

public static class SimpleInjectorInitializer
{
    public static void Initialize()
    {
        var container = new Container();
        InitializeContainer(container);
        container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
        container.RegisterMvcAttributeFilterProvider();
        container.Verify();

        DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
    }

    private static void InitializeContainer(Container container)
    {
        container.Register<IArticleRepository, ArticleRepository>();
        container.Register<IUserRepository, UserRepository>();
        container.Register<IReleaseRepository, ReleaseRepository>();
    }
}

Global.asax.cs:

public class WebApiApplication : System.Web.HttpApplication
{
    private void ConfigureApi()
    {
        // Create the container as usual.
        var container = new Container();

        // Verify the container configuration
        // container.Verify();

        // Register the dependency resolver.
        GlobalConfiguration.Configuration.DependencyResolver =
                new SimpleInjectorWebApiDependencyResolver(container);
    }

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        ConfigureApi();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}

Ответ 1

TLTR: проблема вызвана неявным способом, которым Web API обрабатывает разрешения типов контроллеров; зарегистрируйте свои контроллеры Web API явно, и вы увидите, в чем проблема.

Вот шаг за шагом, что происходит под одеялом:

  1. System.Web.Http.DefaultHttpControllerActivator вызывает SimpleInjectorWebApiDependencyResolver и запрашивает создание контроллера API.
  2. SimpleInjectorWebApiDependencyResolver перенаправляет этот вызов в экземпляр SimpleInjector.Container.
  3. Однако этот экземпляр Container не имеет явных регистраций для этого контроллера API (поскольку вы указали для преобразователя пустой контейнер).
  4. Поскольку явной регистрации нет, контейнер пытается выполнить регистрацию в последний момент для этого типа.
  5. Этот тип контроллера, однако, зависит от интерфейсов, которые не могут быть разрешены, поскольку они не зарегистрированы в контейнере (помните, ваш контейнер пуст).
  6. Хотя контейнер обычно генерирует исключение, в этом случае возвращается значение null, поскольку тип запрашивается методом IServiceProvider.GetService, а тип не был зарегистрирован явно.
  7. Метод SimpleInjectorWebApiDependencyResolver GetService также возвратит null, поскольку по определению он должен возвращать ноль; Он должен возвращать ноль, если регистрация не существует (что в настоящее время имеет место).
  8. Поскольку DependencyResolver возвратил ноль, DefaultHttpControllerActivator вернется к своему поведению по умолчанию, что означает создание самого этого типа, но для этого требуется, чтобы контроллер имел конструктор по умолчанию.

Короче говоря, проблема вызвана неявным способом, которым Web API обрабатывает типы контроллеров.

Таким образом, решение здесь заключается в следующем:

  1. В вашем веб-приложении есть только один Container. Это предотвращает всевозможные проблемы и осложнения вашей конфигурации.
  2. Зарегистрируйте все контроллеры Web API явно в контейнере. Явная регистрация контроллеров гарантирует, что Simple Injector выдаст исключение, если контроллер не может быть разрешен. Кроме того, это позволяет вам вызывать container.Verify(), что приведет к сбою приложения во время запуска, когда конфигурация недействительна (важна проверяемая конфигурация). И это также позволяет вам диагностировать конфигурацию, что дает вам еще большую уверенность в правильности вашей конфигурации.

Мой совет - разместить MVC и Web API в их собственном проекте. Это сделает все намного проще.

Регистрация всех контроллеров Web API может быть выполнена с помощью следующего кода:

container.RegisterWebApiControllers(GlobalConfiguration.Configuration);

UPDATE:

Поскольку эта ошибка настолько распространена, более новые версии класса SimpleInjectorWebApiDependencyResolver просто никогда не возвращают null при запросе типа контроллера. Вместо этого он выдаст описательную ошибку. Из-за этого вы никогда не должны видеть ошибку, пока вы используете официальный SimpleInjectorWebApiDependencyResolver.

Ответ 2

У меня работают следующие настройки:

1) включить Unity.WebAPI из https://www.nuget.org/packages/Unity.WebAPI/ 2) в UnityConfig

public static class UnityConfig
    {
        public static void RegisterComponents()
        {
            var container = new UnityContainer();
            // **** Important note -----
            // register all your components with the container here
            // e.g. container.RegisterType<ITestService, TestService>();


            DependencyResolver.SetResolver(new Unity.Mvc5.UnityDependencyResolver(container));

            GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
        }
    }

3) в файле Global.asax file

 public class MvcApplication : System.Web.HttpApplication
        {
            protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
                UnityConfig.RegisterComponents();
                GlobalConfiguration.Configure(WebApiConfig.Register);
                FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
                RouteConfig.RegisterRoutes(RouteTable.Routes);
                BundleConfig.RegisterBundles(BundleTable.Bundles);
            }
        }

Начало работы с Unity.WebAPI

Для начала просто добавьте вызов UnityConfig.RegisterComponents() в методе Application_Start файла Global.asax.cs. и инфраструктура Web API затем будет использовать Unity.WebAPI DependencyResolver для разрешения ваших компонентов.

например,

public class WebApiApplication : System.Web.HttpApplication
{
  protected void Application_Start()
  {
    AreaRegistration.RegisterAllAreas();
    UnityConfig.RegisterComponents();                           // <----- Add this line
    GlobalConfiguration.Configure(WebApiConfig.Register);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
  }           
}  

Добавьте свои регистрации Unity в метод RegisterComponents класса UnityConfig. Все компоненты, которые реализуют IDisposable должны быть зарегистрирован в HierarchicalLifetimeManager, чтобы убедиться, что они правильно расположены в конце запроса.