Monday, December 26, 2011

Handle chart rendering on multiple instance of Azure webrole application

If you would like to know how chart rendering occurs with different ways in asp.net then this link is great one.

Generally when we add graph , at that time in web.config handlers will be added automatically as per below

  
<handlers>
<remove name="ChartImageHandler"/>
<add name="ChartImageHandler" preCondition="integratedMode" verb="GET,HEAD,POST" path="ChartImg.axd" type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler, System.Web.DataVisualization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</handlers>

<httpHandlers>
<add path="ChartImg.axd" verb="GET,HEAD,POST" type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler, System.Web.DataVisualization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/>
</httpHandlers>

and to store graph images in folder we need to put app-setting as per below
<add key="ChartImageHandler" value="deleteAfterServicing=false;Storage=file;Timeout=20;URL=~/TempCharts/; />"<validation validateIntegratedModeConfiguration="false"/>

in this case graph data point will be plotted and image will generated and store that in temp folder (as per appsetting)
and on request that image will be displayed on page .

Now this will work well in window azure up to 1 instance but when you will make more then 1 instance at that time sometimes graph will be empy. why?

because if one request store that image in first server of stack then another request want that graph from different server of stack and it can not able to find there!

So how we can handle this issue on multiple instance

simple solution is instead of temp location we need to use blob storage  provided by Azure.

generally chart control will use custom charthanlder for handling to store images of chart so we need to make custom chart handler that will store chart images in blob storage and from there it will be serve in all instances.

Just code your custom handler as per below


public class ChartImageHandler : IChartStorageHandler
{
CloudStorageAccount account;
CloudBlobClient client;
CloudBlobContainer ImageContainer;

public ChartImageHandler()
{
//Here you get the container to the CloudStorageAccount object
//The proper way to do this is with:
//account = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
//this is the Dirty way...

account = CloudStorageAccount.Parse("DefaultEndpointsProtocol=http;AccountName=<youraccountstorage>;AccountKey=<yourkey>");
client = account.CreateCloudBlobClient();


//The name of the image container is totatally up to you
ImageContainer = client.GetContainerReference("images");
//if it may not exist, you can use this call to create it....
ImageContainer.CreateIfNotExist();
}
#region IChartStorageHandler Members

public void Delete(string key)
{
CloudBlob image = ImageContainer.GetBlobReference(key);
image.Delete();
}

public bool Exists(string key)
{
CloudBlob image = ImageContainer.GetBlobReference(key);
try
{
image.FetchAttributes();
return true;
}
catch (StorageClientException e)
{
if (e.ErrorCode == StorageErrorCode.ResourceNotFound)
{
return false;
}
else
{
throw;
}
}
}

public byte[] Load(string key)
{
//sometimes load gets called before save is done

CloudBlob image = ImageContainer.GetBlobReference(key);
byte[] imageArray;
try
{
imageArray = image.DownloadByteArray();
}
catch (Exception e)
{
System.Threading.Thread.Sleep(1000);
imageArray = image.DownloadByteArray();

}
return imageArray;
}

public void Save(string key, byte[] data)
{
CloudBlob image = ImageContainer.GetBlobReference(key);
image.UploadByteArray(data);
}
#endregion
}


you need to add reference of this dll found in window azure SdK at C:\Program Files\Windows Azure SDK\v1.3\ref

Microsoft.WindowsAzure.StorageClient.dll

and in web.config just assign that handler in appsetting as per below


<add key="ChartImageHandler" value="deleteAfterServicing=false;Storage=file;Timeout=20;URL=~/TempCharts/;handler=MyApp.MyChartHandler.ChartImageHandler, MyApp;" />


No comments: