Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Charting Control no longer working with .NET 4

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.

like image 521
Oundless Avatar asked Apr 13 '10 16:04

Oundless


3 Answers

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)$" });
    ...
}
like image 184
Kevin Avatar answered Nov 18 '22 02:11

Kevin


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;
           }
       }
    }
like image 43
Michael Ferrante Avatar answered Nov 18 '22 02:11

Michael Ferrante


Thanks for your answers, but I don't think mine was an IIS6/IIS7 problem.

I traced it to the fact that the default value for ImageStorageMode on a ChartControl has changed from UseImageLocation to UseHttpHandler. My ChartControl now has some extra attributes and all works fine.

<asp:Chart ... ImageStorageMode="UseImageLocation" ImageLocation="/Temp/ChartPic_#SEQ(300,3)">

I had to also change the ImageLocation to be non-relative (by adding /Temp/) as that also caused a problem when iterating over the ChartControl's DataPoints in some code-behind.

like image 2
Oundless Avatar answered Nov 18 '22 02:11

Oundless