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

C# PerRequestLifetimeManager只能在HTTP请求的上下文中使用,c#,unity-container,asp.net-mvc-5.2,C#,Unity Container,Asp.net Mvc 5.2,我有一个MVC应用程序,它使用Unity作为IoC容器,并且在我的应用程序中使用PerRequestLifetimeManager定义了多个服务 container.RegisterType() 一切正常,除了我尝试滚动解决方案以自动执行任务(如SharePoint TimerJobs)时,这些任务是以不同的时间间隔启动的 为此,我在一个单独的项目中定义了一个ServiceLocator-Type类ContainerManager,它基本上实现了以下功能: public static o

我有一个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<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;
    }

我遗漏了什么?

您正在使用的服务位置

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

解决问题的一种方法是使用:

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

container.RegisterType<IService, Service>(new PerRequestLifetimeManager());
container.RegisterType<IService, Service>("transient_service", new TransientLifetimeManager());
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();
    }
}
您需要修改注册以使用此lifetime manager

更新2:

自定义LifetimeManager代码无法与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环境和后台环境使用两个不同配置的单独容器。因此,对于您的web环境,您可以控制每个请求的生存期,在后台任务中,您可以控制每个线程的生存期


当您使用服务定位器时,您可能有两个定位器,如WebServiceLocator.Resolve和BackgroundServiceLocator.Resolve,我正在尝试理解您的问题。当你说“但是,当通过我的后台线程启动时,这不起作用”,你是什么意思?你有例外吗?所以,TimerJobs不是按HTTP请求执行的,它们是按一定的时间表执行的,对吗?您希望他们使用自己的
FileService
,对吗?@YacoubMassad没错。当我创建一个要在控制器中运行的新任务(源自HTTP请求)时,Unity能够解析依赖关系。但是,当我的后台计时器执行此操作时,unity不会解析依赖项,并在调用我的ServiceLocator后返回“null”。当您说Task时,您是指来自TPL吗?我以为您正在使用SharePoint中的其他任务?能否提供一些关于如何创建任务的代码?在计时器中,是否要使用与调度任务的请求相同的服务实例?或者你不关心这个,只是想要一个新的服务实例?@YacoubMassad My
Task
是一个自定义类。SharePoint只是用来说明我正在编写的代码类型:)我真的不在乎得到哪个实例。。。我只想得到一个实例。我已经添加了如何创建任务的代码。谢谢您的回答。但这对构造函数注入的依赖项不起作用,是吗?你能解释一下你试图从后台线程创建的对象图,以及这样的图是如何使用构造函数注入的吗?我的一个任务可能被称为清理任务,它从磁盘清理文件(因此需要
IFileService
)因此,所述清理任务的构造函数有一个IFileService参数。这是一个很好的解决方案+1.唯一可能的问题是,我们需要维护两个可能相同的合成根。@YacoubMassad容器配置本身不应导致重复,因为您可以将lifetimemanager排除在外,并在使用之前决定使用哪一个。如果复制源于使用一个或另一个服务定位器的代码,那么最好是使用注入工厂或甚至注入IContainer来替换服务定位器。然后,您只需在每个环境的入口点中引用一个或另一个容器。我不确定这是否有意义。我认为你是对的。您可以使配置容器的代码采用生存期管理器类型作为输入。每个应用程序都将使用不同的生命周期管理器使用这些容器构建代码。
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();
    }
}
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;
    }
}