我正在使用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();
}
}
}
编辑
似乎MVC PerRequestLifetimeManager
在我尝试使用它的上下文中不起作用,但是我已经设计出我认为使用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()));
}
}
我现在的问题是:解决方法是否包含任何缺陷或弱点?
答案 0 :(得分:0)
聚会晚了一点,但是花了几个小时寻找一个好的解决方案之后,我发现了这个博客文章:https://codingsight.com/using-single-ioc-container-http-request-web-api-vs-owin-middleware/
对于WebApi和MVC,想法是相同的,其中Owin中间件在ie中创建“作用域容器”或“子容器”以及依赖项解析。使用OwinContext中的该作用域容器完成MVC。
答案 1 :(得分:-2)
您提供的代码解决了Github here上的OwinPerRequestUnityMiddleware
项目中的问题。
但是,我在使用此代码时注意到的一件事是IDisposable
对象在自定义OData序列化程序提供程序完成创建资源之前就被处置了。
如果即使在OWIN管道的生命周期结束后仍需要对象,则可以在容器中使用Factory
类。
例如,使用DbContext
代替使用IDbContextFactory<MyDbContext>
。