WebAPI: доступ к дочернему контейнеру в качестве локатора службы

В обычных проектах ASP.MVC мы настраиваем преобразователь зависимостей с помощью Unity и пакета Unity.Mvc3 из http://unitymvc3.codeplex.com/

У нас есть эта тестовая служба, зарегистрированная в HierarchicalLifetimeManager

container.RegisterType<ITestService, TestService>(new HierarchicalLifetimeManager());

И подключаем контейнер с Mvc в Global.asax.cs:

System.Web.Mvc.DependencyResolver.SetResolver(new Unity.Mvc3.UnityDependencyResolver(container));

И мы запускаем этот тестовый контроллер:

public class TestController : Controller
{
    private readonly ITestService _service;
    public TestController(ITestService service)
    {
        this._service = service;
    }
    public ActionResult Test()
    {
        var locatedService = System.Web.Mvc.DependencyResolver.Current.GetService<ITestService>();
        if (_service == locatedService)
            return View("Success - Same Service");//This is always the result in an MVC controller
        else
            throw new Exception("Failure - Different Service Located");//This is never the result in an MVC controller
    }
}

Однако в этом проекте мы добавляем ряд контроллеров WebAPI.

У нас есть эта конфигурация в global.asax.cs (используя http://unitywebapi.codeplex.com/ для сейчас. Но я открыт для предложений):

System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);

Мы создали ApiTestController, аналогичный TestController, наследуя от ApiController, а не от Controller. Однако ApiTestController не проходит тест. Я понимаю, что класс System.Web.Mvc.DependencyResolver и свойство System.Web.Mvc.DependencyResolver.Current специфичны для Mvc. Но есть ли аналог у WebAPI?

System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver.GetService не работает, потому что экземпляр System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver является родительским контейнером, который я настроил. Это не дочерний контроллер, который использовался для внедрения ITestService в конструктор.

У этого пользователя похожая проблема: http://unitywebapi.codeplex.com/discussions/359413 Но мне кажется, что это больше связано с WebAPI ASP.NET, чем с Unity.

Спасибо


person Joel    schedule 30.10.2012    source источник


Ответы (3)


Просмотрев исходный код http://unitymvc3.codeplex.com/ и http://unitywebapi.codeplex.com/ Я создал этот класс:

public class MyUnityDependencyResolver : Unity.Mvc3.UnityDependencyResolver, System.Web.Http.Dependencies.IDependencyResolver
{
    public MyUnityDependencyResolver(IUnityContainer container)
        : base(container)
    {
    }

    public System.Web.Http.Dependencies.IDependencyScope BeginScope()
    {
        return this;
    }

    public void Dispose()
    {
        Unity.Mvc3.UnityDependencyResolver.DisposeOfChildContainer();
    }
}

Конфигурация в gobal.asax.cs:

var myResolver = new MyUnityDependencyResolver(container);
System.Web.Mvc.DependencyResolver.SetResolver(myResolver);
System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = myResolver;

Unity.Mvc3.UnityDependencyResolver использует HttpContext.Current.Items для управления дочерними контейнерами. MyUnityDependencyResolver может быть не самой "правильной" реализацией System.Web.Http.Dependencies.IDependencyResolver, но пока вроде работает.

Я отмечу это как ответ через пару дней, если ни у кого больше нет лучших ответов.

person Joel    schedule 30.10.2012
comment
это выглядит так, как будто он вообще не будет создавать дочерние запросы, и поэтому использование HierarchicalLifetimeManager приведет не к времени жизни каждого запроса, а к синглтону, поэтому зависимость будет существовать на протяжении всего времени жизни приложения, что, вероятно, не то, что вы хотите. - person Paul Hiles; 03.04.2013

К сожалению, когда вы вызываете GlobalConfiguration.Configuration.DependencyResolver.GetService, он полностью игнорирует любую область действия и разрешается с использованием внешнего не-дочернего контейнера, который существует на протяжении всего времени существования приложения. Это проблема с Web Api, из-за которой невозможно использовать внедрение конструктора для зависимостей каждого запроса вне контроллеров. Как ни странно, это совершенно другое поведение, чем MVC, как вы говорите.

Что вы можете сделать, так это использовать метод расширения GetDependencyScope() для HttpRequestMessage. Все, что вы решите с помощью этого, будет в области запроса при использовании HierarchicalLifetimeManager в сочетании с Unity.WebApi. Запрос доступен из фильтров действий и обработчиков, поэтому может быть жизнеспособным обходным путем.

Очевидно, что это чистое расположение службы, а не внедрение зависимостей, что далеко от идеала, но я не нашел другого способа доступа к зависимостям для каждого запроса вне контроллеров.

См. этот пост для получения дополнительной информации.

person Paul Hiles    schedule 03.04.2013

DependencyResolver не подходит для внедрения зависимостей в ASP.NET WebAPI.

У Марка Симанна есть две очень хорошие статьи о внедрении зависимостей с помощью WebAPI.

Внедрение зависимостей и управление жизненным циклом с помощью ASP.NET Web API

Внедрение зависимостей в веб-API ASP.NET с Castle Windsor

Если вы хотите сделать это правильно, вы должны взглянуть на них.

person Sebastian Weber    schedule 31.10.2012
comment
Он не использует преобразователь в своем коде, только в инфраструктуре. И это, тем не менее, для создания UoW. В какой-то момент вы должны привязаться к синглтону. Этот аргумент действителен только в том случае, если он использует преобразователь в контроллерах вместо DI-зависимостей. Опять же, в зависимости от контекста, если уже существует сервисный уровень с более строгим соблюдением DI, это тоже может быть в порядке. - person Alwyn; 09.10.2013