Tag Archives: Web programming

CSV Files in Asp.Net MVC

In Asp.Net MVC user interaction is organized around controllers and action methods. Controllers are classes that process incoming requests. Controllers define action methods. When a request is send to the server, MVC frameworks maps the request to an action method in the controller. Action method executes the logic and returns the appropriate result depending on the task it is performing. An action method typically returns a result type that is derived from ActionResult class. Controller class provides methods that return instances of these different types of derived classes e.g., View is the most commonly used method of the controller that returns the instance of ViewResult class, which renders a view as a web page.

imagesThere are many scenarios when we want users to download the files from the web application. Instead of returning the web pages we want to return the files from our action methods. Asp.Net MVC provides below three result types that can be used in action methods to return the files.

  • FileContentResult
  • FilePathResult
  • FileStreamResult

All the above result types derive from FileResult, which in turn derives from the ActionResult class. Although we can use the above classes to return different types of files but we we will create a new type that derives from FileResult, which will work will CSV files. There are many advantages to this approach. It will allow use to write some reusable code that we can use to generate any type of CSV file. Our action methods will be thin and let’s our code to follow the Open/Close principle.

When we write the new type, it needs to derive from FileResult class and override its WriteFile method. We will also add couple of methods to it so that code is better structured. The code of our new type will look like below.

    public class CsvFileResult<T> : FileResult
    {
        private readonly IEnumerable<T> _list;
        private readonly char _separator;

        public CsvFileResult(IEnumerable<T> list, string filename)
            : base("text/csv")
        {
            _list = list;
            _separator = ‘,’;
            FileDownloadName = filename;
        }

        protected override void WriteFile(HttpResponseBase response)
        {
            var outputStream = response.OutputStream;

            using (var memoryStream = new MemoryStream())
            {
                this.WriteData(memoryStream);
               
                outputStream.Write(
                    memoryStream.GetBuffer(),
                    0,
                    (int)memoryStream.Length);
            }
        }

        private void WriteData(MemoryStream memoryStream)
        {
            StreamWriter streamWriter = new StreamWriter(memoryStream);

            foreach (MemberInfo member in typeof(T).GetProperties())
            {
                string memberName = member.Name;

                Object[] displayAttributes = member
                    .GetCustomAttributes(typeof(DisplayAttribute), true);
               
                if (displayAttributes != null && displayAttributes.Length == 1)
                {
                    memberName = ((DisplayAttribute)displayAttributes[0]).Name;
                }

                streamWriter.Write(string.Concat(memberName, _separator));
            }

            streamWriter.WriteLine();

            foreach (T line in _list)
            {
                foreach (MemberInfo member in typeof(T).GetProperties())
                {
                    streamWriter.Write(
                        string.Concat(
                            this.GetPropertyValue(line, member.Name),
                            _separator));
                }

                streamWriter.WriteLine();
            }

            streamWriter.Flush();
        }

        private string GetPropertyValue(object item, string propName)
        {
            return item.GetType()
                .GetProperty(propName)
                .GetValue(item, null)
                .ToString() ?? "";
        }
    }

Let me explain the code here. The constructor takes the IEnumerable of a generic type, which allows us to create the CSV file from any type of list. As can be seen in the WriteData method, we are using reflection to read values dynamically from different properties of the list item. We are also passing content type as text/csv to the base constructor. Although there are constants defined for other content types in System.Net.Mime namespace but text/csv is not defined there so we are passing it as string.

In the WriteData method we are also adding the headings row in the file. The value of each column is derived from the Display attribute of the property. If Display attribute is not present then the name of the property is used by default. This allows us to write appropriate heading to the CSV file without hard coding them in the code e.g., if we are creating a CSV file from a list of Employee class, we can define the headings using the Display attribute as shown below.

    public class Employee
    {
        [Display(Name = "First Name")]
        public string Firstname { get; set; }

        [Display(Name = "Last Name")]
        public string Lastname { get; set; }

        [Display(Name = "Department")]
        public string Dept { get; set; }
    }

Finally, here is how we will use the new type in our action methods.

    public ActionResult EmployeeFile()
    {
        // Generate the list of employees
        IEnumerable<Employee> emps = GetEmps();

        return new CsvFileResult<Employee>(emps, "emps.csv");
    }