Skip to content

Commit 6be5de6

Browse files
authored
Fixed a performance issue caused by a large number of merged ranges. (#246 Three lists generation on one sheet hangs) (#247)
1 parent 2817e4a commit 6be5de6

File tree

4 files changed

+107
-3
lines changed

4 files changed

+107
-3
lines changed

ClosedXML.Report/Excel/XlExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,11 @@ private static bool GetContainingRanges(IXLNamedRange x, IXLRange xlRange)
138138
public static IXLRange GrowToMergedRanges(this IXLRange range)
139139
{
140140
var sheet = range.Worksheet;
141-
sheet.MergedRanges.Where(range.Intersects)
141+
sheet.MergedRanges.Where(range.Intersects).Where(x => !range.Contains(x))
142142
.ForEach(x =>
143143
{
144144
var xlCells = range.Union(x).Select(c => c.Address)
145-
.OrderBy(c => c.RowNumber).ThenBy(c => c.ColumnNumber).ToArray();
145+
.OrderBy(c => c.RowNumber).ThenBy(c => c.ColumnNumber);
146146
range = sheet.Range(xlCells.First().ToStringFixed(), xlCells.Last().ToStringFixed());
147147
});
148148
return range;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Bogus;
5+
using Bogus.DataSets;
6+
using Bogus.Extensions;
7+
8+
namespace ClosedXML.Report.Tests.TestModels
9+
{
10+
public enum Gender
11+
{
12+
Male,
13+
Female
14+
}
15+
16+
public class User
17+
{
18+
public static List<User> Generate(int count)
19+
{
20+
var fruit = new[] {"apple", "banana", "orange", "strawberry", "kiwi"};
21+
22+
var orderIds = 0;
23+
var testOrders = new Faker<Order>()
24+
.RuleFor(o => o.OrderId, f => orderIds++)
25+
.RuleFor(o => o.Item, f => f.PickRandom(fruit))
26+
.RuleFor(o => o.Quantity, f => f.Random.Number(1, 10))
27+
.RuleFor(o => o.OrderDate, f => f.Date.Past())
28+
.RuleFor(o => o.ShipDate, f => f.Date.Recent(40))
29+
//A nullable int? with 80% probability of being null.
30+
.RuleFor(o => o.LotNumber, f => f.Random.Int(0, 100).OrNull(f, .8f));
31+
32+
33+
var rnd = new Randomizer();
34+
var userIds = 0;
35+
var testUsers = new Faker<User>()
36+
.CustomInstantiator(f => new User(userIds++, f.Random.Replace("###-##-####")))
37+
38+
.RuleFor(u => u.Gender, f => f.PickRandom<Gender>())
39+
40+
.RuleFor(u => u.FirstName, (f, u) => f.Name.FirstName((Name.Gender?) u.Gender))
41+
.RuleFor(u => u.LastName, (f, u) => f.Name.LastName((Name.Gender?) u.Gender))
42+
.RuleFor(u => u.Avatar, f => f.Internet.Avatar())
43+
.RuleFor(u => u.UserName, (f, u) => f.Internet.UserName(u.FirstName, u.LastName))
44+
.RuleFor(u => u.Email, (f, u) => f.Internet.Email(u.FirstName, u.LastName))
45+
.RuleFor(u => u.SomethingUnique, f => $"Value {f.UniqueIndex}")
46+
47+
.RuleFor(u => u.CartId, f => Guid.NewGuid())
48+
.RuleFor(u => u.FullName, (f, u) => u.FirstName + " " + u.LastName)
49+
.RuleFor(u => u.Orders, f => testOrders.Generate(rnd.Int(1, 5)).ToList())
50+
.FinishWith((f, u) => { Console.WriteLine("User Created! Id={0}", u.Id); });
51+
52+
return testUsers.Generate(count);
53+
}
54+
55+
public User(int id, string phoneNumber)
56+
{
57+
Id = id;
58+
PhoneNumber = phoneNumber;
59+
}
60+
61+
public int Id { get; }
62+
public string PhoneNumber { get; }
63+
public Gender Gender { get; set; }
64+
public string FirstName { get; set; }
65+
public string LastName { get; set; }
66+
public string Avatar { get; set; }
67+
public string UserName { get; set; }
68+
public string Email { get; set; }
69+
public string SomethingUnique { get; set; }
70+
public Guid CartId { get; set; }
71+
public string FullName { get; set; }
72+
public List<Order> Orders { get; set; }
73+
}
74+
75+
public class Order
76+
{
77+
public int OrderId { get; set; }
78+
public string Item { get; set; }
79+
public int Quantity { get; set; }
80+
public int? LotNumber { get; set; }
81+
public DateTime OrderDate { get; set; }
82+
public DateTime ShipDate { get; set; }
83+
}
84+
}

tests/ClosedXML.Report.Tests/XlTemplateTests.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Collections.Generic;
77
using System.IO;
88
using System.Linq;
9+
using Bogus;
910
using ClosedXML.Report.Excel;
1011
using Xunit;
1112
using Xunit.Abstractions;
@@ -18,6 +19,26 @@ public XlTemplateTests(ITestOutputHelper output) : base(output)
1819
{
1920
}
2021

22+
[Fact]
23+
public void Issue246()
24+
{
25+
//Set the randomizer seed if you wish to generate repeatable data sets.
26+
Randomizer.Seed = new Random(8675309);
27+
var user = User.Generate(1).First();
28+
var entities = TestEntity.GetTestData(300);
29+
30+
XlTemplateTest("SpreadReportTemplate.xlsx",
31+
tpl =>
32+
{
33+
tpl.AddVariable(user);
34+
tpl.AddVariable("EquipmentItemList", entities);
35+
tpl.AddVariable("VehicleList", entities);
36+
},
37+
wb =>
38+
{
39+
});
40+
}
41+
2142
[Fact]
2243
public void Add_simple_variable_should_replace_value_in_related_cell()
2344
{
@@ -88,7 +109,6 @@ public void Add_enumerable_variable_should_fill_range()
88109
}),
89110
wb =>
90111
{
91-
wb.SaveAs(".\\Output\\4.xlsx");
92112
var sheet = wb.Worksheet(1);
93113
sheet.Cell("H1").GetValue<string>().Should().Be("title from test");
94114
sheet.Cell("B4").GetValue<string>().Should().Be("1");
43.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)