mirror of
https://gitee.com/dotnetchina/MiniExcel.git
synced 2024-11-29 18:38:08 +08:00
Upgrade to .NET 8.0 and refactor input value extraction (#681)
* Upgrade to .NET 8.0 * Add tests
This commit is contained in:
parent
f0fe803d6a
commit
a0797a53f6
2
.github/workflows/dotnet.yml
vendored
2
.github/workflows/dotnet.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: 6.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Restore dependencies
|
- name: Restore dependencies
|
||||||
run: dotnet restore
|
run: dotnet restore
|
||||||
env:
|
env:
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -395,6 +395,7 @@ FodyWeavers.xsd
|
|||||||
*.msp
|
*.msp
|
||||||
|
|
||||||
# JetBrains Rider
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
*.sln.iml
|
*.sln.iml
|
||||||
|
|
||||||
/BenchmarkDotNet.Artifacts
|
/BenchmarkDotNet.Artifacts
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
{
|
{
|
||||||
using MiniExcelLibs.Csv;
|
using MiniExcelLibs.Csv;
|
||||||
using MiniExcelLibs.OpenXml;
|
using MiniExcelLibs.OpenXml;
|
||||||
|
using MiniExcelLibs.OpenXml.SaveByTemplate;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
@ -49,7 +50,8 @@
|
|||||||
switch (excelType)
|
switch (excelType)
|
||||||
{
|
{
|
||||||
case ExcelType.XLSX:
|
case ExcelType.XLSX:
|
||||||
return new ExcelOpenXmlTemplate(stream, configuration);
|
var valueExtractor = new InputValueExtractor();
|
||||||
|
return new ExcelOpenXmlTemplate(stream, configuration, valueExtractor);
|
||||||
default:
|
default:
|
||||||
throw new NotSupportedException($"Please Issue for me");
|
throw new NotSupportedException($"Please Issue for me");
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net45;netstandard2.0;net6.0;</TargetFrameworks>
|
<TargetFrameworks>net45;netstandard2.0;net8.0;</TargetFrameworks>
|
||||||
<Version>1.34.2</Version>
|
<Version>1.34.2</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
@ -1,154 +0,0 @@
|
|||||||
|
|
||||||
namespace MiniExcelLibs.OpenXml
|
|
||||||
{
|
|
||||||
using MiniExcelLibs.Utils;
|
|
||||||
using MiniExcelLibs.Zip;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Xml;
|
|
||||||
|
|
||||||
internal partial class ExcelOpenXmlTemplate : IExcelTemplate, IExcelTemplateAsync
|
|
||||||
{
|
|
||||||
private static readonly XmlNamespaceManager _ns;
|
|
||||||
private static readonly Regex _isExpressionRegex;
|
|
||||||
static ExcelOpenXmlTemplate()
|
|
||||||
{
|
|
||||||
_isExpressionRegex = new Regex("(?<={{).*?(?=}})");
|
|
||||||
_ns = new XmlNamespaceManager(new NameTable());
|
|
||||||
_ns.AddNamespace("x", Config.SpreadsheetmlXmlns);
|
|
||||||
_ns.AddNamespace( "x14ac", Config.SpreadsheetmlXml_x14ac );
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly Stream _stream;
|
|
||||||
private readonly OpenXmlConfiguration _configuration;
|
|
||||||
private readonly StringBuilder _calcChainContent = new StringBuilder();
|
|
||||||
|
|
||||||
public ExcelOpenXmlTemplate(Stream stream, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
_stream = stream;
|
|
||||||
_configuration = (OpenXmlConfiguration)configuration ?? OpenXmlConfiguration.DefaultConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SaveAsByTemplate(string templatePath, object value)
|
|
||||||
{
|
|
||||||
using (var stream = FileHelper.OpenSharedRead(templatePath))
|
|
||||||
SaveAsByTemplateImpl(stream, value);
|
|
||||||
}
|
|
||||||
public void SaveAsByTemplate(byte[] templateBtyes, object value)
|
|
||||||
{
|
|
||||||
using (Stream stream = new MemoryStream(templateBtyes))
|
|
||||||
SaveAsByTemplateImpl(stream, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SaveAsByTemplateImpl(Stream templateStream, object value)
|
|
||||||
{
|
|
||||||
//only support xlsx
|
|
||||||
Dictionary<string, object> values = null;
|
|
||||||
if (value is Dictionary<string, object>)
|
|
||||||
{
|
|
||||||
values = value as Dictionary<string, object>;
|
|
||||||
foreach (var key in values.Keys)
|
|
||||||
{
|
|
||||||
var v = values[key];
|
|
||||||
if (v is IDataReader)
|
|
||||||
{
|
|
||||||
values[key] = TypeHelper.ConvertToEnumerableDictionary(v as IDataReader).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var type = value.GetType();
|
|
||||||
var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
|
||||||
values = new Dictionary<string, object>();
|
|
||||||
foreach (var p in props)
|
|
||||||
{
|
|
||||||
values.Add(p.Name, p.GetValue(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
|
|
||||||
foreach (var f in fields)
|
|
||||||
{
|
|
||||||
if (!values.ContainsKey(f.Name))
|
|
||||||
{ values.Add(f.Name, f.GetValue(value)); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
templateStream.CopyTo(_stream);
|
|
||||||
|
|
||||||
var reader = new ExcelOpenXmlSheetReader(_stream, null);
|
|
||||||
var _archive = new ExcelOpenXmlZip(_stream, mode: ZipArchiveMode.Update, true, Encoding.UTF8);
|
|
||||||
{
|
|
||||||
//read sharedString
|
|
||||||
var sharedStrings = reader._sharedStrings;
|
|
||||||
StringBuilder calcSheetContent = new StringBuilder();
|
|
||||||
|
|
||||||
//read all xlsx sheets
|
|
||||||
var sheets = _archive.zipFile.Entries.Where(w => w.FullName.StartsWith("xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| w.FullName.StartsWith("/xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase)
|
|
||||||
).ToList();
|
|
||||||
|
|
||||||
int sheetIdx = 0;
|
|
||||||
foreach (var sheet in sheets)
|
|
||||||
{
|
|
||||||
this.XRowInfos = new List<XRowInfo>(); //every time need to use new XRowInfos or it'll cause duplicate problem: https://user-images.githubusercontent.com/12729184/115003101-0fcab700-9ed8-11eb-9151-ca4d7b86d59e.png
|
|
||||||
this.XMergeCellInfos = new Dictionary<string, XMergeCell>();
|
|
||||||
this.NewXMergeCellInfos = new List<XMergeCell>();
|
|
||||||
|
|
||||||
var sheetStream = sheet.Open();
|
|
||||||
var fullName = sheet.FullName;
|
|
||||||
|
|
||||||
ZipArchiveEntry entry = _archive.zipFile.CreateEntry(fullName);
|
|
||||||
using (var zipStream = entry.Open())
|
|
||||||
{
|
|
||||||
GenerateSheetXmlImpl(sheet, zipStream, sheetStream, values, sharedStrings, false);
|
|
||||||
//doc.Save(zipStream); //don't do it because : ![image](https://user-images.githubusercontent.com/12729184/114361127-61a5d100-9ba8-11eb-9bb9-34f076ee28a2.png)
|
|
||||||
}
|
|
||||||
|
|
||||||
// disposing writer disposes streams as well. reopen the entry to read and parse calc functions
|
|
||||||
using (var filledStream = entry.Open())
|
|
||||||
{
|
|
||||||
sheetIdx++;
|
|
||||||
_calcChainContent.Append( CalcChainHelper.GetCalcChainContent( CalcChainCellRefs, sheetIdx ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var calcChain = _archive.zipFile.Entries.FirstOrDefault(e => e.FullName.Contains("xl/calcChain.xml"));
|
|
||||||
if (calcChain != null)
|
|
||||||
{
|
|
||||||
string calcChainPathname = calcChain.FullName;
|
|
||||||
calcChain.Delete();
|
|
||||||
var calcChainEntry = _archive.zipFile.CreateEntry(calcChainPathname);
|
|
||||||
using (var calcChainStream = calcChainEntry.Open())
|
|
||||||
{
|
|
||||||
CalcChainHelper.GenerateCalcChainSheet(calcChainStream, _calcChainContent.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
_archive.zipFile.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task SaveAsByTemplateAsync(string templatePath, object value, CancellationToken cancellationToken = default(CancellationToken))
|
|
||||||
{
|
|
||||||
return Task.Run(() => SaveAsByTemplate(templatePath, value), cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task SaveAsByTemplateAsync(byte[] templateBtyes, object value, CancellationToken cancellationToken = default(CancellationToken))
|
|
||||||
{
|
|
||||||
return Task.Run(() => SaveAsByTemplate(templateBtyes, value), cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,7 +12,7 @@ using System.Text;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
|
||||||
namespace MiniExcelLibs.OpenXml
|
namespace MiniExcelLibs.OpenXml.SaveByTemplate
|
||||||
{
|
{
|
||||||
internal partial class ExcelOpenXmlTemplate
|
internal partial class ExcelOpenXmlTemplate
|
||||||
{
|
{
|
||||||
@ -113,7 +113,7 @@ namespace MiniExcelLibs.OpenXml
|
|||||||
public List<XMergeCell> NewXMergeCellInfos { get; private set; }
|
public List<XMergeCell> NewXMergeCellInfos { get; private set; }
|
||||||
|
|
||||||
private void GenerateSheetXmlImpl(ZipArchiveEntry sheetZipEntry, Stream stream, Stream sheetStream,
|
private void GenerateSheetXmlImpl(ZipArchiveEntry sheetZipEntry, Stream stream, Stream sheetStream,
|
||||||
Dictionary<string, object> inputMaps, IDictionary<int, string> sharedStrings,
|
IDictionary<string, object> inputMaps, IDictionary<int, string> sharedStrings,
|
||||||
bool mergeCells = false)
|
bool mergeCells = false)
|
||||||
{
|
{
|
||||||
var doc = new XmlDocument();
|
var doc = new XmlDocument();
|
||||||
@ -911,7 +911,7 @@ namespace MiniExcelLibs.OpenXml
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateDimensionAndGetRowsInfo(Dictionary<string, object> inputMaps, ref XmlDocument doc, ref XmlNodeList rows, bool changeRowIndex = true)
|
private void UpdateDimensionAndGetRowsInfo(IDictionary<string, object> inputMaps, ref XmlDocument doc, ref XmlNodeList rows, bool changeRowIndex = true)
|
||||||
{
|
{
|
||||||
// note : dimension need to put on the top ![image](https://user-images.githubusercontent.com/12729184/114507911-5dd88400-9c66-11eb-94c6-82ed7bdb5aab.png)
|
// note : dimension need to put on the top ![image](https://user-images.githubusercontent.com/12729184/114507911-5dd88400-9c66-11eb-94c6-82ed7bdb5aab.png)
|
||||||
|
|
@ -9,7 +9,7 @@ using System.Text;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MiniExcelLibs.OpenXml
|
namespace MiniExcelLibs.OpenXml.SaveByTemplate
|
||||||
{
|
{
|
||||||
internal partial class ExcelOpenXmlTemplate
|
internal partial class ExcelOpenXmlTemplate
|
||||||
{
|
{
|
126
src/MiniExcel/SaveByTemplate/ExcelOpenXmlTemplate.cs
Normal file
126
src/MiniExcel/SaveByTemplate/ExcelOpenXmlTemplate.cs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
namespace MiniExcelLibs.OpenXml.SaveByTemplate
|
||||||
|
{
|
||||||
|
using MiniExcelLibs.Utils;
|
||||||
|
using MiniExcelLibs.Zip;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Xml;
|
||||||
|
|
||||||
|
internal partial class ExcelOpenXmlTemplate : IExcelTemplate, IExcelTemplateAsync
|
||||||
|
{
|
||||||
|
private static readonly XmlNamespaceManager _ns;
|
||||||
|
private static readonly Regex _isExpressionRegex;
|
||||||
|
|
||||||
|
static ExcelOpenXmlTemplate()
|
||||||
|
{
|
||||||
|
_isExpressionRegex = new Regex("(?<={{).*?(?=}})");
|
||||||
|
_ns = new XmlNamespaceManager(new NameTable());
|
||||||
|
_ns.AddNamespace("x", Config.SpreadsheetmlXmlns);
|
||||||
|
_ns.AddNamespace("x14ac", Config.SpreadsheetmlXml_x14ac);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Stream _stream;
|
||||||
|
private readonly OpenXmlConfiguration _configuration;
|
||||||
|
private readonly IInputValueExtractor _inputValueExtractor;
|
||||||
|
private readonly StringBuilder _calcChainContent = new StringBuilder();
|
||||||
|
|
||||||
|
public ExcelOpenXmlTemplate(Stream stream, IConfiguration configuration, InputValueExtractor inputValueExtractor)
|
||||||
|
{
|
||||||
|
_stream = stream;
|
||||||
|
_configuration = (OpenXmlConfiguration)configuration ?? OpenXmlConfiguration.DefaultConfig;
|
||||||
|
_inputValueExtractor = inputValueExtractor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveAsByTemplate(string templatePath, object value)
|
||||||
|
{
|
||||||
|
using (var stream = FileHelper.OpenSharedRead(templatePath))
|
||||||
|
SaveAsByTemplateImpl(stream, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveAsByTemplate(byte[] templateBtyes, object value)
|
||||||
|
{
|
||||||
|
using (Stream stream = new MemoryStream(templateBtyes))
|
||||||
|
SaveAsByTemplateImpl(stream, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveAsByTemplateImpl(Stream templateStream, object value)
|
||||||
|
{
|
||||||
|
//only support xlsx
|
||||||
|
templateStream.CopyTo(_stream);
|
||||||
|
|
||||||
|
var reader = new ExcelOpenXmlSheetReader(_stream, null);
|
||||||
|
var _archive = new ExcelOpenXmlZip(_stream, mode: ZipArchiveMode.Update, true, Encoding.UTF8);
|
||||||
|
{
|
||||||
|
//read sharedString
|
||||||
|
var sharedStrings = reader._sharedStrings;
|
||||||
|
StringBuilder calcSheetContent = new StringBuilder();
|
||||||
|
|
||||||
|
//read all xlsx sheets
|
||||||
|
var sheets = _archive.zipFile.Entries.Where(w =>
|
||||||
|
w.FullName.StartsWith("xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| w.FullName.StartsWith("/xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase)
|
||||||
|
).ToList();
|
||||||
|
|
||||||
|
int sheetIdx = 0;
|
||||||
|
foreach (var sheet in sheets)
|
||||||
|
{
|
||||||
|
this.XRowInfos =
|
||||||
|
new List<XRowInfo>(); //every time need to use new XRowInfos or it'll cause duplicate problem: https://user-images.githubusercontent.com/12729184/115003101-0fcab700-9ed8-11eb-9151-ca4d7b86d59e.png
|
||||||
|
this.XMergeCellInfos = new Dictionary<string, XMergeCell>();
|
||||||
|
this.NewXMergeCellInfos = new List<XMergeCell>();
|
||||||
|
|
||||||
|
var sheetStream = sheet.Open();
|
||||||
|
var fullName = sheet.FullName;
|
||||||
|
|
||||||
|
var inputValues = _inputValueExtractor.ToValueDictionary(value);
|
||||||
|
ZipArchiveEntry entry = _archive.zipFile.CreateEntry(fullName);
|
||||||
|
using (var zipStream = entry.Open())
|
||||||
|
{
|
||||||
|
GenerateSheetXmlImpl(sheet, zipStream, sheetStream, inputValues, sharedStrings, false);
|
||||||
|
//doc.Save(zipStream); //don't do it because : ![image](https://user-images.githubusercontent.com/12729184/114361127-61a5d100-9ba8-11eb-9bb9-34f076ee28a2.png)
|
||||||
|
}
|
||||||
|
|
||||||
|
// disposing writer disposes streams as well. reopen the entry to read and parse calc functions
|
||||||
|
using (var filledStream = entry.Open())
|
||||||
|
{
|
||||||
|
sheetIdx++;
|
||||||
|
_calcChainContent.Append(CalcChainHelper.GetCalcChainContent(CalcChainCellRefs, sheetIdx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var calcChain = _archive.zipFile.Entries.FirstOrDefault(e => e.FullName.Contains("xl/calcChain.xml"));
|
||||||
|
if (calcChain != null)
|
||||||
|
{
|
||||||
|
string calcChainPathname = calcChain.FullName;
|
||||||
|
calcChain.Delete();
|
||||||
|
var calcChainEntry = _archive.zipFile.CreateEntry(calcChainPathname);
|
||||||
|
using (var calcChainStream = calcChainEntry.Open())
|
||||||
|
{
|
||||||
|
CalcChainHelper.GenerateCalcChainSheet(calcChainStream, _calcChainContent.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_archive.zipFile.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SaveAsByTemplateAsync(string templatePath, object value,
|
||||||
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
return Task.Run(() => SaveAsByTemplate(templatePath, value), cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task SaveAsByTemplateAsync(byte[] templateBtyes, object value,
|
||||||
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
return Task.Run(() => SaveAsByTemplate(templateBtyes, value), cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
src/MiniExcel/SaveByTemplate/IInputValueExtractor.cs
Normal file
9
src/MiniExcel/SaveByTemplate/IInputValueExtractor.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MiniExcelLibs.OpenXml.SaveByTemplate
|
||||||
|
{
|
||||||
|
public interface IInputValueExtractor
|
||||||
|
{
|
||||||
|
IDictionary<string, object> ToValueDictionary(object valueObject);
|
||||||
|
}
|
||||||
|
}
|
43
src/MiniExcel/SaveByTemplate/InputValueExtractor.cs
Normal file
43
src/MiniExcel/SaveByTemplate/InputValueExtractor.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using MiniExcelLibs.Utils;
|
||||||
|
|
||||||
|
namespace MiniExcelLibs.OpenXml.SaveByTemplate
|
||||||
|
{
|
||||||
|
public class InputValueExtractor : IInputValueExtractor
|
||||||
|
{
|
||||||
|
public IDictionary<string, object> ToValueDictionary(object valueObject)
|
||||||
|
=> valueObject is Dictionary<string, object> valueDictionary
|
||||||
|
? GetValuesFromDictionary(valueDictionary)
|
||||||
|
: GetValuesFromObject(valueObject);
|
||||||
|
|
||||||
|
private static IDictionary<string, object> GetValuesFromDictionary(Dictionary<string, object> valueDictionary)
|
||||||
|
{
|
||||||
|
return valueDictionary
|
||||||
|
.ToDictionary(
|
||||||
|
x => x.Key,
|
||||||
|
x => x.Value is IDataReader dataReader
|
||||||
|
? TypeHelper.ConvertToEnumerableDictionary(dataReader).ToList()
|
||||||
|
: x.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IDictionary<string, object> GetValuesFromObject(object valueObject)
|
||||||
|
{
|
||||||
|
var type = valueObject.GetType();
|
||||||
|
|
||||||
|
var propertyValues = type
|
||||||
|
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||||
|
.Select(property => new { property.Name, Value = property.GetValue(valueObject) });
|
||||||
|
|
||||||
|
var fieldValues = type
|
||||||
|
.GetFields(BindingFlags.Public | BindingFlags.Instance)
|
||||||
|
.Select(field => new { field.Name, Value = field.GetValue(valueObject) });
|
||||||
|
|
||||||
|
return propertyValues
|
||||||
|
.Concat(fieldValues)
|
||||||
|
.ToDictionary(x => x.Name, x => x.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>net6.0</TargetFrameworks>
|
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<AssemblyOriginatorKeyFile>miniexcel.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>miniexcel.snk</AssemblyOriginatorKeyFile>
|
||||||
@ -22,6 +22,7 @@
|
|||||||
<PackageReference Include="EPPlus" Version="4.5.3.3" />
|
<PackageReference Include="EPPlus" Version="4.5.3.3" />
|
||||||
<PackageReference Include="ExcelDataReader" Version="3.6.0" />
|
<PackageReference Include="ExcelDataReader" Version="3.6.0" />
|
||||||
<PackageReference Include="ExcelDataReader.DataSet" Version="3.6.0" />
|
<PackageReference Include="ExcelDataReader.DataSet" Version="3.6.0" />
|
||||||
|
<PackageReference Include="FluentAssertions" Version="6.12.1" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||||
<PackageReference Include="NPOI" Version="2.7.0" />
|
<PackageReference Include="NPOI" Version="2.7.0" />
|
||||||
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
||||||
|
104
tests/MiniExcelTests/SaveByTemplate/InputValueExtractorTests.cs
Normal file
104
tests/MiniExcelTests/SaveByTemplate/InputValueExtractorTests.cs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using FluentAssertions;
|
||||||
|
using MiniExcelLibs.OpenXml.SaveByTemplate;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace MiniExcelTests.SaveByTemplate;
|
||||||
|
|
||||||
|
public class InputValueExtractorTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void ToValueDictionary_Given_InputIsDictionaryWithoutDataReader_Then_Output_IsAnEquivalentDictionary()
|
||||||
|
{
|
||||||
|
var valueDictionary = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["Name"] = "John",
|
||||||
|
["Age"] = 18,
|
||||||
|
["Fruits"] = new List<string> { "Apples, Oranges" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var sut = new InputValueExtractor();
|
||||||
|
var result = sut.ToValueDictionary(valueDictionary);
|
||||||
|
|
||||||
|
result.Should().BeEquivalentTo(valueDictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToValueDictionary_Given_InputIsDictionaryWithDataReader_Then_DataReaderIsConvertedToListOfDictionaries()
|
||||||
|
{
|
||||||
|
var dataTable = new DataTable();
|
||||||
|
|
||||||
|
dataTable.Columns.Add("id", typeof(int));
|
||||||
|
dataTable.Columns.Add("name", typeof(string));
|
||||||
|
dataTable.Rows.Add(1, "Jack");
|
||||||
|
dataTable.Rows.Add(2, "Mike");
|
||||||
|
|
||||||
|
var expectedOutput = new List<Dictionary<string, object>>
|
||||||
|
{
|
||||||
|
new() { ["id"] = 1, ["name"] = "Jack" },
|
||||||
|
new() { ["id"] = 2, ["name"] = "Mike" }
|
||||||
|
};
|
||||||
|
|
||||||
|
var valueDictionary = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["DataReader"] = dataTable.CreateDataReader()
|
||||||
|
};
|
||||||
|
|
||||||
|
var sut = new InputValueExtractor();
|
||||||
|
var result = sut.ToValueDictionary(valueDictionary);
|
||||||
|
|
||||||
|
result["DataReader"].Should().BeEquivalentTo(expectedOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToValueDictionary_Given_InputIsPocoRecord_Then_Output_IsAnEquivalentDictionary()
|
||||||
|
{
|
||||||
|
var valueObject = new PocoRecord("John", 18, new List<string> { "Apples, Oranges" });
|
||||||
|
|
||||||
|
var expectedOutput = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
["Name"] = "John",
|
||||||
|
["Age"] = 18,
|
||||||
|
["Fruits"] = new List<string> { "Apples, Oranges" }
|
||||||
|
};
|
||||||
|
|
||||||
|
var sut = new InputValueExtractor();
|
||||||
|
var result = sut.ToValueDictionary(valueObject);
|
||||||
|
|
||||||
|
result.Should().BeEquivalentTo(expectedOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ToValueDictionary_Given_InputIsPocoClass_Then_Output_IsAnEquivalentDictionary()
|
||||||
|
{
|
||||||
|
var valueObject = new PocoClass
|
||||||
|
{
|
||||||
|
Name = "John",
|
||||||
|
Age = 18,
|
||||||
|
Fruits = new List<string> { "Apples, Oranges" }
|
||||||
|
};
|
||||||
|
|
||||||
|
var expectedOutput = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
["Name"] = "John",
|
||||||
|
["Age"] = 18,
|
||||||
|
["Fruits"] = new List<string> { "Apples, Oranges" }
|
||||||
|
};
|
||||||
|
|
||||||
|
var sut = new InputValueExtractor();
|
||||||
|
var result = sut.ToValueDictionary(valueObject);
|
||||||
|
|
||||||
|
result.Should().BeEquivalentTo(expectedOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private record PocoRecord(string Name, int Age, IEnumerable<string> Fruits);
|
||||||
|
|
||||||
|
private class PocoClass
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int Age { get; set; }
|
||||||
|
public IEnumerable<string> Fruits; // Field
|
||||||
|
};
|
||||||
|
}
|
@ -10,7 +10,7 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace MiniExcelTests
|
namespace MiniExcelTests.SaveByTemplate
|
||||||
{
|
{
|
||||||
public class MiniExcelTemplateAsyncTests
|
public class MiniExcelTemplateAsyncTests
|
||||||
{
|
{
|
@ -9,7 +9,7 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace MiniExcelTests
|
namespace MiniExcelTests.SaveByTemplate
|
||||||
{
|
{
|
||||||
public class MiniExcelTemplateTests
|
public class MiniExcelTemplateTests
|
||||||
{
|
{
|
Loading…
Reference in New Issue
Block a user