Как настроить простой контейнер инжектора и lifestylse в веб-приложении MVC с помощью WebAPI, WCF, SignalR и фоновой задачи
Простая документация по инжекторам дает отличные примеры того, как настроить контейнер для WebRequest, Web API, WCF,... но примеры специфичны для одной технологии/образа жизни за раз. Наше веб-приложение использует большинство из них вместе!
Мне непонятно, как настроить контейнер для работы с несколькими образцами жизни.
Скажем, у меня есть проект MVC с Web API. У меня есть следующие объекты:
- MyDbContext: мой код сущности первого db-контекста
- IMyDataProvider, реализованный MyDataProvider: содержит логику запросов и использует MyDbContext
- MyController: MVC-контроллер, который использует IMyDataProvider
- MyApiController: контроллер WebApi, который использует IMyDataProvider
Должен ли я создавать и настраивать один контейнер для каждого типа образа жизни?
Когда я регистрирую все, RegisterPerWebRequest<T>
работает в обоих типах контроллеров. Это безопасно? Или я столкнулся с трудностями при использовании async/wait в контроллере Web API?
Какова наилучшая конфигурация, когда у меня есть как MVC, так и Web API-контроллеры, которые вводят те же экземпляры?
Должен ли я использовать гибридный образ жизни?
Теперь, чтобы усложнить ситуацию... наше приложение также использует фоновые задачи и SignalR.
Оба они иногда происходят за пределами WebRequest и нуждаются в доступе к тем же объектам, что описаны выше.
Лучшим решением было бы использовать область Lifetime?
Мне нужно создать новый контейнер для этого образа жизни? или я могу повторно использовать/перенастроить свой контейнер MVC/Web API?
Есть ли тройной образ жизни?
Ответы
Ответ 1
Обычно вам не нужно иметь один контейнер на образ жизни; В общем, вы хотите иметь один экземпляр контейнера для AppDomain. Тем не менее, смешивание веб-API в том же проекте с MVC с архитектурной точки зрения представляет собой ужасную идею IMO (как описано здесь, здесь, и здесь). Поэтому, если вы разделите эти части на свои архитектурные блоки, у вас уже будет меньше проблем.
Но если вы используете MVC и Web API в одном проекте, это в основном означает, что вы всегда будете использовать веб-API. WebApiRequestLifestyle был явно создан для работы:
хорошо как внутри, так и снаружи IIS. т.е. он может функционировать в сам веб-проект API, в котором отсутствует HttpContext.Current. (источник)
В целом, безопасно использовать WebRequestLifestyle в случае, если вы работаете только в IIS, когда у вас нет намерения вращать параллельные операции с помощью ConfigureAwait(false)
(что должно быть действительно редким IMO), как объяснено здесь.
Таким образом, в случае, когда вы все еще смешиваете веб-API с MVC в одном проекте, нет причин использовать гибридный образ жизни; вы можете просто использовать один и тот же образ жизни. Для выполнения фоновой обработки вам может понадобиться построить гибридный образ жизни, но для каждого сценария нужен другой гибрид. Однако, гибриды могут быть сложены, и вы можете легко создать "тройной образ жизни", если это необходимо.
Так как вы хотите выполнять фоновую обработку с помощью SignalR, вам нужно решить, в каком типе охватываемый стиль жизни для запуска этих фоновых операций. Самый очевидный образ жизни - LifetimeScopeLifestyle, и это означает, что вы должны сделать свою регистрацию с использованием следующих видов жизни:
var hybridLifestyle = Lifestyle.CreateHybrid(
lifestyleSelector: () => HttpContext.Current != null,
trueLifestyle: new WebRequestLifestyle(),
falseLifestyle: new LifetimeScopeLifestyle());
Тем не менее, срок службы должен быть явно запущен (так же как и область веб-запросов запускается неявно для вас, если вы включили SimpleInjector.Integration.Web.dll в свое веб-приложение). Как это сделать, зависит от вашего дизайна, но этот q/a о SignalR может указывать на вас в правильном направлении.
Ответ 2
Я должен сказать, что я наткнулся на подобный сценарий некоторое время назад, я закончил тем, что поделился своей конфигурацией через свой веб-API и signalR, но вам нужно реализовать собственный стиль для signalR, поскольку он не основан на веб-запросе.
особенно в signalR, вы обнаружите некоторые проблемы обработки зависимостей для каждого веб-запроса в концентраторе, некоторые из которых будут иметь значение null, например httpContext.Current.
Решение:
Вам нужен гибридный образ жизни между WebRequestLifestlye и Lifestyle.Transient, Lifestyle.Singleton или LifetimeScopeLifestyle. В конце концов я закончил использовать шаблон декоратора, вы можете прочитать этот пост и этот другой пост.
мой декоратор
public class CommandLifetimeScopeDecorator<T> : ICommandHandler<T>
{
private readonly Func<ICommandHandler<T>> _handlerFactory;
private readonly Container _container;
public CommandLifetimeScopeDecorator(
Func<ICommandHandler<T>> handlerFactory, Container container)
{
_handlerFactory = handlerFactory;
_container = container;
}
public void Handle(T command)
{
using (_container.BeginLifetimeScope())
{
var handler = _handlerFactory(); // resolve scoped dependencies
handler.Handle(command);
}
}
}
public interface ICommandHandler<in T>
{
void Handle(T command);
}
Я управлял зависимостями, используя активатор хаба для сигналаR
public class MyHubActivator : IHubActivator
{
private readonly Container _container;
public MyHubActivator(Container container)
{
_container = container;
}
public IHub Create(HubDescriptor descriptor)
{
return _container.GetInstance(descriptor.HubType) as IHub;
}
}
составной корневой файл, в котором вы собираетесь обрабатывать свои зависимости
public CompositRoot(Container container)
{
_container = container;
}
public container Configure()
{
// _container.Registerall container dependencies
return _container;
}
затем поделитесь своей составной конфигурацией корня, когда вы загружаете приложение.
var compositRoot = new CompositRoot(simpleInjector.Container); //simple injector instance
compositRoot.Configure();
Для сигналаR
GlobalHost.DependencyResolver.Register(typeof(IHubActivator), () => new MyHubActivator(compositRoot));
и вы можете повторно использовать свою конфигурацию среди других проектов!
мои два цента
надеюсь, что это поможет!