Migrated repository
Go to file
2021-03-25 10:36:04 +08:00
docs - [Breaking Changes] Remove Query First/FirstOrDefault/Single/SingleOrDefault, user can use LINQ method do it. 2021-03-25 10:36:04 +08:00
drafts drafts\【MiniExcel】Auto specify file or stream from xlsx or xls or csv.linq 2021-03-24 17:12:38 +08:00
samples - Support create CSV by file path or stream 2021-03-25 09:24:00 +08:00
src - [Breaking Changes] Remove Query First/FirstOrDefault/Single/SingleOrDefault, user can use LINQ method do it. 2021-03-25 10:36:04 +08:00
tests - [Breaking Changes] Remove Query First/FirstOrDefault/Single/SingleOrDefault, user can use LINQ method do it. 2021-03-25 10:36:04 +08:00
.gitattributes .gitattributes 2021-03-03 13:26:37 +08:00
.gitignore ignore packages 2021-03-14 20:27:48 +08:00
appveyor.yml support appveyor 2021-03-16 14:58:14 +08:00
LICENSE.md Apache License 2.0 2021-03-10 08:30:47 +08:00
README.md - [Breaking Changes] Remove Query First/FirstOrDefault/Single/SingleOrDefault, user can use LINQ method do it. 2021-03-25 10:36:04 +08:00

NuGet Build status

Features

  • Avoid large file OOM(out of memoery) by IEnumerable Lazy Step By Step getting one row cells not until all rows read in memory
    e.g: Comparison between MiniExcel Query and ExcelDataReader/EPPlus/ClosedXml of reading large Xlsx File miniexcel_lazy_load
  • Support .NET Standard 2.0/.NET 4.6/.NET 5
  • Mini (Less than 100KB) and without any third party library dependencies
  • Like Dapper dynamic/type mapping query style
  • Create excel file or stream by AnonymousType/DapperRows/List/Array/Set/Enumrable/DataTable/Dictionary

Installation

You can install the package from NuGet

Release Notes

Please Check Release Notes

Execute a query and map the results to a strongly typed IEnumerable

public class UserAccount
{
    public Guid ID { get; set; }
    public string Name { get; set; }
    public DateTime BoD { get; set; }
    public int Age { get; set; }
    public bool VIP { get; set; }
    public decimal Points { get; set; }
}

using (var stream = File.OpenRead(path))
    var rows = stream.Query<UserAccount>();

image

Execute a query and map it to a list of dynamic objects without using head

  • dynamic key is A.B.C.D..
MiniExcel 1
Github 2
using (var stream = File.OpenRead(path))
{
    var rows = stream.Query().ToList();
                
    Assert.Equal("MiniExcel", rows[0].A);
    Assert.Equal(1, rows[0].B);
    Assert.Equal("Github", rows[1].A);
    Assert.Equal(2, rows[1].B);
}

Execute a query with first header row

Column1 Column2
MiniExcel 1
Github 2
using (var stream = File.OpenRead(path))
{
    var rows = stream.Query(useHeaderRow:true).ToList();

    Assert.Equal("MiniExcel", rows[0].Column1);
    Assert.Equal(1, rows[0].Column2);
    Assert.Equal("Github", rows[1].Column1);
    Assert.Equal(2, rows[1].Column2);
}

Query First

using (var stream = File.OpenRead(path))
    Assert.Equal("HelloWorld", stream.Query().First().A);

performance: MiniExcel/ExcelDataReader/ClosedXML/EPPlus
queryfirst

Create Excel Xlsx file

Anonymous or strongly type:

var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx");
MiniExcel.SaveAs(path, new[] {
    new { Column1 = "MiniExcel", Column2 = 1 },
    new { Column1 = "Github", Column2 = 2}
});

Datatable:

var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx");
var table = new DataTable();
{
    table.Columns.Add("Column1", typeof(string));
    table.Columns.Add("Column2", typeof(decimal));
    table.Rows.Add("MiniExcel", 1);
    table.Rows.Add("Github", 2);
}

MiniExcel.SaveAs(path, table);

Dapper:

using (var connection = GetConnection(connectionString))
{
    var rows = connection.Query(@"select 'MiniExcel' as Column1,1 as Column2 union all select 'Github',2");
    MiniExcel.SaveAs(path, rows);
}

IEnumerable<IDictionary<string, object>>

var values = new List<Dictionary<string, object>>()
{
    new Dictionary<string,object>{{ "Column1", "MiniExcel" }, { "Column2", 1 } },
    new Dictionary<string,object>{{ "Column1", "Github" }, { "Column2", 2 } }
};
MiniExcel.SaveAs(path, values);

Create File Result :

Column1 Column2
MiniExcel 1
Github 2

SaveAs Stream

using (var stream = new FileStream(path, FileMode.CreateNew))
{
    stream.SaveAs(values);
}

SQLite & Dapper Large Size File SQL Insert Avoid OOM (out of memory)

using (var connection = new SQLiteConnection(connectionString))
{
    connection.Open();
    using (var transaction = connection.BeginTransaction())
    using (var stream = File.OpenRead(path))
    {
	   var rows = stream.Query();
	   foreach (var row in rows)
			 connection.Execute("insert into T (A,B) values (@A,@B)", new { row.A, row.B }, transaction: transaction);
	   transaction.Commit();
    }
}

performance: image

ASP.NET Core 3.1 or MVC 5 Download Excel Xlsx API Demo

public class ExcelController : Controller
{
    public IActionResult Download()
    {
        var values = new[] {
            new { Column1 = "MiniExcel", Column2 = 1 },
            new { Column1 = "Github", Column2 = 2}
        };
        var stream = new MemoryStream();
        stream.SaveAs(values);
        return File(stream,
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            "demo.xlsx");
    }
}

Excel Type Auto Check

Default system will auto check file path or stream is from xlsx or csv, but if you need to specify type, it can use excelType parameter.

stream.SaveAs(excelType:ExcelType.CSV);
//or
stream.SaveAs(excelType:ExcelType.XLSX);

TODO

Please Check Project · todo

Limitations and caveats

  • Same column name use last right one
  • Must be a non-abstract type with a public parameterless constructor

Reference