Skip to content

Commit c07b954

Browse files
authored
Exception when using lambda expressions with the entire list of items #212 (#215)
1 parent 848e2d8 commit c07b954

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

ClosedXML.Report/FormulaEvaluator.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using ClosedXML.Report.Utils;
22
using DocumentFormat.OpenXml.Bibliography;
33
using System;
4+
using System.Collections;
45
using System.Collections.Generic;
56
using System.Globalization;
67
using System.Linq;
78
using System.Linq.Expressions;
9+
using System.Reflection;
810
using System.Text.RegularExpressions;
911

1012
namespace ClosedXML.Report
@@ -46,7 +48,27 @@ public bool TryEvaluate(string formula, out object result, params Parameter[] pa
4648

4749
public void AddVariable(string name, object value)
4850
{
49-
_variables[name] = value;
51+
if (value != null && !value.GetType().IsGenericType && value is IEnumerable enumerable)
52+
{
53+
var itemType = enumerable.GetItemType();
54+
var newEnumerable = EnumerableCastTo(enumerable, itemType);
55+
_variables[name] = newEnumerable;
56+
}
57+
else
58+
_variables[name] = value;
59+
}
60+
61+
private IEnumerable EnumerableCastTo(IEnumerable enumerable, Type itemType)
62+
{
63+
ParameterExpression source = Expression.Parameter(typeof(IEnumerable));
64+
65+
MethodInfo method = typeof(Enumerable).GetMethod(nameof(Enumerable.Cast), BindingFlags.Public | BindingFlags.Static);
66+
MethodInfo castGenericMethod = method.MakeGenericMethod(itemType);
67+
var castExpr = Expression.Call(null, castGenericMethod, source);
68+
69+
var lambda = Expression.Lambda<Func<IEnumerable, IEnumerable>>(castExpr, source);
70+
71+
return lambda.Compile().Invoke(enumerable);
5072
}
5173

5274
private string ObjToString(object val)

ClosedXML.Report/Utils/TypeExtensions.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,18 @@ public static Type GetItemType(this IEnumerable sourceList)
5050
if (result != typeof(object))
5151
return result;
5252

53-
var item = sourceList.Cast<object>().First();
53+
var item = sourceList.Cast<object>().FirstOrDefault(x => x != null);
5454
if (item == null)
5555
return typeof(object);
56-
return item.GetType();
56+
var itemType = item.GetType();
57+
return !sourceList.IsAllItemsAssignableFrom(itemType)
58+
? typeof(object)
59+
: itemType;
60+
}
61+
62+
public static bool IsAllItemsAssignableFrom(this IEnumerable list, Type type)
63+
{
64+
return list.Cast<object>().All(item => item.GetType().IsAssignableFrom(type));
5765
}
5866

5967
internal static bool IsNumeric(this Type type)

tests/ClosedXML.Report.Tests/FormulaEvaluatorTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,21 @@ public void EvalMixedArray()
153153
}
154154
}
155155

156+
[Fact]
157+
public void UsingLambdaExpressionsIssue212()
158+
{
159+
var customers = new object[10000];
160+
for (int i = 0; i < customers.Length; i++)
161+
{
162+
customers[i] = new Customer { Id = i, Name = "Customer"+i };
163+
}
164+
165+
var eval = new FormulaEvaluator();
166+
eval.AddVariable("items", customers);
167+
eval.Evaluate("{{items.Count(c => c.Id == 9999)}}").Should().Be(1);
168+
eval.Evaluate("{{items.Select(i => i.Name).Skip(1000).First()}}").Should().Be("Customer1000");
169+
}
170+
156171
class Customer
157172
{
158173
public int Id { get; set; }

0 commit comments

Comments
 (0)