Recently, I needed to export some data into a CSV file. Curiously, there is no CSV support in Asp.Net MVC and writing your custom ActionResult is quite simple.
Here is mine, if this can save you some times:
|
/// <summary> /// ActionResult specialized in CSV file. /// </summary> public sealed class CSVResult<T> : System.Web.Mvc.FileResult { private IEnumerable<T> collection; private String[] columnsToExclude;
public CSVResult(IEnumerable<T> collection, params String[] columnsToExclude) : base("text/csv") { this.collection = collection; this.columnsToExclude = columnsToExclude; }
protected override void WriteFile(HttpResponseBase response) { System.IO.Stream outputStream = response.OutputStream; using (MemoryStream mstream = new MemoryStream()) { WriteObject(mstream); outputStream.Write(mstream.GetBuffer(), 0, (int) mstream.Length); } }
private void WriteObject(Stream stream) { // We will follow the recommandations stated in this article // http://www.commentcamarche.net/faq/sujet-7273-exporter-a-coup-sur-du-csv // - embedding all values in quotes // - double quote literal values // - add \n\r between each lines.
StreamWriter writer = new StreamWriter(stream, System.Text.Encoding.Default);
// Render columns Type modelType = typeof(T); List<ModelMetadata> metadatas = ModelMetadataProviders.Current.GetMetadataForProperties(null, modelType).ToList();
for(int i=0; i<metadatas.Count; ) { if (Array.IndexOf<String>(columnsToExclude, metadatas[i].PropertyName) != -1) metadatas.RemoveAt(i); else { WriteValue(writer, metadatas[i].DisplayName ?? metadatas[i].PropertyName); i++; } }
writer.WriteLine(); // Render data var en = collection.GetEnumerator(); while (en.MoveNext()) { ModelMetadata mprop = ModelMetadataProviders.Current.GetMetadataForType(() => en.Current, modelType);
foreach(ModelMetadata prop in mprop.Properties) { WriteValue(writer, prop.SimpleDisplayText ?? String.Empty); } writer.WriteLine(); }
writer.Flush(); }
private static void WriteValue(StreamWriter writer, String literal) { // Enclose values in quote writer.Write("\""); string line = literal.Replace("\"", "\"\""); writer.Write(line); writer.Write("\";"); } }
|
Usage:
We have a simple ViewModel that contains the Password property for import usage but it is hide in export context.
This is the purpose of the columnsToExclude parameter.
|
ViewModel
|
|
/// <summary> /// Represents a user as it stands in import/export CSV. /// </summary> public class CSVUserModel { [Display(Order = 0)] public String Account { get; set; }
[Display(Order = 1)] public String FullName { get; set; }
[Display(Order = 2)] public String Password { get; set; }
[Display(Order = 3)] public String Email { get; set; }
[Display(Order = 4)] public Boolean IsAdmin { get; set; }
[Display(Order = 5)] public Boolean IsActive { get; set; } }
|
|
Your controller:
|
|
...
public ActionResult ExportCSV() { var users = storeDB.Users; List<CSVUserModel> exportedUser = new List<CSVUserModel>();
foreach (var user in users) { MembershipUser fbaUser = Membership.GetUser(user.FbaUserId); exportedUser.Add(new CSVUserModel { Account = fbaUser.UserName, FullName = user.Name, Email = user.Email, IsAdmin = user.IsAdmin, IsActive = user.IsActive }); }
return new CSVResult<CSVUserModel>(exportedUser, "Password") { FileDownloadName = "users.csv" }; }
|