Срок действия каждого запроса для Unity с автономным хостом OWIN WebApi - PullRequest
0 голосов
/ 02 июля 2019

Я создаю службу 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()));
    }
}

Теперь у меня вопрос: есть ли у моего обходного пути какие-либо недостатки или слабости?

...