2

我正在使用 OWIN 自托管构建 WebApi 服务(可在控制台模式下运行或作为 Windows 服务运行 - 最终部署为服务,控制台模式用于调试/开发)。我在 Unity 注入的每个请求生命周期方面遇到问题。

我之前已经构建了部署为 IIS WebApi 的其他服务;我添加了 Unity Mvc nuget 和基本的 Unity nuget,按照说明进行操作,一切正常。

在OWIN自托管环境下,我对默认类型注册和注册没有问题ContainerControlledLifetimeManager,但是我无法开始PerRequestLifetimeManager工作。一方面,UnityMvcActivator不会像 IIS 那样在 OWIN 自托管下自动启动。如果我在这样做时显式调用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();
        }
    }
}

编辑

似乎 MVCPerRequestLifetimeManager在我尝试使用它的上下文中不起作用,但我已经设计了我认为使用 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()));
    }
}

我现在的问题是:我的解决方法是否包含任何缺陷或弱点?

4

2 回答 2

0

聚会有点晚了,但花了几个小时寻找一个好的解决方案后,我发现了这篇博文:https ://codingsight.com/using-single-ioc-container-http-request-web-api-vs-owin-middleware/

WebApi 和 MVC 的想法是相同的,其中 Owin 中间件在 ie 中创建“作用域容器”或“子容器”和依赖项解析。MVC 是使用 OwinContext 中的这个作用域容器完成的。

于 2020-11-16T20:51:33.607 回答
-2

您提供的代码在这里解决了OwinPerRequestUnityMiddlewareGithub 上项目中的问题。

但是,我在使用此代码时注意到的一件事是IDisposable对象在自定义 OData 序列化程序提供程序完成创建资源之前被释放。

如果即使在 OWIN 管道的生命周期之后仍需要对象,则可以使用Factory容器中的类。

例如,不要使用 ,而是DbContext使用IDbContextFactory<MyDbContext>

于 2019-09-26T11:41:37.793 回答