PerRequestLifetimeManager只能在HTTP请求的上下文中使用

我有一个MVC应用程序,它使用Unity作为其IoC容器,并使用PerRequestLifetimeManager在我的应用程序中定义了多个服务。

container.RegisterType();

一切正常,除非我尝试将我的解决方案推广到自动化任务(如SharePoint TimerJobs),并以不同的时间间隔启动。

为此,我在一个单独的项目中定义了一个ServiceLocator -Type类ContainerManager ,基本上这样做:

  public static object Resolve(string typeName) { var type = Type.GetType(typeName); return Resolve(type); } public static object Resolve(Type type) { object result = DependencyResolver.Current.GetService(type); return result; } public static T Resolve() where T : class { object result = DependencyResolver.Current.GetService(); return (T)result; } public static object Resolve(string typeName) { var type = Type.GetType(typeName); return Resolve(type); } public static object Resolve(Type type) { object result = DependencyResolver.Current.GetService(type); return result; } public static T Resolve() where T : class { object result = DependencyResolver.Current.GetService(); return (T)result; } 

在我的“TaskManager”中,我执行以下操作:

 var unitOfWork = ContainerManager.Resolve(); 

现在这在手动启动时起作用(当源自HttpRequest时)。 但是,当通过我的后台线程启动时,这不起作用。

我试过直接调用unity(没有我的ServiceLocator),但是我会得到exception: PerRequestLifetimeManager can only be used in the context of an HTTP request

这就是我创建任务的方式:

  private ITask CreateTask() { ITask task = null; if (IsEnabled) { var type = System.Type.GetType(Type); if (type != null) { object instance = ContainerManager.Resolve(type); if (instance == null) { // Not resolved instance = ContainerManager.ResolveUnregistered(type); } task = instance as ITask; } } return task; } 

我错过了什么?

您正在使用被认为是反模式的 Serivice Location。

话虽如此,这里是你问题的直接答案:

解决问题的一种方法是使用命名注册 :

假设您正在使用PerRequestLifetimeManager生命周期管理器向Service注册IService ,如下所示:

 container.RegisterType(new PerRequestLifetimeManager()); 

您还可以为相同类型添加另一个注册,但使用不同的生命周期管理器。 但是,为了区分这个和之前的注册,你必须给它一个这样的名字:

 container.RegisterType("transient_service", new TransientLifetimeManager()); 

在这里,我正在使用Service注册IService并使用瞬态生命周期管理器。 我给这个注册的名字是"transient_service" ,但你可以在这里使用任何名字。

现在,从您的后台线程,您可以找到这样的服务:

 var service = container.Resolve("transient_service"); 

我在这里假设您可以访问容器(您通过服务定位器执行此操作)。 您可能需要更新服务定位器以使其能够按名称查找服务。

更新:

这是另一个解决方案:

如果当前线程中存在HttpContext,则可以创建充当PerRequestLifetimeManager生命周期管理器的自定义生命周期管理器,如果没有,则将回PerRequestLifetimeManager TransientLifetimeManager

以下是这样的终身经理的样子:

 public class PerRequestOrTransientLifeTimeManager : LifetimeManager { private readonly PerRequestLifetimeManager m_PerRequestLifetimeManager = new PerRequestLifetimeManager(); private readonly TransientLifetimeManager m_TransientLifetimeManager = new TransientLifetimeManager(); private LifetimeManager GetAppropriateLifetimeManager() { if (System.Web.HttpContext.Current == null) return m_TransientLifetimeManager; return m_PerRequestLifetimeManager; } public override object GetValue() { return GetAppropriateLifetimeManager().GetValue(); } public override void SetValue(object newValue) { GetAppropriateLifetimeManager().SetValue(newValue); } public override void RemoveValue() { GetAppropriateLifetimeManager().RemoveValue(); } } 

您需要修改注册以使用此类终身经理。

更新2:

自定义LifetimeManger代码不适用于Unity 3.0或更高版本,因为它已被完全重写并进一步抽象为新的Nuget包。 这是一个更新的代码:

 public class PerRequestOrTransientLifeTimeManager : LifetimeManager { private readonly PerRequestLifetimeManager _perRequestLifetimeManager = new PerRequestLifetimeManager(); private readonly TransientLifetimeManager _transientLifetimeManager = new TransientLifetimeManager(); private LifetimeManager GetAppropriateLifetimeManager() { if (HttpContext.Current == null) { return _transientLifetimeManager; } return _perRequestLifetimeManager; } public override object GetValue(ILifetimeContainer container = null) { return GetAppropriateLifetimeManager().GetValue(); } public override void SetValue(object newValue, ILifetimeContainer container = null) { GetAppropriateLifetimeManager().SetValue(newValue); } public override void RemoveValue(ILifetimeContainer container = null) { GetAppropriateLifetimeManager().RemoveValue(); } protected override LifetimeManager OnCreateLifetimeManager() { return this; } } 

我建议您为Web环境和背景环境提供2个不同配置的独立容器。 因此,对于您的Web环境,您可以控制每个请求的生命周期,并且在后台任务中,您可以按线程执行此操作。

当您使用服务定位器时,您可以拥有2个定位器,例如WebServiceLocator.Resolve <>和BackgroundServiceLocator.Resolve <>