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.