private void CreateZipEntry(string path, string contentType, string content)
        {
            ZipArchiveEntry entry = _archive.CreateEntry(path);

            using (var zipStream = entry.Open())
                using (MiniExcelStreamWriter writer = new MiniExcelStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize))
                    writer.Write(content);
            if (!string.IsNullOrEmpty(contentType))
            {
                _zipDictionary.Add(path, new ZipPackageInfo(entry, contentType));
            }
        }
        private void GenerateSheetByColumnInfo <T>(MiniExcelStreamWriter writer, IEnumerable value, List <ExcelColumnInfo> props, int xIndex = 1, int yIndex = 1)
        {
            var isDic       = typeof(T) == typeof(IDictionary);
            var isDapperRow = typeof(T) == typeof(IDictionary <string, object>);

            foreach (T v in value)
            {
                writer.Write($"<x:row r=\"{yIndex}\">");
                var cellIndex = xIndex;
                foreach (var p in props)
                {
                    if (p == null) //reason:https://github.com/shps951023/MiniExcel/issues/142
                    {
                        cellIndex++;
                        continue;
                    }
                    object cellValue = null;
                    if (isDic)
                    {
                        cellValue = ((IDictionary)v)[p.Key];
                        //WriteCell(writer, yIndex, cellIndex, cellValue, null); // why null because dictionary that needs to check type every time
                        //TODO: user can specefic type to optimize efficiency
                    }
                    else if (isDapperRow)
                    {
                        cellValue = ((IDictionary <string, object>)v)[p.Key.ToString()];
                    }
                    else
                    {
                        cellValue = p.Property.GetValue(v);
                    }
                    WriteCell(writer, yIndex, cellIndex, cellValue, p);


                    cellIndex++;
                }
                writer.Write($"</x:row>");
                yIndex++;
            }
        }
 private void WriteEmptySheet(MiniExcelStreamWriter writer)
 {
     writer.Write($@"<?xml version=""1.0"" encoding=""utf-8""?><x:worksheet xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main""><x:dimension ref=""A1""/><x:sheetData></x:sheetData></x:worksheet>");
 }
        private void CreateSheetXml(object value, string sheetPath)
        {
            ZipArchiveEntry entry = _archive.CreateEntry(sheetPath);

            using (var zipStream = entry.Open())
                using (MiniExcelStreamWriter writer = new MiniExcelStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize))
                {
                    if (value == null)
                    {
                        WriteEmptySheet(writer);
                        goto End; //for re-using code
                    }

                    var type = value.GetType();

                    Type genericType = null;

                    //DapperRow

                    if (value is IDataReader)
                    {
                        GenerateSheetByIDataReader(writer, value as IDataReader);
                    }
                    else if (value is IEnumerable)
                    {
                        var values = value as IEnumerable;

                        // try to get type from reflection
                        // genericType = null

                        var rowCount = 0;

                        var maxColumnIndex = 0;
                        //List<object> keys = new List<object>();
                        List <ExcelColumnInfo> props = null;
                        string mode = null;

                        // reason : https://stackoverflow.com/questions/66797421/how-replace-top-format-mark-after-MiniExcelStreamWriter-writing
                        // check mode & get maxRowCount & maxColumnIndex
                        {
                            foreach (var item in values) //TODO: need to optimize
                            {
                                rowCount = checked (rowCount + 1);

                                //TODO: if item is null but it's collection<T>, it can get T type from reflection
                                if (item != null && mode == null)
                                {
                                    if (item is IDictionary <string, object> )
                                    {
                                        mode = "IDictionary<string, object>";
                                        var dic = item as IDictionary <string, object>;
                                        props          = GetDictionaryColumnInfo(dic, null);
                                        maxColumnIndex = props.Count;
                                    }
                                    else if (item is IDictionary)
                                    {
                                        var dic = item as IDictionary;
                                        mode  = "IDictionary";
                                        props = GetDictionaryColumnInfo(null, dic);
                                        //maxColumnIndex = dic.Keys.Count;
                                        maxColumnIndex = props.Count; // why not using keys, because ignore attribute ![image](https://user-images.githubusercontent.com/12729184/163686902-286abb70-877b-4e84-bd3b-001ad339a84a.png)
                                    }
                                    else
                                    {
                                        var _t = item.GetType();
                                        if (_t != genericType)
                                        {
                                            genericType = item.GetType();
                                        }
                                        genericType = item.GetType();
                                        SetGenericTypePropertiesMode(genericType, ref mode, out maxColumnIndex, out props);
                                    }

                                    var collection = value as ICollection;
                                    if (collection != null)
                                    {
                                        rowCount = checked ((value as ICollection).Count);
                                        break;
                                    }
                                    continue;
                                }
                            }
                        }

                        if (rowCount == 0)
                        {
                            // only when empty IEnumerable need to check this issue #133  https://github.com/shps951023/MiniExcel/issues/133
                            genericType = TypeHelper.GetGenericIEnumerables(values).FirstOrDefault();
                            if (genericType == null || genericType == typeof(object) || // sometime generic type will be object, e.g: https://user-images.githubusercontent.com/12729184/132812859-52984314-44d1-4ee8-9487-2d1da159f1f0.png
                                typeof(IDictionary <string, object>).IsAssignableFrom(genericType) ||
                                typeof(IDictionary).IsAssignableFrom(genericType))
                            {
                                WriteEmptySheet(writer);
                                goto End; //for re-using code
                            }
                            else
                            {
                                SetGenericTypePropertiesMode(genericType, ref mode, out maxColumnIndex, out props);
                            }
                        }

                        writer.Write($@"<?xml version=""1.0"" encoding=""utf-8""?><x:worksheet xmlns:r=""http://schemas.openxmlformats.org/officeDocument/2006/relationships"" xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main"" >");

                        // dimension
                        var maxRowIndex = rowCount + (_printHeader && rowCount > 0 ? 1 : 0); //TODO:it can optimize
                        writer.Write($@"<x:dimension ref=""{GetDimensionRef(maxRowIndex, maxColumnIndex)}""/>");

                        //cols:width
                        var ecwProp = props?.Where(x => x?.ExcelColumnWidth != null).ToList();
                        if (ecwProp != null && ecwProp.Count > 0)
                        {
                            writer.Write($@"<x:cols>");
                            foreach (var p in ecwProp)
                            {
                                writer.Write($@"<x:col min=""{p.ExcelColumnIndex + 1}"" max=""{p.ExcelColumnIndex + 1}"" width=""{p.ExcelColumnWidth}"" customWidth=""1"" />");
                            }
                            writer.Write($@"</x:cols>");
                        }

                        //header
                        writer.Write($@"<x:sheetData>");
                        var yIndex = 1;
                        var xIndex = 1;
                        if (_printHeader)
                        {
                            var cellIndex = xIndex;
                            writer.Write($"<x:row r=\"{yIndex}\">");

                            foreach (var p in props)
                            {
                                if (p == null)
                                {
                                    cellIndex++; //reason : https://github.com/shps951023/MiniExcel/issues/142
                                    continue;
                                }

                                var r = ExcelOpenXmlUtils.ConvertXyToCell(cellIndex, yIndex);
                                WriteC(writer, r, columnName: p.ExcelColumnName);
                                cellIndex++;
                            }

                            writer.Write($"</x:row>");
                            yIndex++;
                        }

                        // body
                        if (mode == "IDictionary<string, object>") //Dapper Row
                        {
                            GenerateSheetByColumnInfo <IDictionary <string, object> >(writer, value as IEnumerable, props, xIndex, yIndex);
                        }
                        else if (mode == "IDictionary") //IDictionary
                        {
                            GenerateSheetByColumnInfo <IDictionary>(writer, value as IEnumerable, props, xIndex, yIndex);
                        }
                        else if (mode == "Properties")
                        {
                            GenerateSheetByColumnInfo <object>(writer, value as IEnumerable, props, xIndex, yIndex);
                        }
                        else
                        {
                            throw new NotImplementedException($"Type {type.Name} & genericType {genericType.Name} not Implemented. please issue for me.");
                        }
                        writer.Write("</x:sheetData>");
                        if (_configuration.AutoFilter)
                        {
                            writer.Write($"<x:autoFilter ref=\"A1:{ExcelOpenXmlUtils.ConvertXyToCell(maxColumnIndex, maxRowIndex == 0 ? 1 : maxRowIndex)}\" />");
                        }
                        writer.Write("<x:drawing  r:id=\"drawing" + currentSheetIndex + "\" /></x:worksheet>");
                    }
                    else if (value is DataTable)
                    {
                        GenerateSheetByDataTable(writer, value as DataTable);
                    }
                    else
                    {
                        throw new NotImplementedException($"Type {type.Name} & genericType {genericType.Name} not Implemented. please issue for me.");
                    }
                }
End:        //for re-using code
            _zipDictionary.Add(sheetPath, new ZipPackageInfo(entry, "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"));
        }