我有一个 MVC 应用程序,其中有一个 Controller ,用于接收来自用户的数据,然后将文件上传到 Azure blob 存储。该应用程序使用 Unity IoC 来处理依赖项注入(inject)。
在工作流程中,我隔离了以下代码来演示问题
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 为 null...
还有人遇到过这个问题吗?如果是这样,这是一个错误吗?这是预期的行为吗?我是不是做了什么不寻常的事?我应该做什么来解决这个问题?
我可以通过在有问题的调用之前存储对 HttpContext.Current 的引用并在之后恢复它来绕过它,但这似乎不是正确的方法。
有什么想法吗?
最佳答案
回显@Joachim - http 上下文可能对您的异步线程不可用。将当前线程 id(您可以看到 httpcontext 可用)与线程 id(您可以看到它不可用)进行比较 - 我假设您会看到它们是 2 个不同的线程。如果我的假设是正确的,这可能表明您的主线程(带有 httpcontext 的线程)没有“同步上下文”。 (您可以参阅 http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx 了解其工作原理的更多详细信息)如果是这样,则可能意味着紧接在您的await 语句之后的代码实际上与await 语句之前的代码不在同一线程上运行!因此,从您的角度来看,某一时刻您拥有 http 上下文,而下一时刻您就没有了,因为执行实际上已切换到另一个线程!如果是这种情况,您可能应该考虑在主线程上实现/设置同步上下文,然后控制权将通过 http 上下文返回到原始线程,这应该可以解决您的问题,或者您可以从 http 上下文中检索对象原始线程并找到一种方法将其作为参数传递给异步方法,以便它们不需要访问 http 上下文来获取其状态。
关于azure - 为什么我对 Azure 的调用会杀死 HttpContext.Current,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22311018/