为什么我对Azure的调用会杀死HttpContext.Current

时间:2014-03-10 20:50:15

标签: azure asynchronous unity-container

我有一个MVC应用程序,其中我有一个控制器,它从用户接收数据,然后将文件上传到Azure blob存储。该应用程序使用Unity IoC来处理依赖注入。

在工作流程中,我已将以下代码隔离为演示问题

public class MvcController : Controller
{
    private IDependencyResolver _dependencyResolver;

    public MvcController() : this(DependencyResolver.Current)
    {
    }

    public MvcController(IDependencyResolver dependencyResolver)
    {
        this._dependencyResolver = dependencyResolver;
    }

    public GetService<T>()
    {
        T resolved = _dependencyResolver.GetService<T>()
        if (resolved == null)
            throw new Exception(string.Format("Dependency resolver does not contain service of type {0}", typeof(T).Name));
        return resolved;
    }
}
public class MyController : MvcController
{
    [NoAsyncTimeout]
    public async Task<ActionResult> SaveFileAsync(/* A bunch of arguments */)
    {
        /* A bunch of code */

        //This line gets a concrete instance from HttpContext.Current successfully...
        IMyObject o = GetService<IMyObject>();

        await SaveFileToAzure(/* A bunch of parameters */);
        .
        .
        /* Sometime later */
        Method2(/* A bunch of parameters */);
    }

    private Method2(/* A bunch of parameters */)
    {
        //This line fails because HttpContext.Current is null
        IMyObject o = GetService<IMyObject>();

        /* A bunch of other code */
    }

    private async Task SaveFileToAzure(/* A bunch of parameters */)
    {
        //Grab a blob container to store the file data...
        CloudBlobContainer blobContainer = GetBlobContainer();
        ICloudBlob blob = blobContainer.GetBlockBlobReference(somePath);

        Stream dataStream = GetData();
        System.Threading.CancellationToken cancelToken = GetCancellationToken();

        //All calls to DependencyResolver.GetService<T>() after this line of code fail...
        response = await blob.UploadStreamAsync(dataStream, cancelToken);

        /* A bunch of other code */
    }
}

Unity已注册我的对象:

container.RegisterType<IMyObject, MyObject>(new HttpLifetimeManager());

我的终身经理定义如下:

public sealed class HttpRequestLifetimeManager : LifetimeManager
{
    public Guid Key { get; private set; }

    public HttpRequestLifetimeManager()
    {
        this.Key = Guid.NewGuid();
    }

    public override object GetValue()
    {
        return HttpContext.Current.Items[(object)this.Key];
    }

    public override void SetValue(object newValue)
    {
        HttpContext.Current.Items[(object)this.Key] = newValue;
    }

    public override void RemoveValue()
    {
        HttpContext.Current.Items.Remove((object)this.Key);
    }
}

没什么复杂的。

在失败的GetService()调用中进入HttpRequestLifetimeManager会显示在UploadStreamAsync()之后调用HttpContext.Current为空...

还有其他人遇到过这个问题吗?如果是这样,这是一个错误吗?这是预期的行为吗?我做了一件与众不同的事吗?我应该 解决它?

我可以通过在违规调用之前存储对HttpContext.Current的引用并在之后恢复它来解决它,但这似乎不是正确的方法。

有什么想法吗?

1 个答案:

答案 0 :(得分:3)

要回显@Joachim - 您的异步线程可能无法使用http上下文。比较当前线程ID,你可以看到httpcontext可用,到线程ID,你可以看到它不是 - 我假设你会看到他们是2个不同的线程。如果我的假设是正确的,这可能表明您的主线程(具有httpcontext的线程)没有“synchronizationcontext”。 (你可以看到http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx了解它是如何工作的更多细节)如果是这样的话,它可能意味着紧跟在await语句之后的代码实际上没有在await语句之前的代码运行在同一个线程上!所以从你的角度来看,有一个时刻你有了http上下文而下一个你没有,因为执行实际上已经切换到了另一个线程!如果是这种情况,你应该看一下在你的主线程上实现/设置一个synchronizationcontext然后控件将返回到你的原始线程与http上下文,这应该可以解决你的问题,或者你可以从http上下文中检索你的对象原始线程并找到一种方法将其作为参数传递给async方法/ s,这样他们就不需要访问http上下文来获取它们的状态。

相关问题