public override Task WriteToStreamAsync(Type type, object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
        {
            // Create a worksheet
            var package = new ExcelPackage();

            package.Workbook.Worksheets.Add("Data");
            var worksheet = package.Workbook.Worksheets[1];

            int rowCount  = 0;
            var valueType = value.GetType();

            // Apply cell styles.
            CellStyle?.Invoke(worksheet.Cells.Style);

            // Get the item type.
            var itemType = (util.IsSimpleType(valueType))
                ? null
                : util.GetEnumerableItemType(valueType);

            // If a single object was passed, treat it like a list with one value.
            if (itemType == null)
            {
                itemType = valueType;
                value    = new[] { value };
            }

            // Enumerations of primitive types are also handled separately, since they have
            // no properties to serialise (and thus, no headers or attributes to consider).
            if (util.IsSimpleType(itemType))
            {
                // Can't convert IEnumerable<primitive> to IEnumerable<object>
                var values = (IEnumerable)value;

                foreach (var val in values)
                {
                    AppendRow(new[] { val }, worksheet, ref rowCount);
                }

                // Autofit cells if specified.
                if (AutoFit)
                {
                    worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns();
                }

                // Save and done.
                return(Task.Factory.StartNew(() => package.SaveAs(writeStream)));
            }

            // What remains is an enumeration of object types.
            var serialisableMembers = util.GetMemberNames(itemType);

            var properties = (from p in itemType.GetProperties()
                              where p.CanRead & p.GetGetMethod().IsPublic & p.GetGetMethod().GetParameters().Length == 0
                              select p).ToList();

            var fields    = new List <string>();
            var fieldInfo = new ExcelFieldInfoCollection();

            // Instantiate field names and fieldInfo lists with serialisable members.
            foreach (var field in serialisableMembers)
            {
                var propName = field;
                var prop     = properties.FirstOrDefault(p => p.Name == propName);

                if (prop == null)
                {
                    continue;
                }

                fields.Add(field);
                fieldInfo.Add(new ExcelFieldInfo(field, util.GetAttribute <ExcelColumnAttribute>(prop)));
            }

            var props = itemType.GetProperties().ToList();

            foreach (var prop in props)
            {
                var attributes = prop.GetCustomAttributes(true);
                foreach (var attribute in attributes)
                {
                    var propertyName = prop.Name;
                    if (!fieldInfo.Contains(propertyName))
                    {
                        continue;
                    }

                    string displayFormatString    = null;
                    var    displayFormatAttribute = (DisplayFormatAttribute)prop.GetCustomAttributes(typeof(DisplayFormatAttribute), true).FirstOrDefault();
                    if (displayFormatAttribute != null)
                    {
                        displayFormatString = displayFormatAttribute.DataFormatString;
                    }

                    string displayName          = null;
                    var    displayNameAttribute = (DisplayNameAttribute)prop.GetCustomAttributes(typeof(DisplayNameAttribute), true).FirstOrDefault();
                    if (displayNameAttribute != null)
                    {
                        displayName = displayNameAttribute.DisplayName;
                    }

                    var field = fieldInfo[propertyName];
                    if (!field.IsExcelHeaderDefined)
                    {
                        field.Header = propertyName;
                    }

                    if (!field.IsExcelHeaderDefined)
                    {
                        field.Header = displayName ?? propertyName;
                    }

                    var excelAttribute = attribute as ExcelColumnAttribute;
                    if (excelAttribute != null && excelAttribute.UseDisplayFormatString)
                    {
                        field.FormatString = displayFormatString;
                    }
                }
            }

            if (fields.Count == 0)
            {
                return(Task.Factory.StartNew(() => package.SaveAs(writeStream)));
            }

            // Add header row
            AppendRow((from f in fieldInfo select f.Header).ToList(), worksheet, ref rowCount);

            var data = value as IEnumerable <object>;

            // Output each row of data
            if (data?.FirstOrDefault() != null)
            {
                foreach (var dataObject in data)
                {
                    var row = new List <object>();

                    for (int i = 0; i <= fields.Count - 1; i++)
                    {
                        var cellValue = GetFieldOrPropertyValue(dataObject, fields[i]);
                        var info      = fieldInfo[i];

                        // Boolean transformations.
                        if (info.ExcelAttribute != null && info.ExcelAttribute.TrueValue != null && cellValue.Equals("True"))
                        {
                            row.Add(info.ExcelAttribute.TrueValue);
                        }

                        else if (info.ExcelAttribute != null && info.ExcelAttribute.FalseValue != null && cellValue.Equals("False"))
                        {
                            row.Add(info.ExcelAttribute.FalseValue);
                        }

                        else if (!string.IsNullOrWhiteSpace(info.FormatString) & string.IsNullOrEmpty(info.ExcelNumberFormat))
                        {
                            row.Add(string.Format(info.FormatString, cellValue));
                        }

                        else
                        {
                            row.Add(cellValue);
                        }
                    }

                    AppendRow(row.ToArray(), worksheet, ref rowCount);
                }
            }

            // Enforce any attributes on columns.
            for (int i = 1; i <= fields.Count; i++)
            {
                if (!string.IsNullOrEmpty(fieldInfo[i - 1].ExcelNumberFormat))
                {
                    worksheet.Cells[2, i, rowCount, i].Style.Numberformat.Format = fieldInfo[i - 1].ExcelNumberFormat;
                }
            }

            // Header cell styles
            HeaderStyle?.Invoke(worksheet.Row(1).Style);
            if (FreezeHeader)
            {
                worksheet.View.FreezePanes(2, 1);
            }

            var cells = worksheet.Cells[worksheet.Dimension.Address];

            // Add autofilter and fit to max column width (if requested).
            if (AutoFilter)
            {
                cells.AutoFilter = AutoFilter;
            }
            if (AutoFit)
            {
                cells.AutoFitColumns();
            }

            // Set header row where specified.
            if (HeaderHeight.HasValue)
            {
                worksheet.Row(1).Height       = HeaderHeight.Value;
                worksheet.Row(1).CustomHeight = true;
            }

            return(Task.Factory.StartNew(() => package.SaveAs(writeStream)));
        }
        public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
        {
            var response = context.HttpContext.Response;

            // Create a worksheet
            var package = new ExcelPackage();

            package.Workbook.Worksheets.Add("Data");
            var worksheet = package.Workbook.Worksheets[0];

            var rowCount  = 0;
            var valueType = context.ObjectType;
            var value     = context.Object;

            // Apply cell styles.
            CellStyle?.Invoke(worksheet.Cells.Style);

            // Get the item type.
            var itemType = util.IsSimpleType(valueType)
                ? null
                : util.GetEnumerableItemType(valueType);

            // If a single object was passed, treat it like a list with one value.
            if (itemType == null)
            {
                itemType = valueType;
                value    = new[] { context.Object };
            }

            // Enumerations of primitive types are also handled separately, since they have
            // no properties to serialise (and thus, no headers or attributes to consider).
            if (util.IsSimpleType(itemType))
            {
                // Can't convert IEnumerable<primitive> to IEnumerable<object>
                var values = (IEnumerable)value;

                foreach (var val in values)
                {
                    AppendRow(new[] { val }, worksheet, ref rowCount);
                }

                // Autofit cells if specified.
                if (AutoFit)
                {
                    worksheet.Cells[worksheet.Dimension.Address].AutoFitColumns();
                }

                // Save and done.
                var bytes = package.GetAsByteArray();
                await response.Body.WriteAsync(bytes, 0, bytes.Length);

                await package.Stream.FlushAsync();

                return;
            }

            var data = value as IEnumerable <object>;

            // What remains is an enumeration of object types.
            var serialisableMembers = util.GetMemberNames(itemType);

            var metadataProvider = (IModelMetadataProvider)context.HttpContext.RequestServices.GetService(typeof(IModelMetadataProvider));
            var metadata         = metadataProvider.GetMetadataForType(itemType);

            var properties = (from p in itemType.GetProperties()
                              where p.CanRead & p.GetGetMethod().IsPublic & p.GetGetMethod().GetParameters().Length == 0
                              select p).ToList();

            var fields    = new List <string>();
            var fieldInfo = new ExcelFieldInfoCollection();

            // Instantiate field names and fieldInfo lists with serialisable members.
            foreach (var field in serialisableMembers)
            {
                var propName = field;
                var prop     = properties.FirstOrDefault(p => p.Name == propName);

                if (prop == null)
                {
                    continue;
                }

                fields.Add(field);
                fieldInfo.Add(new ExcelFieldInfo(field, util.GetAttribute <ExcelColumnAttribute>(prop)));
            }

            if (metadata.Properties != null)
            {
                foreach (var modelProp in metadata.Properties)
                {
                    var propertyName = modelProp.PropertyName;

                    if (!fieldInfo.Contains(propertyName))
                    {
                        continue;
                    }

                    var field     = fieldInfo[propertyName];
                    var attribute = field.ExcelAttribute;

                    if (!field.IsExcelHeaderDefined)
                    {
                        field.Header = modelProp.DisplayName ?? propertyName;
                    }

                    if (attribute != null && attribute.UseDisplayFormatString)
                    {
                        Console.WriteLine(modelProp.DisplayFormatString);
                        field.FormatString = modelProp.DisplayFormatString;
                    }
                }
            }

            if (fields.Count == 0)
            {
                var bytes = package.GetAsByteArray();
                await response.Body.WriteAsync(bytes, 0, bytes.Length);

                await package.Stream.FlushAsync();

                return;
            }

            // Add header row
            AppendRow((from f in fieldInfo select f.Header).ToList(), worksheet, ref rowCount);

            // Output each row of data
            if (data?.FirstOrDefault() != null)
            {
                foreach (var dataObject in data)
                {
                    var row = new List <object>();

                    for (int i = 0; i <= fields.Count - 1; i++)
                    {
                        var cellValue = GetFieldOrPropertyValue(dataObject, fields[i]);
                        var info      = fieldInfo[i];

                        // Boolean transformations.
                        if (info.ExcelAttribute != null && info.ExcelAttribute.TrueValue != null && cellValue.Equals("True"))
                        {
                            row.Add(info.ExcelAttribute.TrueValue);
                        }

                        else if (info.ExcelAttribute != null && info.ExcelAttribute.FalseValue != null && cellValue.Equals("False"))
                        {
                            row.Add(info.ExcelAttribute.FalseValue);
                        }

                        else if (!string.IsNullOrWhiteSpace(info.FormatString) & string.IsNullOrEmpty(info.ExcelNumberFormat))
                        {
                            row.Add(string.Format(info.FormatString, cellValue));
                        }

                        else
                        {
                            row.Add(cellValue);
                        }
                    }

                    AppendRow(row.ToArray(), worksheet, ref rowCount);
                }
            }

            // Enforce any attributes on columns.
            for (int i = 1; i <= fields.Count; i++)
            {
                if (!string.IsNullOrEmpty(fieldInfo[i - 1].ExcelNumberFormat))
                {
                    worksheet.Cells[2, i, rowCount, i].Style.Numberformat.Format = fieldInfo[i - 1].ExcelNumberFormat;
                }
            }

            // Header cell styles
            HeaderStyle?.Invoke(worksheet.Row(1).Style);
            if (FreezeHeader)
            {
                worksheet.View.FreezePanes(2, 1);
            }

            var cells = worksheet.Cells[worksheet.Dimension.Address];

            // Add autofilter and fit to max column width (if requested).
            if (AutoFilter)
            {
                cells.AutoFilter = AutoFilter;
            }
            if (AutoFit)
            {
                cells.AutoFitColumns();
            }

            // Set header row where specified.
            if (HeaderHeight.HasValue)
            {
                worksheet.Row(1).Height       = HeaderHeight.Value;
                worksheet.Row(1).CustomHeight = true;
            }

            var packageBytes = package.GetAsByteArray();
            await response.Body.WriteAsync(packageBytes, 0, packageBytes.Length);

            await package.Stream.FlushAsync();
        }