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

本文关键字:上下文 请求 HTTP PerRequestLifetimeManager | 更新日期: 2023-09-27 18:18:06

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

container.RegisterType<IFileService, FileService>();

一切都很好,除了当我试图滚动我的解决方案来自动执行任务(如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<T>() where T : class
    {
        object result = DependencyResolver.Current.GetService<T>();
        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<T>() where T : class
    {
        object result = DependencyResolver.Current.GetService<T>();
        return (T)result;
    }

在我的"TaskManager"中,我做了下面的操作:

var unitOfWork = ContainerManager.Resolve<IFileService>();

现在,当手动启动时(当从HttpRequest发起时),它可以工作。但是,当通过我的后台线程启动时,这不起作用。

我试过直接调用unity(没有我的ServiceLocator),但随后我会得到例外: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;
    }

我错过了什么?

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

您正在使用被认为是反模式的服务位置。

话虽如此,以下是对你问题的直接回答:

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

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

container.RegisterType<IService, Service>(new PerRequestLifetimeManager());

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

container.RegisterType<IService, Service>("transient_service", new TransientLifetimeManager());

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

现在,在后台线程中,你可以像这样定位这个服务:

var service = container.Resolve<IService>("transient_service");

我在这里假设您可以访问容器(这是通过服务定位器进行的)。您可能需要更新您的服务定位器,使其能够按名称定位服务。

更新:

这是另一个解决方案:

你可以创建一个自定义生命周期管理器,如果当前线程中有HttpContext,它将充当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;
    }
}

我建议你有2个独立的容器,不同的配置,用于web环境和后台环境。因此,对于你的web环境,你可以控制每个请求的生存期,在后台任务中你可以控制每个线程。

当你使用服务定位器时,你可以有2个定位器,如webservicellocator . resolve<>和backgroundservicellocator . resolve<>

2023更新为可接受的答案

现在代码应该是这样的:

public class PerRequestOrHierarchicalLifeTimeManager : LifetimeManager, ITypeLifetimeManager,
    IFactoryLifetimeManager
{
    private readonly PerRequestLifetimeManager perRequestLifetimeManager = new PerRequestLifetimeManager();
    private readonly HierarchicalLifetimeManager hierarchicalLifetimeManager = new HierarchicalLifetimeManager();
    private LifetimeManager GetAppropriateLifetimeManager()
    {
        if (HttpContext.Current == null)
        {
            return hierarchicalLifetimeManager;
        }
        return perRequestLifetimeManager;
    }
    public override object GetValue(ILifetimeContainer container = null)
    {
        return GetAppropriateLifetimeManager().GetValue(container);
    }
    public override void SetValue(object newValue, ILifetimeContainer container = null)
    {
        GetAppropriateLifetimeManager().SetValue(newValue, container);
    }
    public override void RemoveValue(ILifetimeContainer container = null)
    {
        GetAppropriateLifetimeManager().RemoveValue(container);
    }
    protected override LifetimeManager OnCreateLifetimeManager()
    {
        return this;
    }
    public LifetimeManager CreateLifetimePolicy()
    {
        return this;
    }
}