Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,4 @@ __pycache__/
*.odx.cs
*.xsd.cs
nuget.txt
/Benchmark/BenchmarkDotNet.Artifacts/*
46 changes: 46 additions & 0 deletions ClosedXML.Report.Benchmarks/Benchmarks/ReportBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Reflection;
using BenchmarkDotNet.Attributes;
using ClosedXML.Report.Benchmarks.Models;

namespace ClosedXML.Report.Benchmarks.Benchmarks;

[MemoryDiagnoser]
public class ReportBenchmarks
{
private Customer _customer = null!;
private byte[] _templateData = [];
const string ResourceName = "ClosedXML.Report.Benchmarks.Resources.Benchmark.xlsx";

[GlobalSetup]
public void Setup()
{
// Load embedded resource outside of the benchmark
using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(ResourceName);
if (stream == null)
throw new Exception($"Resource {ResourceName} not found");

// Create a memory stream to hold the resource data
var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
_templateData = memoryStream.ToArray();
memoryStream.Position = 0;

// Prepare data
var dataBuilder = new DataBuilder();
_customer = dataBuilder.Create();
}

[Benchmark]
public void ReportGeneration()
{
using var memoryStream = new MemoryStream(_templateData);
using var xlTemplate = new XLTemplate(memoryStream);

// Add variables and generate report
xlTemplate.AddVariable(_customer);
xlTemplate.Generate();

//Not testing the save, as it has a high overhead.
//xlTemplate.SaveAs(Path.Combine("Output", "BenchmarkOutput.xlsx"));
}
}
25 changes: 25 additions & 0 deletions ClosedXML.Report.Benchmarks/ClosedXML.Report.Benchmarks.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>default</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
<PackageReference Include="ClosedXML" Version="0.105.0-rc" />
</ItemGroup>

<ItemGroup>
<None Remove="Resources\Benchmark.xlsx" />
<EmbeddedResource Include="Resources\Benchmark.xlsx" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ClosedXML.Report\ClosedXML.Report.csproj" />
</ItemGroup>

</Project>
27 changes: 27 additions & 0 deletions ClosedXML.Report.Benchmarks/Models/Customer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace ClosedXML.Report.Benchmarks.Models;

public class Customer
{
public required string Company { get; set; }

public required string Addr1 { get; set; }

public required string Addr2 { get; set; }


public required string City { get; set; }

public required string State { get; set; }

public required string Country { get; set; }

public required string Phone { get; set; }

public required string Email { get; set; }

public required string Zip { get; set; }

public string Fax { get; set; } = string.Empty;

public List<Order> Orders { get; init; } = [];
}
22 changes: 22 additions & 0 deletions ClosedXML.Report.Benchmarks/Models/Order.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace ClosedXML.Report.Benchmarks.Models;

public class Order
{
public required string OrderNo { get; set; }

public DateTime SaleDate { get; set; }

public DateTime ShipDate { get; set; }

public string ShipToAddr1 { get; set; } = string.Empty;

public string ShipToAddr2 { get; set; } = string.Empty;

public string PaymentMethod { get; set; } = string.Empty;

public int ItemsTotal { get; set; }

public decimal TaxRate { get; set; }

public decimal AmountPaid { get; set; }
}
58 changes: 58 additions & 0 deletions ClosedXML.Report.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using BenchmarkDotNet.Running;
using ClosedXML.Report.Benchmarks.Models;

namespace ClosedXML.Report.Benchmarks;

public class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run(typeof(Program).Assembly);
}
}

public class DataBuilder
{
public Order CreateOrder(int orderNo = 1, int amount = 10000)
{
var orderNoStr = orderNo.ToString("D6");
var isOdd = orderNo % 2 == 1;

return new Order
{
AmountPaid = amount,
ItemsTotal = isOdd ? 1 : 2,
OrderNo = orderNoStr,
PaymentMethod = isOdd ? "Credit" : "Visa",
SaleDate = DateTime.Now,
ShipDate = DateTime.Now,
ShipToAddr1 = "ShipToAddr1",
ShipToAddr2 = "ShipToAddr2",
TaxRate = 0.1m
};
}

public Customer Create()
{
var c = new Customer
{
City = "City",
Addr1 = "1 Main St",
Addr2 = "Townsville",
Company = "Company Name",
Country = "Australia",
Email = "[email protected]",
Fax = "0011 123 456 789",
Phone = "0011 123 456 789",
State = "Qld",
Zip = "4000",
Orders = []
};
for (var i = 0; i < 10000; i++)
{
c.Orders.Add(CreateOrder(i, 100 + i));
}

return c;
}
}
Binary file not shown.
7 changes: 7 additions & 0 deletions ClosedXML.Report.sln
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{8438BAA8
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClosedXML.Report.Tests", "tests\ClosedXML.Report.Tests\ClosedXML.Report.Tests.csproj", "{D529A371-3AEA-4D02-9A0D-308A6276D411}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClosedXML.Report.Benchmarks", "ClosedXML.Report.Benchmarks\ClosedXML.Report.Benchmarks.csproj", "{E61B9B2F-96AF-4B19-9327-C7B724BA0B1D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -25,13 +27,18 @@ Global
{D529A371-3AEA-4D02-9A0D-308A6276D411}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D529A371-3AEA-4D02-9A0D-308A6276D411}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D529A371-3AEA-4D02-9A0D-308A6276D411}.Release|Any CPU.Build.0 = Release|Any CPU
{E61B9B2F-96AF-4B19-9327-C7B724BA0B1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E61B9B2F-96AF-4B19-9327-C7B724BA0B1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E61B9B2F-96AF-4B19-9327-C7B724BA0B1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E61B9B2F-96AF-4B19-9327-C7B724BA0B1D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{DEF4A219-D1CF-42A2-A5AC-FE7F2F005AB0} = {885EF47A-C124-45F0-B979-303B30FB586B}
{D529A371-3AEA-4D02-9A0D-308A6276D411} = {8438BAA8-E968-4C14-9500-E3A52434E32A}
{E61B9B2F-96AF-4B19-9327-C7B724BA0B1D} = {8438BAA8-E968-4C14-9500-E3A52434E32A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {42DA0EC5-D733-4746-9613-413A64EEDC4B}
Expand Down
14 changes: 14 additions & 0 deletions ClosedXML.Report/Excel/CellPosition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace ClosedXML.Report.Excel;

struct CellPosition
{
public CellPosition(int row, int column)
{
Row = row;
Column = column;
}

public int Row { get; set; }

public int Column { get; set; }
}
2 changes: 1 addition & 1 deletion ClosedXML.Report/Excel/Subtotal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ public SubtotalGroup[] ScanForGroups(int groupBy)
var result = new List<SubtotalGroup>(grRanges.Length);
var rows = Sheet.Rows(_range.RangeAddress.FirstAddress.RowNumber, _range.RangeAddress.LastAddress.RowNumber);
if (!rows.Any())
return new SubtotalGroup[0];
return Array.Empty<SubtotalGroup>();

var level = Math.Min(8, rows.Max(r => r.OutlineLevel) + 1);

Expand Down
1 change: 0 additions & 1 deletion ClosedXML.Report/Excel/SubtotalGroup.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using ClosedXML.Excel;

namespace ClosedXML.Report.Excel
Expand Down
24 changes: 11 additions & 13 deletions ClosedXML.Report/Excel/SubtotalSummaryFunc.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq.Expressions;
using ClosedXML.Report.Utils;

namespace ClosedXML.Report.Excel
{
public class SubtotalSummaryFunc
{
private static readonly Dictionary<string, IFuncData<IAggregator>> TotalFuncs = new Dictionary<string, IFuncData<IAggregator>>
private static readonly Dictionary<string, IFuncData<IAggregator>> TotalFuncs = new()
{
{"average", new FuncData<AverageAggregator>(1)},
{"avg", new FuncData<AverageAggregator>(1)},
Expand All @@ -27,7 +26,7 @@ public class SubtotalSummaryFunc

private static IFuncData<IAggregator> GetFunc(string funcName)
{
var func = TotalFuncs.ContainsKey(funcName) ? TotalFuncs[funcName] : null;
var func = TotalFuncs.TryGetValue(funcName, out var totalFunc) ? totalFunc : null;
if (func == null)
Debug.WriteLine("Unknown function " + funcName);
return func;
Expand Down Expand Up @@ -70,7 +69,6 @@ internal object Calculate(IDataSource dataSource)
var agg = _func.CreateAggregator();

var dlg = GetCalculateDelegate(items[0].GetType());
//var dlg = lambda.Compile();
foreach (var item in items)
{
try
Expand Down Expand Up @@ -171,25 +169,25 @@ public void Aggregate(object value)

private class CountAggregator : IAggregator
{
private int _cnt = 0;
private int _cnt;
public void Aggregate(object value)
{
if (value.GetType().IsNumeric())
_cnt++;
}

public object Result { get { return _cnt; } }
public object Result => _cnt;
}

private class CountAAggregator : IAggregator
{
private int _cnt = 0;
private int _cnt;
public void Aggregate(object value)
{
_cnt++;
}

public object Result { get { return _cnt; } }
public object Result => _cnt;
}

private class MinAggregator : IAggregator
Expand All @@ -206,7 +204,7 @@ public void Aggregate(object value)
_min = value;
}

public object Result { get { return _min; } }
public object Result => _min;
}

private class MaxAggregator : IAggregator
Expand All @@ -223,12 +221,12 @@ public void Aggregate(object value)
_max = value;
}

public object Result { get { return _max; } }
public object Result => _max;
}

private class AverageAggregator : IAggregator
{
protected readonly List<object> List = new List<object>();
protected readonly List<object> List = new();

public void Aggregate(object value)
{
Expand All @@ -246,9 +244,9 @@ public virtual object Result
foreach (dynamic v in List)
sum += v;

if (sum is TimeSpan)
if (sum is TimeSpan span)
{
return TimeSpan.FromTicks(((TimeSpan)sum).Ticks / List.Count);
return TimeSpan.FromTicks(span.Ticks / List.Count);
}

return sum / List.Count;
Expand Down
Loading
Loading