I’m sure you’ve all had to export to Excel at one point or another. I created a small wrapper around the EPPlus library for MVC. Basically my wrapper contains a few helpers for common formatting, an ActionResult, and a column definition. Here’s what the end result looks like in the simplest form:
public ActionResult ExportToExcel() { var records = _getSampleInfoQuery.Execute(); return new ExcelFileResult<SampleInfo>(records); } Basically this exports all the columns and records in the SampleInfo collection. So if you want to specify the columns you want, you can do this:
public ActionResult ExportToExcel() { var records = _getSampleInfoQuery.Execute(); var columns = new[] { ExcelColumnDefinition.Create<SampleInfo>(x => x.College), ExcelColumnDefinition.Create<SampleInfo>(x => x.Amount, ExcelFormat.Money), ExcelColumnDefinition.Create<SampleInfo>(x => x.CreatedDate, ExcelFormat.Date, "Date"), ExcelColumnDefinition.Create<SampleInfo>(x => x.CreatedBy), ExcelColumnDefinition.Create<SampleInfo>(x => x.CreatedDate, ExcelFormat.Time, "Time"), ExcelColumnDefinition.Create<SampleInfo>(x => x.PercentageExample, ExcelFormat.Percent), }; return new ExcelFileResult<SampleInfo>(records) {ColumnDefinitions = columns}; } The ExcelColumnDefinition.Create<> is kinda ugly, so you could create a small helper method for readability like this:
private static ExcelColumnDefinition Column(Expression<Func<SampleInfo, object>> member, string format = null, string header = null) { return ExcelColumnDefinition.Create(member, format, header); } Now the above example looks like this:
public ActionResult ExportToExcel() { var records = _getSampleInfoQuery.Execute(); var columns = new[] { Column(x => x.College), Column(x => x.Amount, ExcelFormat.Money), Column(x => x.CreatedDate, ExcelFormat.Date, "Date"), Column(x => x.CreatedBy), Column(x => x.CreatedDate, ExcelFormat.Time, "Time"), Column(x => x.PercentageExample, ExcelFormat.Percent), }; return new ExcelFileResult<SampleInfo>(records) {ColumnDefinitions = columns}; } So this is what the ExcelColumnDefinition looks like:
public class ExcelColumnDefinition { public MemberInfo MemberInfo { get; set; } public string Format { get; set; } public string Header { get; set; } public static ExcelColumnDefinition Create<T>(Expression<Func<T, object>> member, string format = null, string header = null) { return new ExcelColumnDefinition { MemberInfo = GetMemberInfo(member), Format = format, Header = header }; } private static MemberInfo GetMemberInfo<T>(Expression<Func<T, object>> expression) { if (expression.Body is MemberExpression) return ((MemberExpression)expression.Body).Member; return ((MemberExpression) ((UnaryExpression) expression.Body).Operand).Member; } } Here’s the actual ActionResult:
public class ExcelFileResult<T> : ActionResult { private readonly IEnumerable<T> _records; public ExcelFileResult(IEnumerable<T> records) { _records = records; } public override void ExecuteResult(ControllerContext context) { using (var pck = new ExcelPackage()) { var ws = pck.Workbook.Worksheets.Add(WorksheetName); if (ColumnDefinitions == null) { ws.Cells["A1"].LoadFromCollection(_records, true, TableStyles.None); } else { ws.Cells["A1"].LoadFromCollection(_records, true, TableStyles.None, BindingFlags.Default, ColumnDefinitions.Select(x => x.MemberInfo).ToArray()); Format(ws, ColumnDefinitions); } const string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; var fileContentResult = new FileContentResult(pck.GetAsByteArray(), contentType) { FileDownloadName = FileDownloadName }; fileContentResult.ExecuteResult(context); } } private static void Format(ExcelWorksheet worksheet, IList<ExcelColumnDefinition> columnDefinitions) { worksheet.Row(1).Style.Font.Bold = true; for (var columnIndex = 1; columnIndex <= columnDefinitions.Count; columnIndex++) { var columnDefinitionIndex = columnIndex - 1; if (columnDefinitions[columnDefinitionIndex].Format != null) { worksheet.Column(columnIndex).Style.Numberformat.Format = columnDefinitions[columnDefinitionIndex].Format; } if (columnDefinitions[columnDefinitionIndex].Header != null) { worksheet.Cells[1, columnIndex].Value = columnDefinitions[columnDefinitionIndex].Header; } worksheet.Column(columnIndex).AutoFit(); } } public ExcelColumnDefinition[] ColumnDefinitions { get; set; } private string _downloadName; public string FileDownloadName { get { return _downloadName ?? "ExcelFile"; } set { _downloadName = value; } } private string _worksheetName; public string WorksheetName { get { return _worksheetName ?? "Sheet1"; } set { _worksheetName = value; } } } So that’s it. If you have any questions, please feel free to comment below.
You can browse or download the source on GitHub. The sample project has examples.