PerRequestLifetimeManager 只能在 HTTP 请求的上下文中使用 [英] PerRequestLifetimeManager can only be used in the context of an HTTP request

查看:21
本文介绍了PerRequestLifetimeManager 只能在 HTTP 请求的上下文中使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a MVC application that uses Unity as its IoC container and have multiple services defined in my application using the PerRequestLifetimeManager.

container.RegisterType();

一切正常,除非我尝试推出我的解决方案以自动执行任务(如 SharePoint TimerJobs),这些任务以不同的时间间隔启动.

Everything works fine, except when I tried to roll my solution to automate tasks (like SharePoint TimerJobs), started in various intervals.

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

For that, I've defined a ServiceLocator-Type class ContainerManager in a separate project, that does essentially this:

    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;
    }

在我的任务管理器"中,我执行以下操作:

And inside my "TaskManager" I do the following:

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

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

Now this works when started manually (when originating from an HttpRequest). However, this does not work when started via my background thread.

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

I've tried calling unity directly (without my ServiceLocator), but then I'll get the exception: PerRequestLifetimeManager can only be used in the context of an HTTP request

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

That's how I create my tasks:

    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;
    }

我错过了什么?

推荐答案

您正在使用 被认为是一种反模式.

话虽如此,以下是您问题的直接答案:

Having said that, here is a direct answer to your question:

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

One way to solve your problem is using named registrations:

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

Let say that you are registering IService to Service using the PerRequestLifetimeManager lifetime manager like this:

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

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

You can also add another registration for the same types but with a different lifetime manager. However, to distinguished between this and the previous registration, you have to give it a name like this:

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

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

Here I am registering IService with Service and using the transient lifetime manager. The name I am giving to this registration is "transient_service" , but you can use any name here.

现在,从您的后台线程,您可以像这样定位此服务:

Now, from your background thread, you can locate this service like this:

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

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

I am assuming here that you have access to the container (which you are doing through the service locator). You might need to update your service locator to enable it to locate services by name.

更新:

这是另一种解决方案:

如果当前线程中存在 HttpContext,您可以创建一个自定义生命周期管理器作为 PerRequestLifetimeManager 生命周期管理器,如果存在,它将回退到 TransientLifetimeManager不是.

You can create a custom lifetime manager that acts as the PerRequestLifetimeManager lifetime manager if there is an HttpContext in the current thread, and that will fallback to a TransientLifetimeManager if there isn't.

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

Here is how such lifetime manager would look like:

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();
    }
}

您需要修改您的注册才能使用这样的生命周期管理器.

You need to modify your registrations to use such lifetime manager.

更新 2:

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

The custom LifetimeManger code won't work with Unity 3.0 or later since it was completely rewritten and further abstracted into new Nuget packages as well. Here is an updated code:

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;
    }
}

这篇关于PerRequestLifetimeManager 只能在 HTTP 请求的上下文中使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆