Skip to content

Commit 649ca3f

Browse files
committed
fix expression parser for DataTable
1 parent 43506d8 commit 649ca3f

File tree

3 files changed

+146
-13
lines changed

3 files changed

+146
-13
lines changed

src-console/ConsoleApp_net6.0/Program.cs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Data;
34
using System.Linq;
45
using System.Linq.Dynamic.Core;
56
using System.Linq.Dynamic.Core.NewtonsoftJson;
@@ -40,7 +41,8 @@ class Program
4041
{
4142
static void Main(string[] args)
4243
{
43-
Q912();
44+
Q912a();
45+
Q912b();
4446
return;
4547

4648
Json();
@@ -66,7 +68,7 @@ static void Main(string[] args)
6668
Dynamic();
6769
}
6870

69-
private static void Q912()
71+
private static void Q912a()
7072
{
7173
var extractedRows = new List<SalesData>
7274
{
@@ -95,6 +97,39 @@ private static void Q912()
9597
int x = 9;
9698
}
9799

100+
private static void Q912b()
101+
{
102+
var eInfoJoinTable = new DataTable();
103+
eInfoJoinTable.Columns.Add("Region", typeof(string));
104+
eInfoJoinTable.Columns.Add("Product", typeof(string));
105+
eInfoJoinTable.Columns.Add("Sales", typeof(int));
106+
107+
eInfoJoinTable.Rows.Add("North", "Apples", 100);
108+
eInfoJoinTable.Rows.Add("North", "Oranges", 150);
109+
eInfoJoinTable.Rows.Add("South", "Apples", 200);
110+
eInfoJoinTable.Rows.Add("South", "Oranges", 250);
111+
112+
var extractedRows =
113+
from row in eInfoJoinTable.AsEnumerable()
114+
select row;
115+
116+
var rows = extractedRows.AsQueryable();
117+
118+
// GROUPING SET 1: (Region, Product)
119+
var detailed = rows
120+
.GroupBy("new (Region, Product)")
121+
.Select("new (Key.Region as Region, Key.Product as Product, Sum(Convert.ToInt32(Sales)) as TotalSales, 0 as GroupLevel)");
122+
123+
// GROUPING SET 2: (Region)
124+
var regionSubtotal = rows
125+
.GroupBy("Region")
126+
.Select("new (Key as Region, null as Product, Sum(Convert.ToInt32(Sales)) as TotalSales, 1 as GroupLevel)");
127+
128+
var combined = detailed.ToDynamicArray().Concat(regionSubtotal.ToDynamicArray()).AsQueryable();
129+
var ordered = combined.OrderBy("Product").ToDynamicList();
130+
131+
int x = 9;
132+
}
98133

99134
private static void NewtonsoftJson()
100135
{

src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,9 +1609,9 @@ private Expression CreateNewExpression(List<DynamicProperty> properties, List<Ex
16091609

16101610
// Option 2. Call the default (empty) constructor and set the members
16111611
var memberBindings = new MemberBinding[properties.Count];
1612-
for (int i = 0; i < memberBindings.Length; i++)
1612+
for (var i = 0; i < memberBindings.Length; i++)
16131613
{
1614-
string propertyOrFieldName = properties[i].Name;
1614+
var propertyOrFieldName = properties[i].Name;
16151615
Type propertyOrFieldType;
16161616
MemberInfo memberInfo;
16171617
var propertyInfo = type.GetProperty(propertyOrFieldName);
@@ -1632,12 +1632,8 @@ private Expression CreateNewExpression(List<DynamicProperty> properties, List<Ex
16321632
propertyOrFieldType = fieldInfo.FieldType;
16331633
}
16341634

1635-
// Promote from Type to Nullable Type if needed
1636-
var promoted = _parsingConfig.ExpressionPromoter.Promote(expressions[i], propertyOrFieldType, true, true);
1637-
if (promoted is null)
1638-
{
1639-
throw new NotSupportedException($"Unable to promote expression '{expressions[i]}'.");
1640-
}
1635+
// Call Promote and if that returns false, just try to convert the expression to the destination type using Expression.Convert
1636+
var promoted = _parsingConfig.ExpressionPromoter.Promote(expressions[i], propertyOrFieldType, true, true) ?? Expression.Convert(expressions[i], propertyOrFieldType);
16411637
memberBindings[i] = Expression.Bind(memberInfo, promoted);
16421638
}
16431639

test/System.Linq.Dynamic.Core.Tests/DynamicGetMemberBinderTests.cs

Lines changed: 105 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using FluentAssertions;
1+
using System.Data;
2+
using FluentAssertions;
23
using Xunit;
34

45
namespace System.Linq.Dynamic.Core.Tests;
@@ -14,8 +15,16 @@ public class SalesData
1415
public string Sales { get; set; } = null!;
1516
}
1617

18+
public class GroupedSalesData
19+
{
20+
public string Region { get; set; } = null!;
21+
public string? Product { get; set; }
22+
public int TotalSales { get; set; }
23+
public int GroupLevel { get; set; }
24+
}
25+
1726
[Fact]
18-
public void DynamicGetMemberBinder_Test1()
27+
public void DynamicGetMemberBinder_SelectOnArrayWithComplexObjects()
1928
{
2029
// Arrange
2130
var rows = new SalesData[]
@@ -44,7 +53,100 @@ public void DynamicGetMemberBinder_Test1()
4453
}
4554

4655
[Fact]
47-
public void DynamicGetMemberBinder_Test2()
56+
public void DynamicGetMemberBinder_SelectTypeOnArrayWithComplexObjects()
57+
{
58+
// Arrange
59+
var rows = new SalesData[]
60+
{
61+
new() { Region = "North", Product = "Widget", Sales = "100" },
62+
new() { Region = "North", Product = "Gadget", Sales = "150" },
63+
new() { Region = "South", Product = "Widget", Sales = "200" },
64+
new() { Region = "South", Product = "Gadget", Sales = "100" },
65+
new() { Region = "North", Product = "Widget", Sales = "50" }
66+
}.AsQueryable();
67+
68+
// Act
69+
var grouping1 = rows
70+
.GroupBy("new (Region, Product)")
71+
.Select<GroupedSalesData>("new (Key.Region as Region, Key.Product as Product, Sum(Convert.ToInt32(Sales)) as TotalSales, 0 as GroupLevel)");
72+
73+
var grouping2 = rows
74+
.GroupBy("Region")
75+
.Select<GroupedSalesData>("new (Key as Region, null as Product, Sum(Convert.ToInt32(Sales)) as TotalSales, 1 as GroupLevel)");
76+
77+
var combined = grouping1.Concat(grouping2).AsQueryable();
78+
var ordered = combined.OrderBy("Product").ToDynamicList();
79+
80+
// Assert
81+
ordered.Should().HaveCount(6);
82+
}
83+
84+
[Fact]
85+
public void DynamicGetMemberBinder_SelectOnDataTable()
86+
{
87+
// Arrange
88+
var dataTable = new DataTable();
89+
dataTable.Columns.Add("Region", typeof(string));
90+
dataTable.Columns.Add("Product", typeof(string));
91+
dataTable.Columns.Add("Sales", typeof(int));
92+
93+
dataTable.Rows.Add("North", "Apples", 100);
94+
dataTable.Rows.Add("North", "Oranges", 150);
95+
dataTable.Rows.Add("South", "Apples", 200);
96+
dataTable.Rows.Add("South", "Oranges", 250);
97+
98+
var rows = dataTable.Rows.Cast<DataRow>().AsQueryable();
99+
100+
// Act
101+
var grouping1 = rows
102+
.GroupBy("new (Region, Product)")
103+
.Select("new (Key.Region as Region, Key.Product as Product, Sum(Convert.ToInt32(Sales)) as TotalSales, 0 as GroupLevel)");
104+
105+
var grouping2 = rows
106+
.GroupBy("Region")
107+
.Select("new (Key as Region, null as Product, Sum(Convert.ToInt32(Sales)) as TotalSales, 1 as GroupLevel)");
108+
109+
var combined = grouping1.ToDynamicArray().Concat(grouping2.ToDynamicArray()).AsQueryable();
110+
var ordered = combined.OrderBy("Product").ToDynamicList();
111+
112+
// Assert
113+
ordered.Should().HaveCount(6);
114+
}
115+
116+
[Fact]
117+
public void DynamicGetMemberBinder_SelectTypeOnDataTable()
118+
{
119+
// Arrange
120+
var dataTable = new DataTable();
121+
dataTable.Columns.Add("Region", typeof(string));
122+
dataTable.Columns.Add("Product", typeof(string));
123+
dataTable.Columns.Add("Sales", typeof(int));
124+
125+
dataTable.Rows.Add("North", "Apples", 100);
126+
dataTable.Rows.Add("North", "Oranges", 150);
127+
dataTable.Rows.Add("South", "Apples", 200);
128+
dataTable.Rows.Add("South", "Oranges", 250);
129+
130+
var rows = dataTable.Rows.Cast<DataRow>().AsQueryable();
131+
132+
// Act
133+
var grouping1 = rows
134+
.GroupBy("new (Region, Product)")
135+
.Select<GroupedSalesData>("new (Key.Region as Region, Key.Product as Product, Sum(Convert.ToInt32(Sales)) as TotalSales, 0 as GroupLevel)");
136+
137+
var grouping2 = rows
138+
.GroupBy("Region")
139+
.Select<GroupedSalesData>("new (Key as Region, null as Product, Sum(Convert.ToInt32(Sales)) as TotalSales, 1 as GroupLevel)");
140+
141+
var combined = grouping1.ToDynamicArray().Concat(grouping2.ToDynamicArray()).AsQueryable();
142+
var ordered = combined.OrderBy("Product").ToDynamicList();
143+
144+
// Assert
145+
ordered.Should().HaveCount(6);
146+
}
147+
148+
[Fact]
149+
public void DynamicGetMemberBinder_SelectOnArrayWithIntegers()
48150
{
49151
// Arrange
50152
var dynamicData = new[] { 1, 2 }

0 commit comments

Comments
 (0)