Я создаю службу WebApi с использованием собственного хостинга OWIN (запускается либо в режиме консоли, либо в качестве службы Windows - окончательное развертывание для службы, режим консоли для отладки / разработки).У меня проблемы с временем жизни каждого запроса для внедрения в Unity.
Ранее я создавал другие службы, развернутые как IIS WebApi;Я добавил Nuget Mvc вместе с базовым Unity, следуйте инструкциям, и все это работает.
В среде с собственным хостом OWIN у меня нет проблем с регистрацией типов по умолчанию и регистрациями ContainerControlledLifetimeManager
,но я не могу заставить PerRequestLifetimeManager
работать.Во-первых, UnityMvcActivator
не запускается автоматически под собственным хостом OWIN, как для IIS.Если я в явном виде вызываю UnityMvcActivator.Start
, когда я это делаю, попытка разрешения типа, зарегистрированного с PerRequestLifetimeManager
, приводит к InvalidOperationException со следующим сообщением:
PerRequestLifetimeManager может быть толькоиспользуется в контексте HTTP-запроса. Возможные причины этой ошибки: использование менеджера времени жизни в приложении, отличном от ASP.NET, или использование его в потоке, который не связан с соответствующим контекстом синхронизации.
Может ли PerRequestLifetimeManager
работать с собственным хостом OWIN или это зависит от IIS?Если он будет работать под собственным хостом OWIN, как мне заставить UnityMvcActivator
правильно запускаться и мои введенные типы регистрируются?Если PerRequestLifetimeManager
не совместим с собственным хостом OWIN, как еще можно зарегистрировать типы с временем жизни каждого запроса?
Часть UnityConfig:
public static void RegisterTypes(IUnityContainer container)
{
// NOTE: To load from web.config uncomment the line below.
// Make sure to add a Unity.Configuration to the using statements.
// container.LoadConfiguration();
// TODO: Register your type's mappings here.
// container.RegisterType<IProductRepository, ProductRepository>();
// My test registration:
container.RegisterType<ITestService, TestService>(new PerRequestLifetimeManager());
}
UnityMvcActivator (как предусмотрено вNuGet, за исключением одной строки без комментариев):
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(Accounting.Service.UnityMvcActivator), nameof(Accounting.Service.UnityMvcActivator.Start))]
[assembly: WebActivatorEx.ApplicationShutdownMethod(typeof(Accounting.Service.UnityMvcActivator), nameof(Accounting.Service.UnityMvcActivator.Shutdown))]
namespace Accounting.Service
{
/// <summary>
/// Provides the bootstrapping for integrating Unity with ASP.NET MVC.
/// </summary>
public static class UnityMvcActivator
{
/// <summary>
/// Integrates Unity when the application starts.
/// </summary>
public static void Start()
{
var container = UnityConfig.Container;
FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
// TODO: Uncomment if you want to use PerRequestLifetimeManager
Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
}
/// <summary>
/// Disposes the Unity container when the application is shut down.
/// </summary>
public static void Shutdown()
{
UnityConfig.Container.Dispose();
}
}
}
EDIT
Кажется, что MVC PerRequestLifetimeManager
не будет работать в контексте Iя пытаюсь его использовать, но я разработал, как мне кажется, обходной путь использования промежуточного программного обеспечения OWIN следующим образом:
public class PerRequestMiddleware : OwinMiddleware
{
[ThreadStatic] static Dictionary<Type, object> _instances;
public PerRequestMiddleware(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
_instances = null;
await Next.Invoke(context);
if (_instances != null)
{
foreach (var disposable in _instances.Values.OfType<IDisposable>())
{
disposable.Dispose();
}
}
}
public static T GetInstance<T>(Func<T> constructor)
{
if (_instances == null)
{
_instances = new Dictionary<Type, object>();
}
var type = typeof(T);
if (!_instances.TryGetValue(type, out var instance))
{
instance = constructor();
_instances[type] = instance;
}
return (T)instance;
}
}
Вместо регистрации по типу я регистрирую фабрики следующим образом:
public static class UnityConfig
{
#region Unity Container
private static Lazy<IUnityContainer> container =
new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
/// <summary>
/// Configured Unity Container.
/// </summary>
public static IUnityContainer Container => container.Value;
#endregion
/// <summary>
/// Registers the type mappings with the Unity container.
/// </summary>
/// <param name="container">The unity container to configure.</param>
/// <remarks>
/// There is no need to register concrete types such as controllers or
/// API controllers (unless you want to change the defaults), as Unity
/// allows resolving a concrete type even if it was not previously
/// registered.
/// </remarks>
public static void RegisterTypes(IUnityContainer container)
{
// NOTE: To load from web.config uncomment the line below.
// Make sure to add a Unity.Configuration to the using statements.
// container.LoadConfiguration();
// TODO: Register your type's mappings here.
// container.RegisterType<IProductRepository, ProductRepository>();
container.RegisterFactory<ITestService>(c => PerRequestMiddleware.GetInstance(() => new TestService()));
}
}
Теперь у меня вопрос: есть ли у моего обходного пути какие-либо недостатки или слабости?