Jun 28

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" };
}

Tags:

Comments

balanzza luggage scales

Posted on Monday, 5 July 2010 18:35

You need to needed to export some data into a CSV file?? there are so many tutorial..

softlion

Posted on Sunday, 11 July 2010 07:37

If you use this model, the header names are not in the correct order !

    public class UserForExport
    {
        public string Email { get; set; }
        public string Alias { get; set; }
        public bool AcceptNewsFeeds { get; set; }
    }

Any fix ?

softlion

Posted on Sunday, 11 July 2010 07:39

Sorry i messed up

erhu music

Posted on Friday, 16 July 2010 21:26

i wondered why the headers were not working, thanks guys.

Dissertation help

Posted on Saturday, 17 July 2010 01:59

Its great to see that people are sharing quite profitable information with each other and now we can move our selves to a new era.

singing software

Posted on Saturday, 17 July 2010 17:37

Very informative post. Ill check back some time for updates keep up the good work

Comments are closed