如何访问线程或定时器中的 HttpServerUlity.MapPath 方法?

我在我的 Asp 中使用 System.Timers.Timer。Net 应用程序和我需要使用的 HttpServerUtility.MapPath方法,似乎只有通过 HttpContext.Current.Server.MapPath可用。 问题是,当 Timer.Elapsed事件触发时,HttpContext.Currentnull

还有其他获取对 HttpServerUtilityobject 的引用的方法吗? 我可以把它注入到类的构造函数中。安全吗?我如何确定它不会在当前请求的末尾被垃圾收集?

谢谢!

40709 次浏览

I think the reason for why it is null at that time (if you think about it), is that the timer elapsed event doesn't occur as part of a HTTP request (hence there is no context). It is caused by something on your server.

Can you not call the MapPath function before starting the timer, and simply cache the result? Is it absolutely neccessary to have the MapPath call inside the tick event?

When the timer elapse, there is no current HTTP context. This is because the timer events are not related to a specific HTTP request.

What you should do is use HttpServerUtility.MapPath where HTTP context is available. You can do it in one of the request pipeline events (such as Page_Load) or in a Global.asax event such as Application_Start.

Assign the MapPath result to a variable accessible from the Timer.Elapsed event, where you could use Path.Combine to get the location of a specific file you need.

It's possible to use HostingEnvironment.MapPath() instead of HttpContext.Current.Server.MapPath()

I haven't tried it yet in a thread or timer event though.


Some (non viable) solutions I considered;

  • The only method I care about on HttpServerUtility is MapPath. So as an alternative I could use AppDomain.CurrentDomain.BaseDirectory and build my paths from this. But this will fail if your app uses virtual directories (Mine does).

  • Another approach: Add all the paths I need to the the Global class. Resolve these paths in Application_Start.

I don't know if this will solve your virtual directories issue, but I use this for MapPath:

public static string MapPath(string path)
{
if (HttpContext.Current != null)
return HttpContext.Current.Server.MapPath(path);


return HttpRuntime.AppDomainAppPath + path.Replace("~", string.Empty).Replace('/', '\\');
}

HostingEnvironment isn't the perfect solution because it's a very difficult class to mock (see How to unit test code that uses HostingEnvironment.MapPath).

For those who need testability, a better way might be to create your own path-mapper interface as proposed by https://stackoverflow.com/a/1231962/85196, except implement it as

public class ServerPathMapper : IPathMapper {
public string MapPath(string relativePath) {
return HostingEnvironment.MapPath(relativePath);
}
}

The result is easily mockable, uses HostingEnvironment internally, and could even potentially address ase69s's concern at the same time.