Skip to content

Commit 1d4831d

Browse files
authored
Fix issues with inability to bind ranges to the entity referenced by alias or to the root entity properties (#181)
Алексей, привет. Извини, что долго не мержил это. Я был в отпуске, потом сменил работу. Сейчас очень загружен, нет ни минуты свободной.
1 parent e1915f2 commit 1d4831d

File tree

3 files changed

+140
-10
lines changed

3 files changed

+140
-10
lines changed

ClosedXML.Report/FormulaEvaluator.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using ClosedXML.Report.Utils;
2+
using DocumentFormat.OpenXml.Bibliography;
23
using System;
34
using System.Collections.Generic;
45
using System.Globalization;
@@ -29,6 +30,20 @@ public object Evaluate(string formula, params Parameter[] pars)
2930
return formula;
3031
}
3132

33+
public bool TryEvaluate(string formula, out object result, params Parameter[] pars)
34+
{
35+
try
36+
{
37+
result = Evaluate(formula, pars);
38+
return true;
39+
}
40+
catch
41+
{
42+
result = null;
43+
return false;
44+
}
45+
}
46+
3247
public void AddVariable(string name, object value)
3348
{
3449
_variables[name]=value;

ClosedXML.Report/RangeInterpreter.cs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,15 @@ public virtual void EvaluateValues(IXLRange range, params Parameter[] pars)
9494
{
9595
AddParameter(parameter.Value);
9696
}
97-
var innerRanges = range.GetContainingNames().Where(nr => _variables.ContainsKey(nr.Name)).ToArray();
97+
var innerRanges = range.GetContainingNames()
98+
.Select(BindToVariable)
99+
.Where(nr => nr != null)
100+
.ToArray();
101+
98102
var cells = range.CellsUsed()
99103
.Where(c => !c.HasFormula
100104
&& c.GetString().Contains("{{")
101-
&& !innerRanges.Any(nr => nr.Ranges.Contains(c.AsRange())))
105+
&& !innerRanges.Any(nr => nr.NamedRange.Ranges.Contains(c.AsRange())))
102106
.ToArray();
103107

104108
foreach (var cell in cells)
@@ -165,21 +169,18 @@ string EvalString(string str)
165169
}
166170

167171
foreach (var nr in innerRanges)
168-
foreach (var rng in nr.Ranges)
169172
{
173+
foreach (var rng in nr.NamedRange.Ranges)
170174
{
171-
if (!(_variables[nr.Name] is IEnumerable datas))
172-
continue;
173-
174-
var items = datas as object[] ?? datas.Cast<object>().ToArray();
175-
var tplt = RangeTemplate.Parse(nr.Name, rng, _errors, _variables);
175+
var items = nr.RangeData as object[] ?? nr.RangeData.Cast<object>().ToArray();
176+
var tplt = RangeTemplate.Parse(nr.NamedRange.Name, rng, _errors, _variables);
176177
using (var buff = tplt.Generate(items))
177178
{
178-
var ranges = nr.Ranges;
179+
var ranges = nr.NamedRange.Ranges;
179180
var trgtRng = buff.CopyTo(rng);
180181
ranges.Remove(rng);
181182
ranges.Add(trgtRng);
182-
nr.SetRefersTo(ranges);
183+
nr.NamedRange.SetRefersTo(ranges);
183184

184185
tplt.RangeTagsApply(trgtRng, items);
185186
}
@@ -222,5 +223,33 @@ public void AddVariable(string alias, object value)
222223
_variables.Add(alias, value);
223224
_evaluator.AddVariable(alias, value);
224225
}
226+
227+
private BoundRange BindToVariable(IXLNamedRange namedRange)
228+
{
229+
if (_variables.TryGetValue(namedRange.Name, out var variableValue) &&
230+
variableValue is IEnumerable data1)
231+
return new BoundRange(namedRange, data1);
232+
233+
var expression = "{{" + namedRange.Name.Replace("_", ".") +"}}";
234+
235+
if (_evaluator.TryEvaluate(expression, out var res) &&
236+
res is IEnumerable data2)
237+
return new BoundRange(namedRange, data2);
238+
239+
return null;
240+
}
241+
242+
private class BoundRange
243+
{
244+
public IXLNamedRange NamedRange { get; }
245+
246+
public IEnumerable RangeData { get; }
247+
248+
public BoundRange(IXLNamedRange namedRange, IEnumerable rangeData)
249+
{
250+
NamedRange = namedRange;
251+
RangeData = rangeData;
252+
}
253+
}
225254
}
226255
}

tests/ClosedXML.Report.Tests/RangeInterpreterTests.cs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using ClosedXML.Excel;
22
using FluentAssertions;
33
using System.Collections.Generic;
4+
using System.Linq;
45
using Xunit;
56
using Xunit.Abstractions;
67

@@ -55,5 +56,90 @@ IEnumerable<object> GenerateItems()
5556
};
5657
}
5758
}
59+
60+
[Fact]
61+
public void CanBindRangesToRootVariableFields()
62+
{
63+
var entity = new Parent();
64+
var template = CreateBaseTemplate();
65+
var ws = template.Workbook.Worksheets.First();
66+
ws.Range("A3:B4").AddToNamed("Children");
67+
ws.Range("E2:E2").AddToNamed("Container_ItemsInContainer");
68+
69+
template.AddVariable(entity);
70+
template.Generate();
71+
72+
AssertResultIsCorrect(ws);
73+
}
74+
75+
[Fact]
76+
public void CanBindRangesToAliasedVariableFields()
77+
{
78+
var entity = new Parent();
79+
var template = CreateBaseTemplate();
80+
var ws = template.Workbook.Worksheets.First();
81+
ws.Range("A3:B4").AddToNamed("Model_Children");
82+
ws.Range("E2:E2").AddToNamed("Model_Container_ItemsInContainer");
83+
84+
template.AddVariable("Model", entity);
85+
template.Generate();
86+
87+
AssertResultIsCorrect(ws);
88+
}
89+
90+
private XLTemplate CreateBaseTemplate()
91+
{
92+
var wbTemplate = new XLWorkbook();
93+
var ws = wbTemplate.AddWorksheet();
94+
95+
ws.Cell("B1").Value = "{{Model.Name}}";
96+
ws.Cell("B2").Value = "Children:";
97+
ws.Cell("B3").Value = "{{item.ChildName}}";
98+
99+
ws.Cell("D2").Value = "Items in container:";
100+
ws.Cell("E2").Value = "{{item.ChildName}}";
101+
102+
return new XLTemplate(wbTemplate);
103+
}
104+
105+
private void AssertResultIsCorrect(IXLWorksheet ws)
106+
{
107+
ws.Cell("B3").Value.Should().Be("Child 1");
108+
ws.Cell("B5").Value.Should().Be("Child 3");
109+
ws.Cell("E2").Value.Should().Be("Item in container 1");
110+
ws.Cell("G2").Value.Should().Be("Item in container 3");
111+
}
112+
113+
114+
private class Parent
115+
{
116+
public string Name => "Parent Name";
117+
public Container Container = new Container();
118+
public List<Child> Children { get; } = new List<Child>
119+
{
120+
new Child("Child 1"),
121+
new Child("Child 2"),
122+
new Child("Child 3"),
123+
};
124+
}
125+
126+
public class Child
127+
{
128+
public string ChildName { get; }
129+
public Child(string childName)
130+
{
131+
ChildName = childName;
132+
}
133+
}
134+
135+
public class Container
136+
{
137+
public List<Child> ItemsInContainer { get; } = new List<Child>
138+
{
139+
new Child("Item in container 1"),
140+
new Child("Item in container 2"),
141+
new Child("Item in container 3"),
142+
};
143+
}
58144
}
59145
}

0 commit comments

Comments
 (0)