views:

426

answers:

2

I've just upgraded to .NET 4 and my ASP.NET Chart Control no longer displays.

For .NET 3.5, the HTML produced by the control used to look like this:

<img id="20_Chart" src="/ChartImg.axd?i=chart_5f6a8fd179a246a5a0f4f44fcd7d5e03_0.png&amp;g=16eb7881335e47dcba16fdfd8339ba1a" alt="" style="height:300px;width:300px;border-width:0px;" />

and now, for .NET 4, it looks like this (note the change in the source path):

<img id="20_Chart" src="/Statistics/Summary/ChartImg.axd?i=chart_5f6a8fd179a246a5a0f4f44fcd7d5e03_0.png&amp;g=16eb7881335e47dcba16fdfd8339ba1a" alt="" style="height:300px;width:300px;border-width:0px;" />

The chart is in an MVC partial view that is in an MVC Area folder called "Statistics" and a MVC Views folder called "Summary" (i.e. "/Areas/Statistics/Views/Summary"), so this is obviously where the change of path is coming from.

All I've done is to switch the System.Web.DataVisualization assembly from, 3.5 to 4.0.

Any help greatly appreciated.

+3  A: 

We had this same problem on IIS 6 after upgrading from ASP.NET 3.5 to ASP.NET 4.0 with ASP.NET MVC. Everything was working fine on IIS 7, but IIS 6 gave us a problem.

The problem was that the HttpContext.Current.Request.CurrentExecutionFilePath property gave a different result in IIS 6 and IIS 7:

  • Url: /Controller.mvc/Action/1/2
  • IIS 6: /Controller.mvc/Action/1/2
  • IIS 7: /Controller.mvc

Which resulted in Urls for the charts like:

  • IIS 6: /Controller.mvc/Action/1/ChartImg.axd?i=chart_...
  • IIS 7: /ChartImg.axd?i=chart_...

The ChartHttpHandler has got a function in there that calculates the path based off the HttpContext.Current.Request.CurrentExecutionFilePath:

private static string GetHandlerUrl()
{
    string str = Path.GetDirectoryName(HttpContext.Current.Request.CurrentExecutionFilePath ?? "").Replace(@"\", "/");
    if (!str.EndsWith("/", StringComparison.Ordinal))
    {
        str = str + "/";
    }
    return (str + "ChartImg.axd?");
}

The way that the ASP.NET UrlRewriting was working, since the paths to ChartImg.axd still had .mvc in them, the MVC handler was getting invoked instead of the Chart handler.

There were 3 ways we found to deal with it (see below for details):

  1. Add an explicit script map for ".mvc" to the ASP.NET 4.0 dll
  2. Add some additional ignore routes to the route table to cover permutations
  3. Override the Execute() of Controller and put in a redirect back to /ChartImg.axd

(1) Turns out that if we added a script map for .mvc via IIS 6.0 for .mvc the Request.CurrentExecutionFilePath would get calculated as the root path how we wanted it instead of as the deeper path

  • IIS 6.0 Manager
  • Properties -> Home Directory -> Configuration
  • Mappings tab
  • Executable: c:\winnt\microsoft.net\framework\v4.0.30319\aspnet_isapi.dll, Extension: .mvc

(2) We found that adding some route table entries would work, but we had to account for all depths possible in the paths to get ASP.NET MVC to ignore the ChartImg.axd if it were deeply embedded in the path and not at the root:

RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
RouteTable.Routes.IgnoreRoute("{a}/{resource}.axd/{*pathInfo}");
RouteTable.Routes.IgnoreRoute("{a}/{b}/{resource}.axd/{*pathInfo}");
RouteTable.Routes.IgnoreRoute("{a}/{b}/{c}/{resource}.axd/{*pathInfo}");
RouteTable.Routes.IgnoreRoute("{a}/{b}/{c}/{d}/{resource}.axd/{*pathInfo}");

(3) By overriding the Execute() on all our controllers by making a base controller that all our controllers inherit from, we could globally override the Execute() to account for this situation and redirect to /ChartImg.axd

   public partial class MyController: Controller
   {
      protected override void Execute(RequestContext cc)
       {
           // the url for chartimg.axd to be in the application root.  /Controller.mvc/Action/Param1/ChartImg.axd gets here first,
           // but we want it to go to /ChartImg.axd, in which case the IgnoreRoute does work and the chart http handler does it's thing.
           if (cc.HttpContext.Request.Url.AbsoluteUri.Contains("ChartImg.axd"))
           {
               var url = new UriBuilder(cc.HttpContext.Request.Url);
               url.Path = "/ChartImg.axd";
               cc.HttpContext.Response.Redirect(url.ToString());
               return;
           }
       }
    }
Michael Ferrante
+2  A: 

While @Michael's solution is informative on why this problem exists, there is a simpler solution. When registering the routes in your controllers handle in global.asax.cs, you could add an ignored route with a contstraint, as follows:

protected void Application_Start() {
    ...
    RouteTable.Routes.Ignore("{*pathInfo}", new { pathInfo = @"^.*(ChartImg.axd)$" });
    ...
}
Kevin
Michael Ferrante's answer is excellent - but I cannot up-vote this solution enough... cheers Kevin
David A Gibson