Archive for c#

Quick and dirty reporting with the Google Chart API

On a recent project, I need a quick and dirty report that would give a breakdown of statistics for orders that have been placed. I didn’t need any fancy drill down, hover help, or transition effects. This sounds like a perfect use for the Google Chart API! There is no limit on use. All you have to do is construct a URL containing your data, and that is the URL for an image containing your chart. Here is a sample of a chart that we’re going to generate in this post:

Pie Sample

First, we need a quick way to generate the data. I used a SQL Server stored procedure, because I knew it would only take a matter of minutes to create. Here is an example of how I retrieved the data for 2 report sections:

--Paper Breakdown
Select IsNull(p.PaperName + ' (' + Cast(Count(o.PaperId) as VarChar(Max)) + ')', 'Unspecified'),
	count(o.PaperId)
From Orders o
Left Outer Join Papers p on o.PaperId = p.Id
Group By p.PaperName
Having Count(o.PaperId) > 0
--Style Breakdown
Select IsNull(s.Sku + ' (' + Cast(Count(o.StyleId) as VarChar(Max)) + ')', 'Unspecified'),
	count(o.StyleId)
From Orders o
Left Outer Join Styles s on o.StyleId = s.Id
Group By s.Sku
Having Count(o.StyleId) > 0

Then, I simply had to call the stored procedure, and put the data into a DataSet called "_data" (code not shown). Then, I wrote a quick method to get the URL for a Google chart:

private static string getPieChartUrl(DataTable dt)
{
	if (dt.Rows.Count == 0)
		return "";
	StringBuilder sb = new StringBuilder();
	sb.Append("http://chart.apis.google.com/chart?chs=600x200&cht=p"); //Base URL
	//Pie slice data
	StringBuilder sliceData = new StringBuilder();
	foreach (DataRow currRow in dt.Rows)
		sliceData.Append(currRow[1] + ",");
	sliceData.Remove(sliceData.Length - 1, 1); //Remove the trailing comma
	sb.AppendFormat("&chd=t:{0}", sliceData);
	//Pie label data
	StringBuilder labelData = new StringBuilder();
	foreach (DataRow currRow in dt.Rows)
		labelData.Append(currRow[0] + "|");
	labelData.Remove(labelData.Length - 1, 1); //Remove the trailing comma
	sb.AppendFormat("&chl={0}", labelData);
	return sb.ToString();
}

My ASPX page then has 2 image controls:

<h2>Breakdown by Paper Type</h2>
<asp:Image runat="server" ID="imgPiePapers" />
<h2>Breakdown by Style SKU</h2>
<asp:Image runat="server" ID="imgPieStyles" />

Then, simply wire up the data:

imgPiePapers.ImageUrl = getPieChartUrl(_data.Tables[0]);
imgPieStyles.ImageUrl = getPieChartUrl(_data.Tables[1]);

Creating user friendly strings for enum values

In this post, I’ll show you how to create a custom attribute for enum values to assign extra information to an enum. In this case, we’ll be associating a user friendly display name.

Order Status Screenshot

In .NET, enums are basically a list of constant values. It’s a way to assign code friendly names to a group of values. For example, an order status:

enum OrderStatus
{
	NewOrder = 1,
	Processing = 2,
	Shipped = 3
}

In the above example, the numeric values are optional, they will be assigned automatically if you don’t supply them.

What if you want to display the enum values in a drop down list, so that an admin can change the status of an order? One option is to loop through the enum, and display the code value ("NewOrder, "Processing", etc.). Another option is to have a "switch" statement or a "for" statement that allows you to hard code a user friendly value.

Another option is to create a custom attribute to add additional meta data:

public class EnumValueDataAttribute : Attribute
{
	private string _name;
	public string Name
	{
		get{ return _name; }
		set{ _name = value; }
	}
}

Now we can use the attribute to the enum I showed you previously:

enum OrderStatus
{
	[EnumValueData(Name="New Order")]
	NewOrder = 1,
	[EnumValueData(Name="In Process")]
	Processing = 2,
	[EnumValueData(Name="Shipped")]
	Shipped = 3
}

Now we can create a method that gets the data in a useful format:

public static Dictionary<int, string> GetListItems(Type enumType)
{
	if (!enumType.IsEnum)
		throw new ApplicationException("GetListItems does not support non-enum types");
	Dictionary<int, string> list = new Dictionary<int, string>();
	foreach(FieldInfo field in enumType.GetFields(BindingFlags.Static | BindingFlags.GetField | BindingFlags.Public))
	{
		int value;
		string display;
		value = (int)field.GetValue(null);
		display = Enum.GetName(enumType, value);
		foreach(Attribute currAttr in field.GetCustomAttributes(true))
		{
			EnumValueDataAttribute valueAttribute = currAttr as EnumValueDataAttribute;
			if (valueAttribute != null)
				display = valueAttribute.Name;
		}
		list.Add(value, display);
	}
	return list;
}

Notice that the method will also work even if the "EnumValueData" attribute is missing, so you don’t have to supply it for every value.

Based off of this code, you could easily create additional fields to associate with enum values. Basically what we’re doing is creating a simple database table in our code. You’ll have to decide if this is a good idea for your project.

LINQ, I’m not ready for you just yet

Today I was between features on the current project I’m working on, so I had some free time start researching some technologies I’ve been meaning to learn and start using. The topics at the top of my learning list are LINQ and MVC. I gave LINQ a few months to mature, so I figured it was a good time to investigate.

Boy Crying

(there is no emoticon to express my anger!)

The picture above shows how I felt when I started writing my first LINQ expression. The biggest problem was the fact that the latest version of ReSharper doesn’t support any .NET 3.0+ language features. Not only does it not support LINQ, it’s IntelliSense severely interrupts you while writing it. So much so that it makes it unusable.

I went ahead and downloaded the latest development build (Build 783). On their download page, it’s listed as "Works here". That wasn’t encouraging. It does work a little better with LINQ, but it’s still a steaming pile of you know what (dog poop for the not-so-smart). This further reinforces my love/hate relationship with ReSharper.

Anyway, I was eventually able to write some LINQ code. A great tool to get started is LinqPad, which is basically a query analyzer but with LINQ expressions. Writing LINQ is very difficult with a SQL background, because everything is backwards. You think you know what you’re doing, but you don’t.

Right now, we’re using NHibernate in the main project that I’ve been working on for the past couple of months. It’s amazing, but there are a couple of things that would be nice:

  • Better optimization of queries - It looks like LINQ does an amazing job with this.
  • Batched reads & writes - LINQ does batched writes, but lazy loading by default. Maybe not as big a deal as I think.
  • Cross session saving - I spent hours battling with some code that loaded a complex object with relationships in one session, and then saved them in another. It appears that LINQ solves this, but I’ll have to run some tests to be sure.
  • Less work generating mapping files and relationships.

One thing that is nice about LINQ to objects is the fact that it will generate all of the model classes, plus the glue that connects the model to the database. You can either use Visual Studio and drop the tables into a mapping file, or you can use SqlMetal to script the class generation.

One of the biggest questions I’m trying to answer write now, is how unit testing fits in with LINQ. We’re currently testing our data access layer by using an in memory SQLite database, which let’s us perform close to real world saves and loads. We also use interfaces for our data access methods, which makes it easy to create testable classes that can simply be supplied a database interface.

I’m also not sure if it even makes sense to put my LINQ queries in a data access layer. The code would almost seem trivial, and would just create a lack of flexibility. Ironically, it almost feels like you should use LINQ to query against your data access layer.

For now, there are more questions than answers. For now, I don’t plan on retrofitting my last project with LINQ, but I’m going to investigate if it will be a good foundation for the data access logic in my next project. Of course if I go that route, you’ll be sure to hear about it!

Reading an octet stream post in ASP.NET

I’m using Adobe Flex to take a snapshot of some controls. I turn the PNG encoded bitmap data into a ByteArray. I then send the data to the server, through an ASP.NET page. On the Flex side of things, I send the data over with the following code:

var req:URLRequest = new URLRequest();
req.method = URLRequestMethod.POST;
req.data = snapshot.data;
req.contentType="application/octet-stream";
req.url = "snapshotuploadhandler.aspx";

var loader:URLLoader = new URLLoader;
loader.load(req);

I assumed this code was incorrect, but it was actually the code in the receiving page. Instead of checking the "Files" property on the request object, I needed to read the InputStream on the request. Here is the working code:

if (Request.ContentLength > 0)
{
    byte[] buffer = new byte[Request.ContentLength];
    using (BinaryReader br = new BinaryReader(Request.InputStream))
        br.Read(buffer, 0, buffer.Length);
    return buffer;
}

Disabling GridView Validation

If you want to put a gridview on a form with validators, you’ll run into validation issues very quickly. The LinkButton controls cause the page validation. Immediately I looked for a "ValidationGroup" property on the GridView, but unfortunately, none exists.

Datagrid-ValidationGroup-Example

 

The solution is to handle the "DataBound" event on the GridView, and manually find the link buttons. The following code loops through the controls in the first cell of the row currently being edited.

protected void Page_Load(object sender, EventArgs e)
{
    dgMyGrid.DataBound += dgMyGrid_DataBound;
}

private void dgMyGrid_DataBound(object sender, EventArgs e)
{
    if (dgMyGrid.EditIndex >= 0)
    {
        //Turn off validation on the link buttons, because we don't want other page validation issues interfering.
        foreach (Control currControl in dgMyGrid.Rows[dgMyGrid.EditIndex].Cells[0].Controls)
        {
            LinkButton lb = currControl as LinkButton;
            if (lb != null)
                lb.ValidationGroup = "no-validation";
        }
    }
}