如果发生异常,HttpContext.Current是否已被释放?

4
我问这个问题的原因是因为HttpContext.Current.Items集合似乎是一个很好的放置IDisposable对象(如DataContext)的地方,这样仓储库就可以透明地访问它,而不必向仓储库注入与特定ORM技术相关的任何依赖项。这还允许存储库决定是否参与UnitOfWork或承担实际持久化任何更改的额外责任。
例如:
页面:
protected void Page_Load(...)
{
   Items[KeyValueFromConfigurationFile] = new DataContext();
   var repo = new Repository();
   var rootEntity = repo.GetById(1);
}

代码仓库:

public virtual TEntity GetById(int id)
{
   var ctx = HttpContext.Current.Items[KeyValueFromConfigurationFile] as DataContext;
   return ctx.TEntities.SingleOrDefault(p => p.Id == id);
}

当然,我会检查 null 并执行必要的步骤来获取 DataContext,如果它在 HttpContext.Current.Items 集合中不可用。
所以,回到我的原始问题,考虑上述代码:即使抛出异常,HttpContext.Current 中包含的任何对象及其 Items 集合是否都会被处理?
2个回答

14

HttpContext.Current.Items 中的对象不会自动被释放。你需要自己来处理它们的释放。你可以在 global.asax Application_EndRequest 方法中处理它们:

            foreach (var item in HttpContext.Current.Items.Values)
            {
                var disposableItem = item as IDisposable;

                if (disposableItem != null)
                {
                    disposableItem.Dispose();
                }
            }

1
谢谢,这个有效。一个需要注意的事情是 EndRequest 被触发的频率。在这个帖子中 http://www.mojoportal.com/Forums/Thread.aspx?pageid=5&t=1558~-1 。Visual Studio Web Server 会为所有的 HTTP 请求(包括图片、CSS等)触发此事件,而 IIS 只在 .NET 处理的页面中触发。如果你在 VS Web Server 中调试并看到事件一遍又一遍地被触发,请记住这一点。 - Walter Stabosz
foreach (var v in HttpContext.Current.Items.Values.OfType<IDisposable>()) v.Dispose(); - Denis

2

我认为上一篇帖子提供的解决方案没有问题,因为底层集合未被修改,而在枚举时无法进行修改,但最近我遇到了这段代码的偶发错误。当OnEndRequest触发并执行以下for循环时,我们看到错误“Collection was modified; enumeration operation may not execute”:

        foreach (var item in HttpContext.Current.Items.Values)
        {
            var disposableItem = item as IDisposable;

            if (disposableItem != null)
            {
                disposableItem.Dispose();
            }
        }

显然,在使用foreach循环迭代集合的同时处理当前HttpContext项目会带来问题。有几种解决方法,但我选择了这种方式:

        int size = HttpContext.Current.Items.Count;
        if (size > 0)
        {
            var keys = new object[size];
            HttpContext.Current.Items.Keys.CopyTo(keys, 0);

            for (int i = 0; i < size; i++)
            {
                var obj = HttpContext.Current.Items[keys[i]] as IDisposable;
                if (obj != null)
                    obj.Dispose();
            }
        }

这是正确的,但集合在某种程度上被修改了,我不确定原因。通常,dispose调用不应该影响集合。也许其他线程同时更改了集合(Application_EndRequest在不同的线程上触发,而且在响应发送到客户端之后/并发时)。 - Cosmin Vană

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接