private static TResult ConvertToXlsxStreamAsync <TResource, TResult>(
            IDictionary <string, string> properties, IEnumerable <TResource> resources,
            Func <byte[], TResult> callback)
        {
            var guidReferences = resources
                                 .Select(
                (obj, index) =>
            {
                if (typeof(IReferenceable).IsInstanceOfType(obj))
                {
                    var resourceId = (obj as IReferenceable).id;
                    return(resourceId.PairWithValue($"A{index}"));
                }
                return(default(KeyValuePair <Guid, string>?));
            })
                                 .SelectWhereHasValue()
                                 .ToDictionary();

            using (var stream = new MemoryStream())
            {
                OpenXmlWorkbook.Create(stream,
                                       (workbook) =>
                {
                    #region Custom properties

                    workbook.WriteCustomProperties(
                        (writeProp) =>
                    {
                        properties.Select(
                            prop =>
                        {
                            writeProp(prop.Key, prop.Value);
                            return(true);
                        }).ToArray();
                        return(true);
                    });

                    #endregion

                    workbook.WriteSheetByRow(
                        (writeRow) =>
                    {
                        var propertyOrder = typeof(TResource).GetProperties();
                        if (!propertyOrder.Any() && resources.Any())
                        {
                            propertyOrder = resources.First().GetType().GetProperties();
                        }

                        #region Header

                        var headers = propertyOrder
                                      .Select(
                            propInfo => propInfo.GetCustomAttribute <JsonPropertyAttribute, string>(
                                attr => attr.PropertyName,
                                () => propInfo.Name))
                                      .ToArray();
                        writeRow(headers);

                        #endregion

                        #region Body

                        var rows = resources.Select(
                            (result, index) =>
                        {
                            var values = propertyOrder
                                         .Select(
                                property => property.GetValue(result).CastToXlsSerialization(property, guidReferences))
                                         .ToArray();
                            writeRow(values);
                            return(true);
                        }).ToArray();

                        #endregion

                        return(true);
                    });

                    return(true);
                });

                var buffer = stream.ToArray();
                return(callback(buffer));
            }
        }
        private static TResult ConvertToMultisheetXlsxStreamAsync <TResource, TResult>(
            IDictionary <string, string> properties, IEnumerable <TResource> resources,
            Func <byte[], TResult> callback)
            where TResource : IReferenceable
        {
            var resourceGroups = resources
                                 .GroupBy(
                (res) =>
            {
                var resourceId = res.id;
                return(typeof(TResource).Name);
            });

            var guidReferences = resourceGroups
                                 .SelectMany(
                grp =>
                grp
                .Select(
                    (res, index) =>
            {
                var resourceId = res.id;
                return(resourceId.PairWithValue($"{grp.Key}!A{index + 2}"));
            }))
                                 .ToDictionary();

            using (var stream = new MemoryStream())
            {
                bool wroteRows = OpenXmlWorkbook.Create(stream,
                                                        (workbook) =>
                {
                    #region Custom properties

                    workbook.WriteCustomProperties(
                        (writeProp) =>
                    {
                        properties.Select(
                            prop =>
                        {
                            writeProp(prop.Key, prop.Value);
                            return(true);
                        }).ToArray();
                        return(true);
                    });

                    #endregion

                    foreach (var resourceGrp in resourceGroups)
                    {
                        bool wroteRow = workbook.WriteSheetByRow(
                            (writeRow) =>
                        {
                            var resourcesForSheet = resourceGrp.ToArray();
                            if (!resourcesForSheet.Any())
                            {
                                return(false);
                            }
                            var resource = resourcesForSheet.First();
                            if (resource.IsDefault())
                            {
                                return(false);
                            }
                            var propertyOrder = resource.GetType().GetProperties().Reverse().ToArray();

                            #region Header

                            var headers = propertyOrder
                                          .Select(
                                propInfo => propInfo.GetCustomAttribute <JsonPropertyAttribute, string>(
                                    attr => attr.PropertyName,
                                    () => propInfo.Name))
                                          .ToArray();
                            writeRow(headers);

                            #endregion

                            #region Body

                            var rows = resourcesForSheet.Select(
                                (result, index) =>
                            {
                                var values = propertyOrder
                                             .Select(
                                    property => property.GetValue(result).CastToXlsSerialization(property, guidReferences))
                                             .ToArray();
                                writeRow(values);
                                return(true);
                            }).ToArray();

                            #endregion

                            return(true);
                        },
                            resourceGrp.Key);
                    }

                    return(true);
                });

                stream.Flush();
                var buffer = stream.ToArray();
                return(callback(buffer));
            }
        }