簡體   English   中英

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

[英]PerRequestLifetimeManager can only be used in the context of an HTTP request

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

我錯過了什么?

您正在使用被認為是反模式的 Serivice Location。

話雖如此,這里是您問題的直接答案:

解決問題的一種方法是使用命名注冊

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

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

您還可以為相同類型添加另一個注冊,但使用不同的生命周期管理器。 但是,為了區分這個和之前的注冊,你必須給它一個這樣的名字:

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

在這里,我正在使用Service注冊IService並使用瞬態生命周期管理器。 我給這個注冊的名字是"transient_service" ,但你可以在這里使用任何名字。

現在,從您的后台線程,您可以找到這樣的服務:

var service = container.Resolve<IService>("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 <>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM