ASHX to create graphs via graphviz

http://www.graphviz.org/ - tools to draw graphs from text.

Here is sample file test.dot:

digraph G {
	"A"->"B"
	"C"->"A"
	"D"->"C"
	"E"->"C"
	"F"->"D"
}

Now you can run:

dot test.dot -Tpng > test.png

To generate png image from it.

Here is sample code to do it in ashx:

<%@ WebHandler Language="C#" Class="dot" %>

using System;
using System.Web;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Runtime.Serialization.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;

public class dot : IHttpHandler
{
	public void ProcessRequest(HttpContext context)
	{
		context.Response.ContentType = "image/png";

		try
		{
			if (String.IsNullOrEmpty(context.Request.Params["ID"])) throw new Exception("ID is required");

			context.Response.BinaryWrite(Dot2Png(DataSet2Dot(GetData(int.Parse(context.Request.Params["ID"])))));
		}
		catch (Exception ex)
		{
			context.Response.BinaryWrite(GenerateMessage(ex.Message));
		}
	}

	protected byte[] GenerateMessage(string message)
	{
		Bitmap objBmpImage = new Bitmap(1, 1);
		int intWidth = 0;
		int intHeight = 0;
		Font objFont = new Font("Arial", 13, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Pixel);
		Graphics objGraphics = Graphics.FromImage(objBmpImage);
		intWidth = (int)objGraphics.MeasureString(message, objFont).Width;
		intHeight = (int)objGraphics.MeasureString(message, objFont).Height;
		objBmpImage = new Bitmap(objBmpImage, new Size(intWidth, intHeight));
		objGraphics = Graphics.FromImage(objBmpImage);
		objGraphics.Clear(Color.White);
		objGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
		objGraphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
		objGraphics.DrawString(message, objFont, new SolidBrush(Color.FromArgb(102, 102, 102)), 0, 0);
		objGraphics.Flush();

		MemoryStream mem = new MemoryStream();
		objBmpImage.Save(mem, ImageFormat.Png);

		return mem.ToArray();
	}

	protected byte[] Dot2Png(string dot)
	{
		string file = Path.GetTempFileName();
		string png = Path.GetTempFileName();
		string command = @"""C:\Program Files (x86)\Graphviz2.36\bin\dot.exe"" " + file + " -Tpng > " + png;

		File.WriteAllText(file, dot);

		System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
		procStartInfo.RedirectStandardOutput = true;
		procStartInfo.UseShellExecute = false;
		procStartInfo.CreateNoWindow = true;
		System.Diagnostics.Process proc = new System.Diagnostics.Process();
		proc.StartInfo = procStartInfo;
		proc.Start();
		string result = proc.StandardOutput.ReadToEnd();

		byte[] bytes = File.ReadAllBytes(png);

		File.Delete(png);
		File.Delete(file);

		return bytes;
	}

	protected string DataSet2Dot(DataSet ds)
	{
		List<string> items = new List<string>();

		foreach (DataRow edge in ds.Tables[1].Rows)
		{
			string ParentName = "";
			string ChildName = "";

			foreach (DataRow node in ds.Tables[0].Rows)
			{
				if (node["ID"].ToString() == edge["ParentID"].ToString())
				{
					ParentName = node["Name"].ToString();
				}
				else if (node["ID"].ToString() == edge["ChildID"].ToString())
				{
					ChildName = node["Name"].ToString();
				}
			}

			items.Add(string.Format("\t\"{0}\"->\"{1}\"", ParentName, ChildName));
		}

		return string.Format($"digraph G { { {0} {1} {0} } } {0}", Environment.NewLine, string.Join(Environment.NewLine, items.ToArray()));
	}

	/// <summary>
	/// Retrieve dataset with two tables.
	///
	/// Table[0] (ID, Name)
	/// Table[1] (ParentID, ChildID)
	/// </summary>
	/// <param name="ID"></param>
	/// <returns></returns>
	protected DataSet GetData(int ID)
	{
		DataSet ds = new DataSet();
		using (var conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["RabotaUA"].ToString()))
		using (
			var command = new SqlCommand("spTagMaster_Structure", conn)
			{
				CommandType = CommandType.StoredProcedure
			})
		{
			conn.Open();
			command.Parameters.Add(new SqlParameter("@ID", ID));
			SqlDataAdapter da = new SqlDataAdapter();
			da.SelectCommand = command;
			da.Fill(ds);
			conn.Close();
		}
		return ds;
	}

	public bool IsReusable
	{
		get
		{
			return false;
		}
	}
}

This code expects to retrieve two tables from spTagMaster_Structure stored procedure, one of which should have ID, Name columns and second - ParentID, ChildID.