开发者

Export iQueryable of complex objects to Excel

We have some code that exports data from a database to Excel and it works well.

The main problem is we use a DB view to gather all our data. This creates the issue of having a sizable view as we have multiple types of objects of which we export.

class Event
  int id
  string title
  List<EventDate> Dates
  string desc

class EventDate
  DateTime start
  DateTime end
  List<EventLocation> Locations

class EventLocation
  string address
  string city
  string zip

class Birthday : Event
  int balloonsOrdered
  string cakeText

class M开发者_StackOverflow中文版eeting : Event
  string Organizer
  string Topic

So, above is the model. Birthday and Meeting inherit from Event and all Event objects have a list of EventDate objects. Each EventDate object has a start date, end date and a list of Location objects.

Our goal is to find a dynamic way to get data from the DB to an Excel doc. We'd rather not maintain a massive view in the database (as we'll add more event types eventually).

I'm not all that familiar with .NET's XML capabilities, but the solution we are using now uses OpenXML and the code makes sense.


You could create a CSV file using List<T> and and following code:

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Web;

public static void CreateCSV<T>(List<T> list, string csvNameWithExt)
{
    if (list == null || list.Count == 0) return;

    HttpContext.Current.Response.Clear();
    HttpContext.Current.Response.AddHeader(
        "content-disposition", string.Format("attachment; filename={0}", csvNameWithExt));
    HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";

    //get type from 0th member
    Type t = list[0].GetType();
    string newLine = Environment.NewLine;

    using (StringWriter sw = new StringWriter())
    {
        //gets all properties
        PropertyInfo[] props = t.GetProperties();

        //this is the header row
        //foreach of the properties in class above, write out properties
        foreach (PropertyInfo pi in props)
        {
            sw.Write(pi.Name.ToUpper() + ",");
        }
        sw.Write(newLine);

        //this acts as datarow
        foreach (T item in list)
        {
            //this acts as datacolumn
            foreach (PropertyInfo Column in props)
            {
                //this is the row+col intersection (the value)
                string value = item.GetType().GetProperty(Column.Name).GetValue(item, null).ToString();
                if (value.Contains(","))
                {
                    value = "\"" + value + "\"";
                }
                sw.Write(value + ",");
            }
            sw.Write(newLine);
        }

        //  render the htmlwriter into the response
        HttpContext.Current.Response.Write(sw.ToString());
        HttpContext.Current.Response.End();
    }
}


I improved the solution from Brad to better work with Entity Framework Data Annotations :

  • it gets the display name from annotations instead of the column name
  • it does not export columns that are marked "scaffold = false" via annotations
  • it offers you a way to deal with complex types (not primitive types)

    public static class ExportListToCSV
    {
        public static void CreateCSV<T>(List<T> list, string csvNameWithExt)
        {
            if (list == null || list.Count == 0) return;
    
            HttpContext.Current.Response.Clear();
            HttpContext.Current.Response.AddHeader(
                "content-disposition", string.Format("attachment; filename={0}", csvNameWithExt));
            HttpContext.Current.Response.ContentType = "application/vnd.ms-excel";
    
            //get type from 0th member
            Type t = list[0].GetType();
            string newLine = Environment.NewLine;
    
            //gets all columns
            PropertyInfo[] columns = t.GetProperties();
    
            // skip columns where ScaffoldColumn = false
            // useful to skip column containing IDs for Foreign Keys
            var props = t.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                           .Select(p => new
                           {
                               Property = p,
                               Attribute = p.GetCustomAttribute<ScaffoldColumnAttribute>()
                           })
                           .Where(p => p.Attribute == null || (p.Attribute != null && p.Attribute.Scaffold != false))
                           .ToList();
    
    
            using (StringWriter sw = new StringWriter())
            {
                //this is the header row                
                foreach (var prop in props)
                {
                    var pi = prop.Property;
                    string columnName = "";
    
                    // outputs raw column name, but this is not really meaningful for the end user
                    //sw.Write(pi.Name + ",");
    
                    columnName = pi.GetDisplayName();
                    sw.Write(columnName + ",");
                }
    
                sw.Write(newLine);
    
                //this acts as datarow
                foreach (T item in list)
                {
                    //this acts as datacolumn
                    foreach (var prop in props)
                    {
                        var column = prop.Property;
                        //this is the row+col intersection (the value)
                        PropertyInfo info = item.GetType().GetProperty(column.Name);
    
                        //string value = info.GetValue(item, null);
                        string value = GetDescriptionForComplexObjects(info.GetValue(item, null));                        
                        if (value.Contains(","))
                        {
                            value = "\"" + value + "\"";
                        }
                        sw.Write(value + ",");
                    }
                    sw.Write(newLine);
                }
    
                //  render the htmlwriter into the response
                HttpContext.Current.Response.Write(sw.ToString());
                HttpContext.Current.Response.End();
            }
        }
    
        private static string GetDescriptionForComplexObjects(object value)
        {      
            string desc = "";
            if (             
                (value != null && !IsSimpleType(value.GetType()))
                )
            {
                dynamic dynObject = value;
                if (dynObject != null)
                {
                    //using Model Extensions, 
                    //I made sure all my objects have a DESCRIPTION property 
                    //this property must return a string
                    //for an employee object, you would return the employee's name for example
                    //desc = dynObject.DESCRIPTION;
                }
            }
            else
            {
                desc = "" + value;
            }
    
            return desc;
        }
    
        public static string GetDisplayName(this PropertyInfo pi)
        {
            if (pi == null)
            {
                throw new ArgumentNullException(nameof(pi));
            }
            return pi.IsDefined(typeof(DisplayAttribute)) ? pi.GetCustomAttribute<DisplayAttribute>().GetName() : pi.Name;
        }
    
        public static bool IsSimpleType(Type type)
        {
            return
                type.IsPrimitive ||
                new Type[] {
            typeof(Enum),
            typeof(String),
            typeof(Decimal),
            typeof(DateTime),
            typeof(DateTimeOffset),
            typeof(TimeSpan),
            typeof(Guid)
                }.Contains(type) ||
                Convert.GetTypeCode(type) != TypeCode.Object ||
                (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && IsSimpleType(type.GetGenericArguments()[0]))
                ;
        }
    }
    

    }

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜