Skip to content

Commit ed51756

Browse files
committed
Merge branch 'master' into stef-PredefinedMethodsHelper
2 parents 7688690 + 195bc16 commit ed51756

File tree

12 files changed

+320
-21
lines changed

12 files changed

+320
-21
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# v1.6.2 (24 April 2025)
2+
- [#913](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/913) - Update DynamicGetMemberBinder to add BindingRestrictions to DynamicMetaObject [feature] contributed by [StefH](https://github.com/StefH)
3+
- [#907](https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/907) - Select("...") from an IQueryable of anonymous objects created via Select("new { ... }") throws InvalidOperationException [bug]
4+
- [#912](https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/912) - Concat question [bug]
5+
16
# v1.6.0.2 (11 February 2025)
27
- [#896](https://github.com/zzzprojects/System.Linq.Dynamic.Core/pull/896) - Fix AbstractDynamicLinqCustomTypeProvider.ResolveTypeBySimpleName to use AdditionalTypes [bug] contributed by [StefH](https://github.com/StefH)
38

Generate-ReleaseNotes.bat

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
rem https://github.com/StefH/GitHubReleaseNotes
22

3-
SET version=v1.6.0.2
3+
SET version=v1.6.2
44

5-
GitHubReleaseNotes --output CHANGELOG.md --exclude-labels known_issue out_of_scope invalid question documentation wontfix environment duplicate --language en --version %version% --token %GH_TOKEN%
5+
GitHubReleaseNotes --output CHANGELOG.md --exclude-labels known_issue out_of_scope not_planned invalid question documentation wontfix environment duplicate --language en --version %version% --token %GH_TOKEN%

src-console/ConsoleAppEF6_InMemory/Program.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ static async Task Main(string[] args)
2626
await using (var context = new TestContextEF6())
2727
{
2828
var result784 = context.Products.Where("NullableInt = @0", 1).ToDynamicArray<ProductDynamic>();
29-
Console.WriteLine("a1 {0}", string.Join(",", result784.Select(r => r.Key)));
29+
Console.WriteLine("a1 {0}", string.Join(", ", result784.Select(r => r.Key)));
3030
}
3131

3232
await using (var context = new TestContextEF6())
@@ -65,5 +65,20 @@ static async Task Main(string[] args)
6565
Console.WriteLine(result.Key + ":" + JsonSerializer.Serialize(result.Dict, JsonSerializerOptions));
6666
}
6767
}
68+
69+
// #907 and #912
70+
await using (var context = new TestContextEF6())
71+
{
72+
var dynamicData = context.Products
73+
.AsQueryable()
74+
.Select("new { NullableInt as Value }")
75+
.ToDynamicArray();
76+
var dynamicResult = dynamicData
77+
.AsQueryable()
78+
.Select("Value")
79+
.ToDynamicArray();
80+
81+
Console.WriteLine("#907 and #912 = {0}", string.Join(", ", dynamicResult));
82+
}
6883
}
6984
}

src-console/ConsoleApp_net6.0/Program.cs

Lines changed: 84 additions & 1 deletion
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;
@@ -21,10 +22,29 @@ public class Y
2122
{
2223
}
2324

25+
public class SalesData
26+
{
27+
public string Region { get; set; }
28+
public string Product { get; set; }
29+
public string Sales { get; set; }
30+
}
31+
32+
public class GroupedSalesData
33+
{
34+
public string Region { get; set; }
35+
public string? Product { get; set; }
36+
public int TotalSales { get; set; }
37+
public int GroupLevel { get; set; }
38+
}
39+
2440
class Program
2541
{
2642
static void Main(string[] args)
2743
{
44+
Q912a();
45+
Q912b();
46+
return;
47+
2848
Json();
2949
NewtonsoftJson();
3050

@@ -39,7 +59,7 @@ static void Main(string[] args)
3959
{
4060
new X { Key = "x" },
4161
new X { Key = "a" },
42-
new X { Key = "a", Contestants = new List<Y> { new Y() } }
62+
new X { Key = "a", Contestants = new List<Y> { new() } }
4363
}.AsQueryable();
4464
var groupByKey = q.GroupBy("Key");
4565
var selectQry = groupByKey.Select("new (Key, Sum(np(Contestants.Count, 0)) As TotalCount)").ToDynamicList();
@@ -48,6 +68,69 @@ static void Main(string[] args)
4868
Dynamic();
4969
}
5070

71+
private static void Q912a()
72+
{
73+
var extractedRows = new List<SalesData>
74+
{
75+
new() { Region = "North", Product = "Widget", Sales = "100" },
76+
new() { Region = "North", Product = "Gadget", Sales = "150" },
77+
new() { Region = "South", Product = "Widget", Sales = "200" },
78+
new() { Region = "South", Product = "Gadget", Sales = "100" },
79+
new() { Region = "North", Product = "Widget", Sales = "50" }
80+
};
81+
82+
var rows = extractedRows.AsQueryable();
83+
84+
// GROUPING SET 1: (Region, Product)
85+
var detailed = rows
86+
.GroupBy("new (Region, Product)")
87+
.Select<GroupedSalesData>("new (Key.Region as Region, Key.Product as Product, Sum(Convert.ToInt32(Sales)) as TotalSales, 0 as GroupLevel)");
88+
89+
// GROUPING SET 2: (Region)
90+
var regionSubtotal = rows
91+
.GroupBy("Region")
92+
.Select<GroupedSalesData>("new (Key as Region, null as Product, Sum(Convert.ToInt32(Sales)) as TotalSales, 1 as GroupLevel)");
93+
94+
var combined = detailed.Concat(regionSubtotal).AsQueryable();
95+
var ordered = combined.OrderBy("Product").ToDynamicList();
96+
97+
int x = 9;
98+
}
99+
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+
}
133+
51134
private static void NewtonsoftJson()
52135
{
53136
var array = JArray.Parse(@"[

src/System.Linq.Dynamic.Core/DynamicGetMemberBinder.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ public DynamicGetMemberBinder(string name, ParsingConfig? config) : base(name, c
2121

2222
public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject? errorSuggestion)
2323
{
24-
var instance = Expression.Call(
24+
var methodCallExpression = Expression.Call(
2525
DynamicGetMemberMethod,
2626
target.Expression,
2727
Expression.Constant(Name),
2828
Expression.Constant(IgnoreCase));
2929

30-
return DynamicMetaObject.Create(target.Value!, instance);
30+
// Fix #907 and #912: "The result of the dynamic binding produced by the object with type '<>f__AnonymousType1`4' for the binder 'System.Linq.Dynamic.Core.DynamicGetMemberBinder' needs at least one restriction.".
31+
var restrictions = BindingRestrictions.GetInstanceRestriction(target.Expression, target.Value);
32+
return new DynamicMetaObject(methodCallExpression, restrictions, target.Value!);
3133
}
3234

3335
public static object? GetDynamicMember(object value, string name, bool ignoreCase)

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

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

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

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

test/EntityFramework.DynamicLinq.Tests/EntityFramework.DynamicLinq.Tests.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
<Compile Include="..\System.Linq.Dynamic.Core.Tests\TestClasses\*.cs" />
3030
</ItemGroup>
3131

32+
<ItemGroup>
33+
<Compile Remove="..\System.Linq.Dynamic.Core.Tests\TestHelpers\SkipIfGitHubActionsAttribute.cs" />
34+
</ItemGroup>
35+
3236
<ItemGroup>
3337
<ProjectReference Include="..\..\src\System.Linq.Dynamic.Core\System.Linq.Dynamic.Core.csproj" />
3438
<ProjectReference Include="..\..\src\EntityFramework.DynamicLinq\EntityFramework.DynamicLinq.csproj" />

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ public void DynamicClass_GetRuntimeType()
217217
typeOf.ToString().Should().Be("System.Linq.Dynamic.Core.DynamicClass");
218218
}
219219

220-
[SkipIfGitHubActions]
220+
[SkipIfGitHubActionsFact]
221221
public void DynamicClassArray()
222222
{
223223
// Arrange
@@ -249,7 +249,7 @@ public void DynamicClassArray()
249249
isValid.Should().BeTrue();
250250
}
251251

252-
[SkipIfGitHubActions]
252+
[SkipIfGitHubActionsFact]
253253
public void DynamicClassArray_Issue593_Fails()
254254
{
255255
// Arrange
@@ -281,7 +281,7 @@ public void DynamicClassArray_Issue593_Fails()
281281
isValid.Should().BeFalse(); // This should actually be true, but fails. For solution see Issue593_Solution1 and Issue593_Solution2.
282282
}
283283

284-
[SkipIfGitHubActions]
284+
[SkipIfGitHubActionsFact]
285285
public void DynamicClassArray_Issue593_Solution1()
286286
{
287287
// Arrange
@@ -318,7 +318,7 @@ public void DynamicClassArray_Issue593_Solution1()
318318
isValid.Should().BeTrue();
319319
}
320320

321-
[SkipIfGitHubActions]
321+
[SkipIfGitHubActionsFact]
322322
public void DynamicClassArray_Issue593_Solution2()
323323
{
324324
// Arrange

0 commit comments

Comments
 (0)