使用反射的C#通用Excel导出器

时间:2015-06-10 12:49:45

标签: c# reflection

我正在使用C#中的通用Excel导出器。我的观点是放置任何类型的集合,并指定应该使用lambda表达式导出类的哪些属性,我已经这样做了。我正在努力解决的问题是,当我的类中有复杂的属性时,导出的属性值是“Namespace.ClassName”(例如“MyApp.ViewModels.MyViewModel”)。

这是我的代码:

Excel导出器类:

    public class ExcelExporter
    {
        public void ExportToExcel<T>(IEnumerable<T> data, params Expression<Func<T, object>>[] columns)
        {
            DataTable dataTable = this.ConvertToDataTable(data, columns);              
            //Export the dataTable object to Excel using some library...
        }

        private DataTable ConvertToDataTable<T>(IEnumerable<T> data, params Expression<Func<T, object>>[] columnsFunc)
        {
            DataTable table = new DataTable();

            foreach (var column in columnsFunc)
            {
                string columnName = ReflectionUtility.GetPropertyDisplayName<T>(column);
                table.Columns.Add(columnName);
            }

            foreach (T obj in data)
            {
                DataRow row = table.NewRow();

                for (int i = 0; i < table.Columns.Count; i++)
                {
                    row[table.Columns[i].ColumnName] = ReflectionUtility.GetPropertyValue<T>(obj, columnsFunc[i]);
                }

                table.Rows.Add(row);
            }

            return table;

        }

ReflectionUtility类 - 提供获取属性名称和值的方法。 “GetPropertyDisplayName”方法从属性中读取[DisplayName]属性值并将其设置为Excel文档中的标题列(原因是我希望像'FirstName'这样的属性显示为'First Name'。

public static class ReflectionUtility
    {
        /// <summary>
        /// Returns the display name of a property (set by using [DisplayName] attribute).
        /// If [DisplayName] is not provided, returns the actual property name.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static string GetPropertyDisplayName<T>(Expression<Func<T, object>> expression)
        {
            var memberExpression = expression.Body as MemberExpression;

            if (memberExpression == null)
            {
                memberExpression = ((UnaryExpression)expression.Body).Operand as MemberExpression;
            }

            var property = memberExpression.Member as PropertyInfo;

            if (property != null)
            {
                var displayNameAttribute = property.GetCustomAttribute(typeof(DisplayNameAttribute), false) as DisplayNameAttribute;

                if (displayNameAttribute != null)
                {
                    return displayNameAttribute.DisplayName;
                }
            }

            return memberExpression.Member.Name;
        }

        public static object GetPropertyValue<T>(T obj, Expression<Func<T, object>> expression)
        {
            var memberExpression = expression.Body as MemberExpression;

            if (memberExpression == null)
            {
                memberExpression = ((UnaryExpression)expression.Body).Operand as MemberExpression;
            }

            var property = memberExpression.Member as PropertyInfo;

            if (property != null)
            {
                // Note: If we want to export complex object, the object's value is something like "Namespace.ClassName", which is
                // inappropriate for displaying. So we must specify additionally which property from the complex object should be visualized...

                var value = property.GetValue(obj);

                return value;
            }

            return null;
        }

我如何使用ExcelExporter类:

ExcelExporter excelExporter = new ExcelExporter();

    excelExporter.ExportToExcel<MyViewModel>(genericListToExport,
        p => p.StringProperty1,
        p => p.StringProperty2,
        p => p.ComplexProperty.IntProperty1);

如何传递ComplexProperty.IntProperty1并获取它的值并处理ComplexProperty为null时的情况,因此我不会得到NullReferenceException。

以下是测试场景Excel输出: enter image description here

感谢任何帮助!

1 个答案:

答案 0 :(得分:4)

EPPlus可以将IEnumerable加载到工作表中。这意味着您可以加载Enumerable.Select调用的结果,将列限制为只有您想要的列,例如:

var products=allProducts.Where(prod=>prod.CustomerId=14)
                        .Select(new {prod.Name,prod.Category});
sheet.Cells["A1"].LoadFromCollection(products);

如果需要,您可以使用它,或者您可以在ExcelRangeBase.LoadFromCollection中查看它是如何完成的。

查看代码,EPPlus查找DisplayNameDescription属性以生成标题文本,然后再回到成员名称