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

Writing Better Code

Writing high quality has always been a challenge. It is one of those things that needs continuous focus through the project’s lifecycle. The intent of this article is to understand the code quality and help project teams to make progress on improving their code’s quality. The quality of the software has below two aspects.

imagesFunctionality
It includes the functional completeness and correctness, performance efficiency, availability, compatibility, security, usability and UI aesthetics.

Software Structure
This is about the quality of the software structure in particular, which includes modularity, readability, testability, modifiability and flexibility. This is fairly involved subject and I am trying to take a very simplistic approach to improve this aspect of the code quality.

Below are the brief encapsulations of the six principles to improve the code quality.

Learn to write good code: First thing that we should do to improve the quality of the code is to help team members to learn about the basics of software construction. It is very important that developers understand the principles of object-oriented programming and design like SOLID principles, design patterns and how to write clean code.

Build coding standards: We must agree on coding standards within a team so that code is consistent, which will help one developer to understand the code of other developer. We can agree on few items and make them mandatory items whereas rest of them can be categorized as recommended items. A checklist can be created for repeated issues. We can also use static analysis tools like Fxcop and NDepend, which help us to detect code violations and dependencies.

TDD, ATDD and Continuous Integration: It is important to unit test the code and make sure it runs as expected. We should cover as much code as possible by unit test, which helps us to add new code without breaking any existing functionality.

TDD (Test Driven Development) is a process of writing tests first and then writing code to pass those tests. TDD focuses on testing low-level functionality and is a primary developer tool to help create written unit of code like function or class.

ATDD (Acceptance Test Driven Development) is next level up from the unit test based TDD. TDD is about building code right whereas ATDD is building right code. In ATDD criteria specified by the customer are automated into the acceptance tests. In ATDD, the focus is on user stories, which describe the functionality that the customer cares about. Although it is possible to take an ATDD approach using a unit-test framework like MSTest, however unlike TDD, ATDD is aimed mainly for the non-programmer stakeholders so it is better to use specialized tools like FitNesse that can be easily used by those people to define the user stories in a precise way using a non-programming language. It also helps to get transparency about the state of the project as it simple to run all the tests and see exactly what functionality is already implemented and which still isn’t.

Continuous integration is very valuable practice that helps developers to detect and fix integration problems continuously. It ensure code builds and doesn’t break anything that already exists. Current build is constantly made available for testing, which helps developers to get immediate feedback on quality and functionality. When coupled with TDD and ATDD created automated tests it become much more valuable by immediate testing of all the changes.

Lightweight Code Reviews: We work hard at writing quality code but issues still slip in and sometimes it is very hard to catch some of these issues beforehand like improperly implemented requirements, poorly designed interfaces, etc. Inspecting the code and thinking about it in the context of requirements requires a human brain. Unlike formal code inspections, lightweight code reviews require less overhead and have been found as effective as heavyweight reviews. Code reviews can be conducted by sitting with the developer or with the help of source code managements systems like Visual Studio or specialized code review tools.

Awareness in the team: There should be awareness within the team. For example, if one developer fixes an issue, creates a utility or writes some reusable code, he should let other team members know so that they do not repeat the mistake or try to reinvent the wheel. Many distributed teams prefer to create a wiki for sharing the information whereas other use the mailing list because it is easier to go through an email than a long document.

Refactoring: No developer introduces smells in code intentionally. There can be many reasons for these bad smells for example, missing quality mindset or guidance, time pressure, lack of awareness of right approach or lack of skill in a tool. Whatever may be the reason for bad smells, if they are not taken care of first time we need to revisit the code second time and fix them. Below are the common problems that we must look for.

   · Inconsistency
   · Duplication
   · Code at wrong level of abstraction
   · Magic Numbers
   · High depth of inheritance tree
   · High Coupling
   · Lack of cohesion of methods
   · Very large methods
   · High cyclomatic complexity
   · Too many arguments
   · Obscured Intent
   · Non-descriptive names
   · Dead code

This is not an exhaustive list, just the ones that I think are most important to improve the code quality. If you have any more suggestions please add the comment.

ISO Date/Time in IE8

ISO 8601 is the international standard for the representation for the dates and times, which provides unambiguous representation of dates and times. It organizes date and time values from the most to the lease significant: year, month or week, day, hour, minute, second and fraction of second.

fallback

It allows two ways to communicate time based information across time zones:

1. In UTC with a special designator (Z)
2. By attaching Offset to local time (+hh:mm or -hh:mm)

 

Below are the formats of date and time expressed in ISO 8601.

Year (YYYY): It is represented as four digits from 0000 to 9999 (eg 2010).

Year and month (YYYY-MM): MM indicates a two-digit month of the year, 01 through 12 (eg 2010-03).

Date (YYYY-MM-DD): DD indicates a two-digit day of that month, 01 through 31 (eg 2010-03-13).

Week (YYYY-Www): W appears literally in the string to indicate week whereas ww is the week number from 01 to 53 (eg 2010-W17).

Date with week number (YYYY-Www-D): D indicates the week day from 1 through 7 (eg 2010-W17-3).

Date and time (YYYY-MM-DDThh:mm:ssTZD): T appears literally in the string and indicates the beginning of the time. hh refers to hour between 00 to 24, mm refers to minutes from 00 to 59 and ss refers to seconds from 00 to 60 where 60 is used only to denote the leap second.

TZD is the time zone designator, it is Z when time is represented in UTC. In case of offset to UTC it can be +hh:mm or -hh:mm (eg 2010-03-13T11:10:05Z or 2010-03-13T11:10:05+05:30 or 2010-03-13T11:10:05-04:30).

Although ISO 8601 provides a well defined method to represent date and time but unfortunately it is not supported in all the browsers. If any of user is using your app in IE8 or below you will have already discovered that ISO standard timestamps are not supported in IE8. Sometimes we have no control over the date format e.g. if you using REST services of SharePoint 2013 from IE8, which returns the date and time in standard ISO timestamp, There is no option but to shim it.

The below JavaScript code creates a shim. It defines a Date.fromString function, which checks if date is a standard ISO timestamp and whether browser can parse it or not. If browser is not able to parse the date correctly then it uses the custom logic otherwise the native code is used.

var d = window.Date,
     regexIso8601 = /^(\d{4}|\+\d{6})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})(\.(\d{1,3}))?(?:Z|([\-+])(\d{2}):(\d{2}))?)?)?)?$/,
     lOff,
     lHrs,
     lMin;

if (d.parse(‘2011-11-29T15:52:30.5′) !== 1322599950500 ||
    d.parse(‘2011-11-29T15:52:30.52′) !== 1322599950520 ||
    d.parse(‘2011-11-29T15:52:18.867′) !== 1322599938867 ||
    d.parse(‘2011-11-29T15:52:18.867Z’) !== 1322581938867 ||
    d.parse(‘2011-11-29T15:52:18.867-03:30′) !== 1322594538867 ||
    d.parse(‘2011-11-29′) !== 1322524800000 ||
    d.parse(‘2011-11′) !== 1320105600000 ||
    d.parse(‘2011′) !== 1293840000000) {

    d.__parse = d.parse;
    lOff = -(new Date().getTimezoneOffset());
    lHrs = Math.floor(lOff / 60);
    lMin = lOff % 60;

    d.parse = function (v) {
        var m = regexIso8601.exec(v);

        if (m) {
            return Date.UTC(
            m[1],
            (m[2] || 1) – 1,
            m[3] || 1,
            m[4] – (m[8] ? m[9] ? m[9] + m[10] : 0 : lHrs) || 0,
            m[5] – (m[8] ? m[9] ? m[9] + m[11] : 0 : lMin) || 0,
            m[6] || 0,
            ((m[7] || 0) + ’00’).substr(0, 3) );
        }

        return d.__parse.apply(this, arguments);
    };
}

d.__fromString = d.fromString;

d.fromString = function (v) {
    if (!d.__fromString || regexIso8601.test(v)) {
        return new d(d.parse(v));
    }

    return d.__fromString.apply(this, arguments);
};

We can wrap the above code in a JavaScript file and load that in the main page and whenever we need to parse the data from our code we will always use the below method to get the date correctly.

var utcDateString = ‘2011-11-29T15:52:18.867Z';
var parsedDate = Date.fromString(utcDateString);

Follow

Get every new post delivered to your Inbox.