If I had a nickel for every time I had to export data in CSV format… I would be pretty poor since as often as it does happen it is my fulltime job to get it done. One of my claims to fame longevity is an old post on the joel on software discussion boards asking about a standard for the CSV format (there is none). Anyway, here is a recent incarnation that is a bit more flexible since I can pass any IEnumerable<T> to it:

protected byte[] GenerateCSVDownload<T>
(
					IEnumerable<T> linqSource, 
					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 linqSource){
		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());
}


Here is an example of usage:

// 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"}
		};
// returns file contents
var fileBytes = GenerateCSVDownload<Person>(
						people,
						new string[] { "First Name", "Last Name" },
						new Dictionary<int, Func<Person, object>>() { 
							{0, per => per.FirstName},
							{1, per => per.LastName}
						}
);


Some final things to note: