My last post dealt with emitting CSV files from an IEnumerable<T>. I thought I’d extend the idea a bit by using an extension method on IEnumerable<T> as a means of making the functionality portable without the use of a “helper method.”

Here is the extension method:

public static byte[] GenerateCSVDownload<T>(this IEnumerable<T> data, 
				string[] headers, Dictionary<int, Func<T, object>> columnData)
{
	Func<object, string> csvFormatter = (field) => String.Format("\"{0}\"", field);
	Action<IEnumerable<object>, StringBuilder> rowBuilder = (rowData, fileStringBuilder) =>
	{
		fileStringBuilder.AppendFormat("{0}\n", String.Join(",", rowData.Select(r => csvFormatter(r.ToString())).ToArray()));
	};

	StringBuilder builder = new StringBuilder();
	rowBuilder(headers, builder);

	foreach (T entityObject in data)
	{
		List<object> row = new List<object>();
		for (int i = 0; i < columnData.Keys.Count; i++)
		{
			row.Add(columnData[i](entityObject));
		}
		rowBuilder(row, builder);
	}

	return System.Text.Encoding.UTF8.GetBytes(builder.ToString());
}

And usage:

var people = new List<Person>() { 
                new Person(){ FirstName = "Stan", LastName = "Lee"}, 
                new Person(){ FirstName = "Jack", LastName = "Kirby"}, 
                new Person(){ FirstName = "Alex", LastName = "Ross"},
                new Person(){ FirstName = "Adi", LastName = "Granov"}
        };

var bytes = people.GenerateCSVDownload(
			new string[] { "First Name", "Last Name" },
                        new Dictionary<int, Func<Person, object>>() { 
                            {0, per => per.FirstName},
                            {1, per => per.LastName}
                        });


I will reiterate from my previous post: this sketch is just about the technique. For large amounts of data you'd be better off chunking the data out. You could also abstract the method a bit to deal with different kinds of output (string, file, etc) by just accepting or returning a stream.