diff --git a/drafts/【MiniExcel】AutoFillCenterEmptyRowOrCellLogic.linq b/drafts/【MiniExcel】AutoFillCenterEmptyRowOrCellLogic.linq deleted file mode 100644 index 3dfebaa..0000000 --- a/drafts/【MiniExcel】AutoFillCenterEmptyRowOrCellLogic.linq +++ /dev/null @@ -1,145 +0,0 @@ - - AngleSharp - Dapper - DocumentFormat.OpenXml - Newtonsoft.Json - System.Data.SqlClient - Dapper - Newtonsoft.Json - System.Data.SqlClient - System.IO.Compression - System.Net.Http - System.Threading.Tasks - - -void Main() -{ - var source = new Dictionary>() - { - {0,new Dictionary(){{0,0},{3,3}}}, - {3,new Dictionary(){{2,2}}}, - }; - Console.WriteLine(JsonConvert.SerializeObject(source, Newtonsoft.Json.Formatting.Indented)); - var rows = GetRows(@"D:\git\MiniExcel\samples\xlsx\TestCenterEmptyRow\TestCenterEmptyRow.xlsx").ToList(); - Console.WriteLine(rows); -} - -private static string ConvertToString(ZipArchiveEntry entry) -{ - if (entry == null) - return null; - using (var eStream = entry.Open()) - using (var reader = new StreamReader(eStream)) - return reader.ReadToEnd(); -} - -internal static class ExcelXName -{ - internal readonly static XNamespace ExcelNamespace = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); - internal readonly static XNamespace ExcelRelationshipsNamepace = XNamespace.Get("http://schemas.openxmlformats.org/officeDocument/2006/relationships"); - internal readonly static XName Row; - internal readonly static XName R; - internal readonly static XName V; - internal readonly static XName T; - internal readonly static XName C; - internal readonly static XName Dimension; - static ExcelXName() - { - Row = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main") + "row"; - R = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main") + "r"; - V = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main") + "v"; - T = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main") + "t"; - C = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main") + "c"; - Dimension = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main") + "dimension"; - } -} - -IEnumerable> GetRows(string path) -{ - using (FileStream stream = new FileStream(path, FileMode.Open)) - using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Read, false, UTF8Encoding.UTF8)) - { - var firstSheetEntry = archive.Entries.First(w => w.FullName.StartsWith("xl/worksheets/", StringComparison.OrdinalIgnoreCase)); - var xml = ConvertToString(firstSheetEntry); - var xl = XElement.Parse(xml); - - var maxRowIndex = 3; - var maxColumnIndex = 3; - - // first get sheet row count & column count - - var rowIndex = 0; - foreach (var row in xl.Descendants(ExcelXName.Row)) - { - { - var r = row.Attribute("r")?.Value?.ToString(); - - var rIndex = int.MinValue; - if (int.TryParse(r, out var _rowIndex)) - rIndex = _rowIndex - 1; // The row attribute is 1 - based - } - - - var cells = new List(maxColumnIndex); - - foreach (var cell in row.Descendants(ExcelXName.C)) - { - - } - - yield return cells; - } - } -} - -void Main2() -{ - { - var dic = new Dictionary>() { - {0,new Dictionary(){{0,0},{3,3}}}, - {3,new Dictionary(){{2,3}}}, - }; - Console.WriteLine(JsonConvert.SerializeObject(dic, Newtonsoft.Json.Formatting.Indented)); - - var maxRowIndex = 3; - var maxColumnIndex = 3; - for (int rowIndex = 0; rowIndex <= maxRowIndex; rowIndex++) - { - if (!dic.ContainsKey(rowIndex)) - { - var d = new Dictionary(); - for (int columnIndex = 0; columnIndex <= maxColumnIndex; columnIndex++) - { - d.Add(columnIndex, null); - } - dic.Add(rowIndex, d); - } - else - { - var d = dic[rowIndex]; - for (int columnIndex = 0; columnIndex <= maxColumnIndex; columnIndex++) - { - if (!d.ContainsKey(columnIndex)) - { - d.Add(columnIndex, null); - } - } - } - } - - Console.WriteLine(JsonConvert.SerializeObject(dic, Newtonsoft.Json.Formatting.Indented)); - } - { - var dic = new Dictionary>() { - {0,new Dictionary(){{0,0},{1,null},{2,null},{3,3}}}, - {1,new Dictionary(){{0,null},{1,null},{2,null},{3,null}}}, - {2,new Dictionary(){{0,null},{1,null},{2,null},{3,null}}}, - {3,new Dictionary(){{0,null},{1,null},{2,2},{3,null}}}, - }; - //Console.WriteLine(dic); - var json = JsonConvert.SerializeObject(dic, Newtonsoft.Json.Formatting.Indented); - Console.WriteLine(json); - } -} - -// You can define other methods, fields, classes and namespaces here diff --git a/drafts/【MiniExcel】Reader Mode.linq b/drafts/【MiniExcel】Reader Mode.linq new file mode 100644 index 0000000..28b932d --- /dev/null +++ b/drafts/【MiniExcel】Reader Mode.linq @@ -0,0 +1,350 @@ + + AngleSharp + Dapper + DocumentFormat.OpenXml + Newtonsoft.Json + System.Data.SqlClient + Dapper + Newtonsoft.Json + System.Data.SqlClient + System.Net.Http + System.Threading.Tasks + System.IO.Compression + + +void Main() +{ + using (var reader = new XlsxEasyRowsValueReader(@"D:\git\MiniExcel\samples\xlsx\TestCenterEmptyRow\TestCenterEmptyRow.xlsx")) + { + while (reader.Read()) + { + Console.WriteLine($"row : {reader.CurrentRowIndex}"); + Console.Write("Cells : | "); + for (int i = 0; i < reader.FieldCount; i++) + { + var v = reader.GetValue(i); + Console.Write(v == null ? null : v); + Console.Write(" | "); + } + Console.WriteLine(); + } + } +} + +internal class Worksheet +{ + public int RowCount { get; set; } + public int FieldCount { get; set; } + public Dictionary> Rows { get; set; } +} + +// You can define other methods, fields, classes and namespaces here +public class MiniExcel +{ + internal static Worksheet ConvertAsSheet(string path) + { + using (FileStream stream = new FileStream(path, FileMode.Open)) + using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Read, false, UTF8Encoding.UTF8)) + { + //sharedStrings must in memory cache + Dictionary GetSharedStrings() + { + var sharedStringsEntry = archive.Entries.SingleOrDefault(w => w.FullName == "xl/sharedStrings.xml"); + var xml = ConvertToString(sharedStringsEntry); + var xl = XElement.Parse(xml); + var ts = xl.Descendants(ExcelXName.T).Select((s, i) => new { i, v = s.Value?.ToString() }) + .ToDictionary(s => s.i, s => s.v) + ; + return ts; + } + + var sharedStrings = GetSharedStrings(); + + //notice: for performance just read first one and no care the order + var rowIndexMaximum = int.MinValue; + var columnIndexMaximum = int.MinValue; + + + + var datarows = new Dictionary>(); + var firstSheetEntry = archive.Entries.First(w => w.FullName.StartsWith("xl/worksheets/", StringComparison.OrdinalIgnoreCase)); + { + var xml = ConvertToString(firstSheetEntry); + var xl = XElement.Parse(xml); + + foreach (var row in xl.Descendants(ExcelXName.Row)) + { + // + var datarow = new Dictionary(); + { + var r = row.Attribute("r")?.Value?.ToString(); + + var rowIndex = int.MinValue; + if (int.TryParse(r, out var _rowIndex)) + rowIndex = _rowIndex - 1; // The row attribute is 1 - based + rowIndexMaximum = Math.Max(rowIndexMaximum, rowIndex); + + datarows.Add(rowIndex, datarow); + } + + foreach (var cell in row.Descendants(ExcelXName.C)) + { + var t = cell.Attribute("t")?.Value?.ToString(); + var v = cell.Descendants(ExcelXName.V).SingleOrDefault()?.Value; + if (t == "s") + { + if (!string.IsNullOrEmpty(v)) + v = sharedStrings[int.Parse(v)]; + } + + var r = cell.Attribute("r")?.Value?.ToString(); + { + var cellIndex = GetColumnIndex(r) - 1; + columnIndexMaximum = Math.Max(columnIndexMaximum, cellIndex); + + datarow.Add(cellIndex, v); + } + } + } + } + + return new Worksheet { FieldCount=columnIndexMaximum+1,RowCount=rowIndexMaximum+1,Rows=datarows}; + } + } + + private static string ConvertToString(ZipArchiveEntry entry) + { + if (entry == null) + return null; + using (var eStream = entry.Open()) + using (var reader = new StreamReader(eStream)) + return reader.ReadToEnd(); + } + + /// X=CellLetter,Y=CellNumber,ex:A1=(1,1),B2=(2,2) + internal static void ConvertCellToXY(string cell,out int x,out int y) + { + x=GetColumnIndex(cell); + y=GetCellNumber(cell); + } + + internal static int GetColumnIndex(string cell) + { + const string keys = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const int mode = 26; + + var x = 0; + var cellLetter = GetCellLetter(cell); + //AA=27,ZZ=702 + for (int i = 0; i < cellLetter.Length; i++) + x = x * mode + keys.IndexOf(cellLetter[i]); + + return x; + } + + internal static int GetCellNumber(string cell) + { + if (string.IsNullOrEmpty(cell)) + throw new Exception("cell is null or empty"); + string cellNumber = string.Empty; + for (int i = 0; i < cell.Length; i++) + { + if (Char.IsDigit(cell[i])) + cellNumber += cell[i]; + } + return int.Parse(cellNumber); + } + + internal static string GetCellLetter(string cell) + { + string GetCellLetter = string.Empty; + for (int i = 0; i < cell.Length; i++) + { + if (Char.IsLetter(cell[i])) + GetCellLetter += cell[i]; + } + return GetCellLetter; + } +} + +internal static class ExcelXName +{ + internal readonly static XNamespace ExcelNamespace = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main"); + internal readonly static XNamespace ExcelRelationshipsNamepace = XNamespace.Get("http://schemas.openxmlformats.org/officeDocument/2006/relationships"); + internal readonly static XName Row; + internal readonly static XName R; + internal readonly static XName V; + internal readonly static XName T; + internal readonly static XName C; + internal readonly static XName Dimension; + static ExcelXName() + { + Row = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main") + "row"; + R = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main") + "r"; + V = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main") + "v"; + T = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main") + "t"; + C = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main") + "c"; + Dimension = XNamespace.Get("http://schemas.openxmlformats.org/spreadsheetml/2006/main") + "dimension"; + } +} + +public class XlsxEasyRowsValueReader : IDataReader +{ + private static Worksheet[] _Sheets = new Worksheet[1]; + private Dictionary> _Rows {get{return _Sheets[0].Rows;}} + + public XlsxEasyRowsValueReader(string filePath) + { + _Sheets[0] = MiniExcel.ConvertAsSheet(filePath); + } + + public int RowCount {get{return _Sheets[0].RowCount;}} + public int FieldCount {get{return _Sheets[0].FieldCount;}} + public int Depth { get; private set; } + public int CurrentRowIndex { get { return Depth - 1; } } + + public object this[int i] => GetValue(i); + public object this[string name] => GetValue(GetOrdinal(name)); + + public bool Read() + { + if (Depth == RowCount) + return false; + Depth++; + return true; + } + + public string GetName(int i) => Helper.ConvertColumnName(i + 1); + + + public int GetOrdinal(string name) + { + //TODO + var keys = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray().ToList(); + var dic = keys.ToDictionary(s => s, s => keys.IndexOf(s)); + return dic[(name[0])]; + } + + public object GetValue(int i) + { + //if (CurrentRowIndex < 0) + // throw new InvalidOperationException("Invalid attempt to read when no data is present."); + if (!_Rows.Keys.Contains(CurrentRowIndex)) + return null; + if (_Rows[this.CurrentRowIndex].TryGetValue(i, out var v)) + return v; + return null; + } + + public int GetValues(object[] values) + { + return this.Depth; + } + + //TODO: multiple sheets + public bool NextResult() => false; + + public void Dispose() { } + + public void Close() { } + + public int RecordsAffected => throw new NotImplementedException(); + + bool IDataReader.IsClosed => this.RowCount - 1 == this.Depth; + + public string GetString(int i) => (string)GetValue(i); + + public bool GetBoolean(int i) => (bool)GetValue(i); + + public byte GetByte(int i) => (byte)GetValue(i); + + public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) => throw new NotImplementedException(); + + public char GetChar(int i) => (char)GetValue(i); + + public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) => throw new NotImplementedException(); + + public IDataReader GetData(int i) => throw new NotImplementedException(); + + public string GetDataTypeName(int i) => throw new NotImplementedException(); + + public DateTime GetDateTime(int i) => (DateTime)GetValue(i); + + public decimal GetDecimal(int i) => (decimal)GetValue(i); + + public double GetDouble(int i) => (double)GetValue(i); + + public Type GetFieldType(int i) + { + var v = GetValue(i); + return v == null ? typeof(string) : v.GetType(); + } + + public float GetFloat(int i) => (float)GetValue(i); + + public Guid GetGuid(int i) => (Guid)GetValue(i); + + public short GetInt16(int i) => (short)GetValue(i); + + public int GetInt32(int i) => (int)GetValue(i); + + public long GetInt64(int i) => (long)GetValue(i); + + public DataTable GetSchemaTable() + { + var dataTable = new DataTable("SchemaTable"); + dataTable.Locale = System.Globalization.CultureInfo.InvariantCulture; + dataTable.Columns.Add("ColumnName", typeof(string)); + dataTable.Columns.Add("ColumnOrdinal", typeof(int)); + for (int i = 0; i < this.FieldCount; i++) + { + dataTable.Rows.Add(this.GetName(i), i); + } + DataColumnCollection columns = dataTable.Columns; + foreach (DataColumn item in columns) + { + item.ReadOnly = true; + } + return dataTable; + } + + public bool IsDBNull(int i) => GetValue(i) == null; + +} + +internal static class Helper +{ + public static TValue GetValueOrDefault + (this IDictionary dictionary, + TKey key, + TValue defaultValue) + { + TValue value; + return dictionary.TryGetValue(key, out value) ? value : defaultValue; + } + + public static TValue GetValueOrDefault + (this IDictionary dictionary, + TKey key, + Func defaultValueProvider) + { + TValue value; + return dictionary.TryGetValue(key, out value) ? value + : defaultValueProvider(); + } + + internal static string ConvertColumnName(int x) + { + int dividend = x; + string columnName = String.Empty; + int modulo; + + while (dividend > 0) + { + modulo = (dividend - 1) % 26; + columnName = Convert.ToChar(65 + modulo).ToString() + columnName; + dividend = (int)((dividend - modulo) / 26); + } + return columnName; + } +} \ No newline at end of file diff --git a/drafts/【Reader】Basic Reader.linq b/drafts/【Reader】Basic Reader.linq new file mode 100644 index 0000000..a2e9c8b --- /dev/null +++ b/drafts/【Reader】Basic Reader.linq @@ -0,0 +1,264 @@ + + AngleSharp + Dapper + DocumentFormat.OpenXml + Newtonsoft.Json + System.Data.SqlClient + Dapper + Newtonsoft.Json + System.Data.SqlClient + System.Net.Http + System.Threading.Tasks + Xunit + + +#load "xunit" + +#region private::Tests + +[Fact] +void Test_NullRow() +{ + using (var reader = new XlsxEasyRowsValueReader(null)) + { + Action testCode = () => { var v = reader[0]; }; + var ex = Record.Exception(testCode); + Console.WriteLine(); + + Assert.NotNull(ex); + Assert.IsType(ex); + } +} + +[Fact] +void Test_ReaderGetName() +{ + using (var reader = new XlsxEasyRowsValueReader(null)) + { + Assert.Equal("A", reader.GetName(0)); + Assert.Equal("B", reader.GetName(1)); + Assert.Equal("BD", reader.GetName(55)); + } +} + +[Fact] +void Test_ReaderGetOriginal() +{ + using (var reader = new XlsxEasyRowsValueReader(null)) + { + Assert.Equal(0, reader.GetOrdinal("A")); + //Assert.Equal("B", reader.GetName(1)); + //Assert.Equal("BD", reader.GetName(55)); + } +} + +#endregion + +void Main() +{ + //RunTests(); // Call RunTests() or press Alt+Shift+T to initiate testing. + + Console.WriteLine("========"); + using (var reader = new XlsxEasyRowsValueReader(null)) + { + var st = reader.GetSchemaTable(); + Console.WriteLine(st); + //todo + var dt = new DataTable(); + dt.Load(reader); + } + + Console.WriteLine("========"); + using (var reader = new XlsxEasyRowsValueReader(null)) + { + while (reader.Read()) + { + Console.WriteLine($"row : {reader.CurrentRowIndex}"); + Console.Write("Cells : | "); + for (int i = 0; i < reader.FieldCount; i++) + { + var v = reader.GetValue(i); + Console.Write(v == null ? null : v); + Console.Write(" | "); + } + Console.WriteLine(); + } + } + + Console.WriteLine("========"); + using (var reader = new XlsxEasyRowsValueReader(null)) + { + for (int i = 0; i < reader.FieldCount; i++) + { + var name = reader.GetName(i); + Console.Write($" | name : {name}"); + //var ordinal = reader.GetOrdinal(name); + //Console.Write($" | ordinal : {ordinal}"); + Console.WriteLine(); + } + } +} + + + +public class XlsxEasyRowsValueReader : IDataReader +{ + private static Dictionary> _Rows { get; set; } = new Dictionary>() { + {0,new Dictionary(){{0,0},{3,3}}}, + {3,new Dictionary(){{2,2}}}, + }; + + public XlsxEasyRowsValueReader(string filePath) + { + + } + + public int RowCount { get; set; } = 4; + public int FieldCount { get; set; } = 4; + public int Depth { get; private set; } + public int CurrentRowIndex { get { return Depth - 1; } } + + public object this[int i] => GetValue(i); + public object this[string name] => GetValue(GetOrdinal(name)); + + public bool Read() + { + if (Depth == RowCount) + return false; + Depth++; + return true; + } + + public string GetName(int i) => Helper.ConvertColumnName(i + 1); + + + public int GetOrdinal(string name) + { + //TODO + var keys = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray().ToList(); + var dic = keys.ToDictionary(s => s, s => keys.IndexOf(s)); + return dic[(name[0])]; + } + + public object GetValue(int i) + { + //if (CurrentRowIndex < 0) + // throw new InvalidOperationException("Invalid attempt to read when no data is present."); + if (!_Rows.Keys.Contains(CurrentRowIndex)) + return null; + if (_Rows[this.CurrentRowIndex].TryGetValue(i, out var v)) + return v; + return null; + } + + public int GetValues(object[] values) { + return this.Depth; + } + + //TODO: multiple sheets + public bool NextResult() => false; + + public void Dispose() { } + + public void Close() { } + + public int RecordsAffected => throw new NotImplementedException(); + + bool IDataReader.IsClosed => this.RowCount - 1 == this.Depth; + + public string GetString(int i) => (string)GetValue(i); + + public bool GetBoolean(int i) => (bool)GetValue(i); + + public byte GetByte(int i) => (byte)GetValue(i); + + public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) => throw new NotImplementedException(); + + public char GetChar(int i) => (char)GetValue(i); + + public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) => throw new NotImplementedException(); + + public IDataReader GetData(int i) => throw new NotImplementedException(); + + public string GetDataTypeName(int i) => throw new NotImplementedException(); + + public DateTime GetDateTime(int i) => (DateTime)GetValue(i); + + public decimal GetDecimal(int i) => (decimal)GetValue(i); + + public double GetDouble(int i) => (double)GetValue(i); + + public Type GetFieldType(int i) + { + var v = GetValue(i); + return v==null ? typeof(string) : v.GetType(); + } + + public float GetFloat(int i) => (float)GetValue(i); + + public Guid GetGuid(int i) => (Guid)GetValue(i); + + public short GetInt16(int i) => (short)GetValue(i); + + public int GetInt32(int i) => (int)GetValue(i); + + public long GetInt64(int i) => (long)GetValue(i); + + public DataTable GetSchemaTable() + { + var dataTable = new DataTable("SchemaTable"); + dataTable.Locale = System.Globalization.CultureInfo.InvariantCulture; + dataTable.Columns.Add("ColumnName", typeof(string)); + dataTable.Columns.Add("ColumnOrdinal", typeof(int)); + for (int i = 0; i < this.FieldCount; i++) + { + dataTable.Rows.Add(this.GetName(i), i); + } + DataColumnCollection columns = dataTable.Columns; + foreach (DataColumn item in columns) + { + item.ReadOnly = true; + } + return dataTable; + } + + public bool IsDBNull(int i) => GetValue(i) == null; + +} + +internal static class Helper +{ + public static TValue GetValueOrDefault + (this IDictionary dictionary, + TKey key, + TValue defaultValue) + { + TValue value; + return dictionary.TryGetValue(key, out value) ? value : defaultValue; + } + + public static TValue GetValueOrDefault + (this IDictionary dictionary, + TKey key, + Func defaultValueProvider) + { + TValue value; + return dictionary.TryGetValue(key, out value) ? value + : defaultValueProvider(); + } + + internal static string ConvertColumnName(int x) + { + int dividend = x; + string columnName = String.Empty; + int modulo; + + while (dividend > 0) + { + modulo = (dividend - 1) % 26; + columnName = Convert.ToChar(65 + modulo).ToString() + columnName; + dividend = (int)((dividend - modulo) / 26); + } + return columnName; + } +} diff --git a/drafts/【SqlConnection】Trace Reader Mode.linq b/drafts/【SqlConnection】Trace Reader Mode.linq new file mode 100644 index 0000000..d574f30 --- /dev/null +++ b/drafts/【SqlConnection】Trace Reader Mode.linq @@ -0,0 +1,39 @@ + + + 2a2e3c0b-0e23-4992-bf19-66db2739e377 + true + (localdb)\mssqllocaldb + Northwind + + AngleSharp + Dapper + DocumentFormat.OpenXml + Newtonsoft.Json + System.Data.SqlClient + Dapper + Newtonsoft.Json + System.Data.SqlClient + System.Net.Http + System.Threading.Tasks + + +void Main() +{ + using (var cn = (SqlConnection)this.Connection) + { + cn.Open(); + using(var cmd = cn.CreateCommand()){ + cmd.CommandText="select 1 id"; + using(var reader = cmd.ExecuteReader()){ + var name = reader.GetName(0); + Console.WriteLine(name); + var dt = reader.GetSchemaTable(); + Console.WriteLine(dt); + var v = reader.GetValue(0); + var vs = reader.GetValues(null); + } + } + } +} + +// You can define other methods, fields, classes and namespaces here