mirror of
https://gitee.com/dotnetchina/MiniExcel.git
synced 2024-12-02 03:47:41 +08:00
0.2.2
- SavaAs support xl/sheet dimension - [Breaking Changes] SaveAs value type from object to DataTable & ICollection - Bug fix: ICollection with type but no data error (https://github.com/shps951023/MiniExcel/issues/105)
This commit is contained in:
parent
1d33ccdce8
commit
786e2a82bd
@ -2,6 +2,11 @@
|
||||
|
||||
## Release Notes
|
||||
|
||||
### 0.2.2
|
||||
- SavaAs support xl/sheet dimension
|
||||
- [Breaking Changes] SaveAs value type from object to DataTable & ICollection
|
||||
- Bug fix: ICollection with type but no data error (https://github.com/shps951023/MiniExcel/issues/105)
|
||||
|
||||
### 0.2.1
|
||||
- Optimize type mapping bool and datetime auto check
|
||||
- Query Support xl/worksheets/Sheet Xml Xml `<c>` without `r` attribute or without `<dimension>` but `<c>` with `r` attribute, but now performance is slow than with dimension ([](https://github.com/shps951023/MiniExcel/issues/2))
|
||||
|
108
drafts/【Reflection】Get ICollection Generic Type.linq
Normal file
108
drafts/【Reflection】Get ICollection Generic Type.linq
Normal file
@ -0,0 +1,108 @@
|
||||
<Query Kind="Program">
|
||||
<Connection>
|
||||
<ID>5fffb9dc-a56f-4ffa-a582-f9da6bc9fdad</ID>
|
||||
<Persist>true</Persist>
|
||||
<Server>192.168.1.4</Server>
|
||||
<SqlSecurity>true</SqlSecurity>
|
||||
<UserName>sa</UserName>
|
||||
<Password>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAyumoRWrbXEqda8ynsoawYAAAAAACAAAAAAAQZgAAAAEAACAAAACumZoBhp4lj0R4mTg98suX0pykwksNIARbRIh49xu5/QAAAAAOgAAAAAIAACAAAADoNABocqodkXYmDtdW0GqBvGuMfAJeL++I3kdCYqM4rxAAAADANn2PCQ6OByhczsa8iMQPQAAAAKx4dlXxPcHN4uDHZRcYbnhkQZ52tjk6YEm+q+GruBVhVPrtz22hjCT4VMaK2N6EtZF2Rfr2P8fUTQH/ZPns5GA=</Password>
|
||||
<Database>kn2015</Database>
|
||||
</Connection>
|
||||
<NuGetReference>Dapper</NuGetReference>
|
||||
<NuGetReference>MiniExcel</NuGetReference>
|
||||
<NuGetReference>System.Data.SqlClient</NuGetReference>
|
||||
<Namespace>Xunit</Namespace>
|
||||
<RemoveNamespace>System.Data</RemoveNamespace>
|
||||
<RemoveNamespace>System.Diagnostics</RemoveNamespace>
|
||||
<RemoveNamespace>System.IO</RemoveNamespace>
|
||||
<RemoveNamespace>System.Linq.Expressions</RemoveNamespace>
|
||||
<RemoveNamespace>System.Text</RemoveNamespace>
|
||||
<RemoveNamespace>System.Text.RegularExpressions</RemoveNamespace>
|
||||
<RemoveNamespace>System.Threading</RemoveNamespace>
|
||||
<RemoveNamespace>System.Transactions</RemoveNamespace>
|
||||
<RemoveNamespace>System.Xml</RemoveNamespace>
|
||||
<RemoveNamespace>System.Xml.Linq</RemoveNamespace>
|
||||
<RemoveNamespace>System.Xml.XPath</RemoveNamespace>
|
||||
</Query>
|
||||
|
||||
//[c# Reflection - Find the Generic Type of a Collection - Stack Overflow](https://stackoverflow.com/questions/2561070/c-sharp-reflection-find-the-generic-type-of-a-collection)
|
||||
#load "xunit"
|
||||
|
||||
#region private::Tests
|
||||
|
||||
[Fact]
|
||||
void ValueGenericTypeTest()
|
||||
{
|
||||
var strings = new List<int>();
|
||||
var props = Helpers.GetSubtypeGetProperties(strings);
|
||||
Assert.Equal(0,props.Length);
|
||||
}
|
||||
|
||||
public class TestType
|
||||
{
|
||||
public string A { get; set; }
|
||||
public string B { get; set; }
|
||||
}
|
||||
|
||||
[Fact()]
|
||||
void IListUpcastingTest()
|
||||
{
|
||||
IList datas = new List<TestType>();
|
||||
var props = Helpers.GetSubtypeGetProperties(datas).ToList();
|
||||
Assert.Equal(2, props.Count());
|
||||
Assert.Equal("A", props[0].Name);
|
||||
Assert.Equal("B", props[1].Name);
|
||||
}
|
||||
|
||||
|
||||
[Fact()]
|
||||
void ArrayTest()
|
||||
{
|
||||
ICollection datas = new[] { new { A = "1", B = "2" } };
|
||||
var props = Helpers.GetSubtypeGetProperties(datas);
|
||||
Assert.Equal(2, props.Count());
|
||||
}
|
||||
|
||||
[Fact()]
|
||||
void OnlyValidOGenericTypes_Test()
|
||||
{
|
||||
ICollection datas = new[] { new { A = "1", B = "2" } };
|
||||
var df = datas.GetType().GetGenericTypeDefinition(); //InvalidOperationException: This operation is only valid on generic types.
|
||||
}
|
||||
|
||||
[Fact()]
|
||||
void DictionaryTest()
|
||||
{
|
||||
ICollection datas = new[] { new Dictionary<string, object>() { { "A", "A" }, { "B", "B" } } };
|
||||
var props = Helpers.GetSubtypeGetProperties(datas);
|
||||
}
|
||||
#endregion
|
||||
|
||||
internal static class Helpers
|
||||
{
|
||||
public static PropertyInfo[] GetSubtypeGetProperties(ICollection value)
|
||||
{
|
||||
var collectionType = value.GetType();
|
||||
|
||||
Type gType;
|
||||
if (collectionType.IsGenericTypeDefinition || collectionType.IsGenericType)
|
||||
gType = collectionType.GetGenericArguments().Single();
|
||||
else if (collectionType.IsArray)
|
||||
gType = collectionType.GetElementType();
|
||||
else
|
||||
throw new NotImplementedException($"{collectionType.Name} type not implemented,please issue for me, https://github.com/shps951023/MiniExcel/issues");
|
||||
if (typeof(IDictionary).IsAssignableFrom(gType))
|
||||
throw new NotImplementedException($"{gType.Name} type not implemented,please issue for me, https://github.com/shps951023/MiniExcel/issues");
|
||||
var props = gType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
||||
return props;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Main()
|
||||
{
|
||||
//RunTests(); // Call RunTests() or press Alt+Shift+T to initiate testing.
|
||||
}
|
||||
|
||||
// You can define other methods, fields, classes and namespaces here
|
||||
|
@ -12,6 +12,7 @@
|
||||
using System.Reflection;
|
||||
using MiniExcelLibs.Utils;
|
||||
using System.Globalization;
|
||||
using System.Collections;
|
||||
|
||||
public static partial class MiniExcel
|
||||
{
|
||||
@ -26,13 +27,24 @@
|
||||
|
||||
private readonly static UTF8Encoding Utf8WithBom = new System.Text.UTF8Encoding(true);
|
||||
|
||||
public static void SaveAs(this Stream stream,object value, string startCell = "A1", bool printHeader = true)
|
||||
public static void SaveAs(this Stream stream, DataTable value, string startCell = "A1", bool printHeader = true)
|
||||
{
|
||||
SaveAsImpl(stream,GetCreateXlsxInfos(value, startCell, printHeader));
|
||||
SaveAsImpl(stream, GetCreateXlsxInfos(value, startCell, printHeader));
|
||||
stream.Position = 0;
|
||||
}
|
||||
|
||||
public static void SaveAs(string filePath, object value, string startCell = "A1", bool printHeader = true)
|
||||
public static void SaveAs(this Stream stream, ICollection value, string startCell = "A1", bool printHeader = true)
|
||||
{
|
||||
SaveAsImpl(stream, GetCreateXlsxInfos(value, startCell, printHeader));
|
||||
stream.Position = 0;
|
||||
}
|
||||
|
||||
public static void SaveAs(string filePath, DataTable value, string startCell = "A1", bool printHeader = true)
|
||||
{
|
||||
SaveAsImpl(filePath, GetCreateXlsxInfos(value, startCell, printHeader));
|
||||
}
|
||||
|
||||
public static void SaveAs(string filePath, ICollection value, string startCell = "A1", bool printHeader = true)
|
||||
{
|
||||
SaveAsImpl(filePath, GetCreateXlsxInfos(value, startCell, printHeader));
|
||||
}
|
||||
@ -42,7 +54,12 @@
|
||||
var xy = ExcelOpenXmlUtils.ConvertCellToXY(startCell);
|
||||
|
||||
var defaultFiles = GetDefaultFiles();
|
||||
var dimensionRef = string.Empty;
|
||||
|
||||
// dimension
|
||||
var dimensionRef = "A1";
|
||||
var maxRowIndex = 0;
|
||||
var maxColumnIndex = 0;
|
||||
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
@ -52,17 +69,10 @@
|
||||
{
|
||||
var dt = value as DataTable;
|
||||
|
||||
var maxRowIndex = dt.Rows.Count;
|
||||
var maxColumnIndex = dt.Columns.Count;
|
||||
// dimension
|
||||
{
|
||||
if (maxRowIndex == 0 && maxColumnIndex == 0)
|
||||
dimensionRef = "A1";
|
||||
else if ( maxColumnIndex == 1)
|
||||
dimensionRef = $"A{maxRowIndex}";
|
||||
else
|
||||
dimensionRef = $"A1:{Helpers.GetAlphabetColumnName(maxColumnIndex-1)}{maxRowIndex}";
|
||||
}
|
||||
maxRowIndex = dt.Rows.Count;
|
||||
maxColumnIndex = dt.Columns.Count;
|
||||
|
||||
|
||||
if (printHeader)
|
||||
{
|
||||
@ -119,16 +129,12 @@
|
||||
else if (value is System.Collections.ICollection)
|
||||
{
|
||||
var collection = value as System.Collections.ICollection;
|
||||
object firstValue = null;
|
||||
{
|
||||
foreach (var v in collection)
|
||||
{
|
||||
firstValue = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var type = firstValue.GetType();
|
||||
var props = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
var props = Helpers.GetSubtypeProperties(collection);
|
||||
maxColumnIndex = props.Length;
|
||||
if (props.Length == 0)
|
||||
throw new InvalidOperationException($"Properties count is 0");
|
||||
|
||||
if (printHeader)
|
||||
{
|
||||
sb.AppendLine($"<x:row r=\"{yIndex.ToString()}\">");
|
||||
@ -179,6 +185,17 @@
|
||||
sb.AppendLine($"</x:row>");
|
||||
yIndex++;
|
||||
}
|
||||
maxRowIndex = yIndex-1;
|
||||
}
|
||||
|
||||
// dimension
|
||||
{
|
||||
if (maxRowIndex == 0 && maxColumnIndex == 0)
|
||||
dimensionRef = "A1";
|
||||
else if (maxColumnIndex == 1)
|
||||
dimensionRef = $"A{maxRowIndex}";
|
||||
else
|
||||
dimensionRef = $"A1:{Helpers.GetAlphabetColumnName(maxColumnIndex - 1)}{maxRowIndex}";
|
||||
}
|
||||
|
||||
defaultFiles[@"xl/worksheets/sheet1.xml"].Xml = $@"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
@ -191,7 +208,7 @@
|
||||
return defaultFiles;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Query<T>(this Stream stream) where T : class , new()
|
||||
public static IEnumerable<T> Query<T>(this Stream stream) where T : class, new()
|
||||
{
|
||||
return QueryImpl<T>(stream);
|
||||
}
|
||||
@ -294,10 +311,10 @@
|
||||
private static void SaveAsImpl(string path, Dictionary<string, ZipPackageInfo> zipPackageInfos)
|
||||
{
|
||||
using (FileStream stream = new FileStream(path, FileMode.CreateNew))
|
||||
using(ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Create, false, Utf8WithBom))
|
||||
using (ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Create, false, Utf8WithBom))
|
||||
CreteXlsxImpl(zipPackageInfos, archive);
|
||||
}
|
||||
private static void SaveAsImpl(Stream stream,Dictionary<string, ZipPackageInfo> zipPackageInfos)
|
||||
private static void SaveAsImpl(Stream stream, Dictionary<string, ZipPackageInfo> zipPackageInfos)
|
||||
{
|
||||
using (var archive = new ZipArchive(stream, ZipArchiveMode.Create, true, Utf8WithBom))
|
||||
{
|
||||
|
@ -5,7 +5,7 @@
|
||||
<Product>MiniExcel</Product>
|
||||
<PackageTags>excel;xlsx;micro-helper;mini;openxml;helper;</PackageTags>
|
||||
<Description>
|
||||
A high performance Excel Xlsx Micro-Helper without any third party library and supporting create and dynamic/type mapping query etc..
|
||||
A high performance Excel Xlsx Micro-Helper without any third party library to create and dynamic/type mapping query etc..
|
||||
|
||||
Github : https://github.com/shps951023/MiniExcel
|
||||
Issues : https://github.com/shps951023/MiniExcel/issues
|
||||
@ -19,7 +19,7 @@
|
||||
<RepositoryUrl>https://github.com/shps951023/MiniExcel</RepositoryUrl>
|
||||
<PackageIconUrl>https://raw.githubusercontent.com/shps951023/ImageHosting/master/img/2019-01-17.13.18.32-image.png</PackageIconUrl>
|
||||
<TargetFrameworks>net461;netstandard2.0;net5.0</TargetFrameworks>
|
||||
<Version>0.2.1</Version>
|
||||
<Version>0.2.2</Version>
|
||||
<PackageReleaseNotes>Please Check [Release Notes](https://github.com/shps951023/MiniExcel/tree/master/docs)</PackageReleaseNotes>
|
||||
<RepositoryType>Github</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
@ -4,6 +4,7 @@
|
||||
namespace MiniExcelLibs.Utils
|
||||
{
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Globalization;
|
||||
@ -64,13 +65,29 @@ namespace MiniExcelLibs.Utils
|
||||
return cell;
|
||||
}
|
||||
|
||||
public static IEnumerable<PropertyInfo> GetPropertiesWithSetter(Type type)
|
||||
public static IEnumerable<PropertyInfo> GetPropertiesWithSetter(this Type type)
|
||||
{
|
||||
return type.GetProperties(BindingFlags.SetProperty |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.Instance).Where(prop => prop.GetSetMethod() != null);
|
||||
}
|
||||
|
||||
public static PropertyInfo[] GetSubtypeProperties(ICollection value)
|
||||
{
|
||||
var collectionType = value.GetType();
|
||||
|
||||
Type gType;
|
||||
if (collectionType.IsGenericTypeDefinition || collectionType.IsGenericType)
|
||||
gType = collectionType.GetGenericArguments().Single();
|
||||
else if (collectionType.IsArray)
|
||||
gType = collectionType.GetElementType();
|
||||
else
|
||||
throw new NotImplementedException($"{collectionType.Name} type not implemented,please issue for me, https://github.com/shps951023/MiniExcel/issues");
|
||||
if (typeof(IDictionary).IsAssignableFrom(gType))
|
||||
throw new NotImplementedException($"{gType.Name} type not implemented,please issue for me, https://github.com/shps951023/MiniExcel/issues");
|
||||
var props = gType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
||||
return props;
|
||||
}
|
||||
|
||||
public static string ConvertEscapeChars(string input)
|
||||
{
|
||||
|
@ -270,6 +270,48 @@ namespace MiniExcelLibs.Tests
|
||||
}
|
||||
}
|
||||
|
||||
public class SaveAsFileWithDimensionByICollectionTestType
|
||||
{
|
||||
public string A { get; set; }
|
||||
public string B { get; set; }
|
||||
}
|
||||
[Fact()]
|
||||
public void SaveAsFileWithDimensionByICollection()
|
||||
{
|
||||
//List<strongtype>
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx");
|
||||
var values = new List<SaveAsFileWithDimensionByICollectionTestType>()
|
||||
{
|
||||
new SaveAsFileWithDimensionByICollectionTestType{A="A",B="B"},
|
||||
new SaveAsFileWithDimensionByICollectionTestType{A="A",B="B"},
|
||||
};
|
||||
MiniExcel.SaveAs(path, values);
|
||||
Assert.Equal("A1:B3", GetFirstSheetDimensionRefValue(path));
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
//Array<anoymous>
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx");
|
||||
var values = new []
|
||||
{
|
||||
new {A="A",B="B"},
|
||||
new {A="A",B="B"},
|
||||
};
|
||||
MiniExcel.SaveAs(path, values);
|
||||
Assert.Equal("A1:B3", GetFirstSheetDimensionRefValue(path));
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
// without properties
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx");
|
||||
var values = new List<int>();
|
||||
Assert.Throws<InvalidOperationException>(() => MiniExcel.SaveAs(path, values));
|
||||
}
|
||||
}
|
||||
|
||||
[Fact()]
|
||||
public void SaveAsFileWithDimension()
|
||||
{
|
||||
@ -368,6 +410,7 @@ namespace MiniExcelLibs.Tests
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx");
|
||||
|
||||
var table = new DataTable();
|
||||
{
|
||||
table.Columns.Add("a", typeof(string));
|
||||
|
Loading…
Reference in New Issue
Block a user