C#: Export a List of Objects to CSV with LINQ, Attributes, and Generics

Have you ever needed to transform data from in-memory C# objects to a CSV flat-file of a specific format?  Here’s an easy way to get the job done with C#, LINQ, and Generics.

First you need the Attribute you will use to mark up the properties on your exportable class:

/// <summary>
/// The name of the column for the CSV
/// generated by a list of objects with
/// properties marked with this attribute,
/// if Export is true.  Uses Order to order
/// the properties on export
/// </summary>
public class CsvColumnNameAttribute : Attribute
{
    public bool Export { get; set; }
    public int Order { get; set; }
    public string Name { get; set; }

    public CsvColumnNameAttribute()
    {
        Export = true;
        Order = int.MaxValue; // so unordered columns are at the end
    }

}

And here is how you use the attributes to mark up your class so you can export a list of instantiated objects:

public class MyClassToExport
{
    [CsvColumnAttribute(Name = "Activation Date", Order = 1)]  
    public DateTime Date { get; set; }
   
    [CsvColumnAttribute(Name = "Full Name", Order = 2)]
    public string User { get; set; }
   
    [CsvColumnAttribute(Name = "Account Type", Order = 3)]
    public int Level { get; set; }
   
    [CsvColumnAttribute(Name = "Account Action", Order = 4)]
    public string Action { get; set; }
   
    [CsvColumnAttribute(Export = false)]
    public DateTime Added { get; set; }
}

Then you need to code to actually create the CSV using LINQ and Generics:

/// <summary>
/// Generate a CSV as a string from a list
/// of objects that have the CsvColumnNameAttribute
/// applied
/// </summary>
public string GetCsv<T>(List<T> csvDataObjects)
{
    PropertyInfo[] propertyInfos = typeof(T).GetProperties();
    var sb = new StringBuilder();
    sb.AppendLine(GetCsvHeaderSorted(propertyInfos));
    csvDataObjects.ForEach(d => sb.AppendLine(GetCsvDataRowSorted(d, propertyInfos)));
    return sb.ToString();
}

private string GetCsvDataRowSorted<T>(T csvDataObject, PropertyInfo[] propertyInfos)
{
    IEnumerable<string> valuesSorted = propertyInfos
        .Select(x => new
        {
            Value = x.GetValue(csvDataObject, null),
            Attribute = (CsvColumnNameAttribute)Attribute.GetCustomAttribute(x, typeof(CsvColumnNameAttribute), false)
        })
        .Where(x => x.Attribute != null && x.Attribute.Export)
        .OrderBy(x => x.Attribute.Order)
        .Select(x => GetPropertyValueAsString(x.Value));
    return String.Join(",", valuesSorted);
}

private string GetCsvHeaderSorted(PropertyInfo[] propertyInfos)
{
    IEnumerable<string> headersSorted = propertyInfos
        .Select(x => (CsvColumnNameAttribute)Attribute.GetCustomAttribute(x, typeof(CsvColumnNameAttribute), false))
        .Where(x => x != null && x.Export)
        .OrderBy(x => x.Order)
        .Select(x => x.Name);
    return String.Join(",", headersSorted);
}

private string GetPropertyValueAsString(object propertyValue)
{
    string propertyValueString;

    if (propertyValue == null)
        propertyValueString = "";
    else if (propertyValue is DateTime)
        propertyValueString = ((DateTime)propertyValue).ToString("dd MMM yyyy");
    else if (propertyValue is int)
        propertyValueString = propertyValue.ToString();
    else if (propertyValue is float)
        propertyValueString = ((float)propertyValue).ToString("#.####"); // format as you need it
    else if (propertyValue is double)
        propertyValueString = ((double)propertyValue).ToString("#.####"); // format as you need it
    else // treat as a string
        propertyValueString = @"""" + propertyValue.ToString().Replace(@"""", @"""""") + @""""; // quotes with 2 quotes

    return propertyValueString;
}

Once you have that code up in going in your application, you can export your list of objects to a CSV with just a couple lines of code:

// example usage
var export = new List<MyClassToExport>();
// TODO add items to list :)
var csv = GetCsv(export);

Hope this helps! And let me know if you have any suggestions or improvements.

Posted in Attributes, C#, Generics, LINQ | Leave a comment

IT Toolbox: TechNet Magazine January 2012

The January IT Toolbox column is up on the TechNet Magazine website.

In January, I covered these products:

  • SQL Source Control: Version your T-SQL within SSMS
  • Bins: Taskbar Organizer for Windows
  • MD5 Checksum Tool: Free MD5 checksum tool

Check it out and let me know what you think!

And, as always, if you have a utility or application you would like me to cover, please let me know.

Posted in Microsoft TechNet | Leave a comment

IT Toolbox: TechNet Magazine December 2011

The December IT Toolbox column is up on the TechNet Magazine website.

In December, I covered these products:

  • SSMS Tools Pack: Extend SQL Server Management studio
  • Network Inventory Advisor: Automated system and device inventory application

Check it out and let me know what you think!

And, as always, if you have a utility or application you would like me to cover, please let me know.

Posted in Microsoft TechNet | Leave a comment

IT Toolbox: TechNet Magazine November 2011

The November IT Toolbox column is up on the TechNet Magazine website.

In November, I covered these products:

  • MaxiVista: Extend Windows’ desktop onto another physical machine
  • EaseUS Todo Backup: An alternate backup solution for Windows desktops

Check it out and let me know what you think!

And, as always, if you have a utility or application you would like me to cover, please let me know.

Posted in Microsoft TechNet | Leave a comment

IT Toolbox: TechNet Magazine October 2011

The October IT Toolbox column is up on the TechNet Magazine website.

This month I covered three products:

  • up.time: Up/down service, server and network device monitoring
  • Synergy: Software-based KVM
  • Iperf: Test your network’s performance

Check it out and let me know what you think!

And, as always, if you have a utility or application you would like me to cover, please let me know.

Posted in Microsoft TechNet | Leave a comment

C# : String Extension to Parse Enums

It seems to me that there is too much typing involved in parsing a string as an Enum type. Here’s a quick string extension method to turn it into a simpler one liner:

public static T EnumParse<T>(this string input, bool ignoreCase = true)
{
    return (T)Enum.Parse(typeof(T), input, ignoreCase);
}

With this extension, you can now do this:

string s = "MyEnumValue";
MyEnum e = s.EnumParse<MyEnum>();

Seems shorter to me. :)

Posted in C# | Leave a comment

IT Toolbox: TechNet Magazine September 2011

The September IT Toolbox column is up on the TechNet Magazine website.

This month I covered these two products:

  • Lansweeper: Hardware, software, and device inventory management
  • ipMonitor: Detailed monitoring of your servers, systems, and network devices.

Check it out and let me know what you think!

And, as always, if you have a utility or application you would like me to cover, please let me know.

Posted in Microsoft TechNet | Leave a comment

ASP.Net: App_Offline.htm as a Maintenance Page

While Googling about, I came upon an oldy-but-goody, that apparently has been available since the release of ASP.Net 2.0: App_Offline.htm.

An undocumented feature, you can copy a file called app_offline.htm into the root of your .Net website and the application will “bleed off” existing connections and redirect all new requests to the app_offline.htm page.

Once you remove the file, requests will again go back to your site.

Here’s ScottGu’s posts about it from back in 2005:

One thing to note if you are using ASP.Net MVC.  Be sure your system.webserver configuration is set up to handle all requests so you don’t get 404s instead of the app_offline.htm page:

<system.webServer>
        <modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
Posted in ASP.Net, IIS, MVC | Leave a comment

SSIS: Use Two OLE DB Destination Adapters to Catch Insertion/Constraint Errors

If you are using SQL Server Integration Server (SSIS) packages to insert large volumes of data, you have undoubtedly encountered incorrectly formatted or typed data in your data source including precision errors, conversion errors, and primary key/foreign key constraint errors etc.

To avoid package failure, you can choose to redirect error output to an alternate destination rather.  However, if you are using “Table or view – fast load” as your insertion method, it can be difficult to see which rows are actually the offenders in your redirected output.

One way to get around this is to use two OLE DB Destination adapters with different table access modes and then redirect the output.

On the first OLD DB Destination adapter, choose “Table or view – fast load” as the access mode, but set the maximum commit size to a specific number appropriate for your data set size.  Remember the smaller the commit size the slower the package will run.  In my particular case, I found a commit size of 5000 was appropriate for the data source.

Next, connect the error output from your first OLE DB Destination adapter to a second OLE DB Destination adapter rather than directly to a “bad input” file or database destination.  On this adapter, choose “Table or view” as the access mode which will insert records into the database one-by-one (another reason why you will have to tune your first adapter’s commit size to get optimal balance between performance and error handling).

Finally, take the error output from this second OLE DB Destination adapter and connect it to your “bad input” destination.

When it runs, the data batch that has the bad data on the first adapter will get redirected to the second adapter rather than having the whole commit size set being dumped to your error output file.  And because it is inserting one-by-one with “Table or view” access, you will only get the “real” errors dumped out to your error destination.

 

Posted in MS SQL Server, SSIS | 1 Comment

JavaScript: Extending String to Have startsWith and endWith Functions

Here’s two prototype functions you can add to your JavaScript to give you “startsWith” and “endsWith” functions:

if (!String.prototype.startsWith) {
    String.prototype.startsWith = function (str) {
        return this.slice(0, str.length) == str;
    };
}

if (!String.prototype.endsWith) {
    String.prototype.endsWith = function(str) {
        return this.slice(-str.length) == str;
    };
}

Hope this helps!

Posted in JavaScript | Leave a comment