Skip to content

Commit a027d6c

Browse files
committed
Merge branch 'master' into not-in
2 parents 47936f9 + 5540fd1 commit a027d6c

File tree

7 files changed

+160
-11
lines changed

7 files changed

+160
-11
lines changed

System.Linq.Dynamic.Core.sln

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.EntityFrameworkCo
159159
EndProject
160160
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Linq.Dynamic.Core.Tests.Net8", "test\System.Linq.Dynamic.Core.Tests.Net8\System.Linq.Dynamic.Core.Tests.Net8.csproj", "{CEBE3A33-4814-42A4-BD8E-F7F2308A4C8C}"
161161
EndProject
162+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleAppPerformanceTest", "src-console\ConsoleAppPerformanceTest\ConsoleAppPerformanceTest.csproj", "{067C00CF-29FA-4643-814D-3A3C3C84634F}"
163+
EndProject
162164
Global
163165
GlobalSection(SolutionConfigurationPlatforms) = preSolution
164166
Debug|Any CPU = Debug|Any CPU
@@ -1039,6 +1041,22 @@ Global
10391041
{CEBE3A33-4814-42A4-BD8E-F7F2308A4C8C}.Release|x64.Build.0 = Release|Any CPU
10401042
{CEBE3A33-4814-42A4-BD8E-F7F2308A4C8C}.Release|x86.ActiveCfg = Release|Any CPU
10411043
{CEBE3A33-4814-42A4-BD8E-F7F2308A4C8C}.Release|x86.Build.0 = Release|Any CPU
1044+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1045+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Debug|Any CPU.Build.0 = Debug|Any CPU
1046+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Debug|ARM.ActiveCfg = Debug|Any CPU
1047+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Debug|ARM.Build.0 = Debug|Any CPU
1048+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Debug|x64.ActiveCfg = Debug|Any CPU
1049+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Debug|x64.Build.0 = Debug|Any CPU
1050+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Debug|x86.ActiveCfg = Debug|Any CPU
1051+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Debug|x86.Build.0 = Debug|Any CPU
1052+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Release|Any CPU.ActiveCfg = Release|Any CPU
1053+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Release|Any CPU.Build.0 = Release|Any CPU
1054+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Release|ARM.ActiveCfg = Release|Any CPU
1055+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Release|ARM.Build.0 = Release|Any CPU
1056+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Release|x64.ActiveCfg = Release|Any CPU
1057+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Release|x64.Build.0 = Release|Any CPU
1058+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Release|x86.ActiveCfg = Release|Any CPU
1059+
{067C00CF-29FA-4643-814D-3A3C3C84634F}.Release|x86.Build.0 = Release|Any CPU
10421060
EndGlobalSection
10431061
GlobalSection(SolutionProperties) = preSolution
10441062
HideSolutionNode = FALSE
@@ -1098,6 +1116,7 @@ Global
10981116
{7A31366C-DD98-41A3-A0C1-A97068BD9658} = {BCA2A024-9032-4E56-A6C4-17A15D921728}
10991117
{C774DAE7-54A0-4FCD-A3B7-3CB63D7E112D} = {DBD7D9B6-FCC7-4650-91AF-E6457573A68F}
11001118
{CEBE3A33-4814-42A4-BD8E-F7F2308A4C8C} = {8463ED7E-69FB-49AE-85CF-0791AFD98E38}
1119+
{067C00CF-29FA-4643-814D-3A3C3C84634F} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62}
11011120
EndGlobalSection
11021121
GlobalSection(ExtensibilityGlobals) = postSolution
11031122
SolutionGuid = {94C56722-194E-4B8B-BC23-B3F754E89A20}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net48</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<LangVersion>12</LangVersion>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<ProjectReference Include="..\..\src\System.Linq.Dynamic.Core\System.Linq.Dynamic.Core.csproj" />
13+
</ItemGroup>
14+
15+
</Project>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.Linq.Dynamic.Core;
2+
3+
TestDynamic();
4+
return;
5+
6+
static void TestDynamic()
7+
{
8+
var list = new List<Demo>();
9+
for (int i = 0; i < 100000; i++)
10+
{
11+
list.Add(new Demo { ID = i, Name = $"Name {i}", Description = $"Description {i}" });
12+
}
13+
14+
var xTimeAll = System.Diagnostics.Stopwatch.StartNew();
15+
var query = list.AsQueryable().Select(typeof(Demo), "new { ID, Name }").ToDynamicList();
16+
Console.WriteLine($"Total 1st Query: {(int)xTimeAll.Elapsed.TotalMilliseconds}ms");
17+
18+
xTimeAll.Restart();
19+
_ = query.AsQueryable().Select("ID").Cast<int>().ToList();
20+
Console.WriteLine($"Total 2nd Query: {(int)xTimeAll.Elapsed.TotalMilliseconds}ms");
21+
22+
xTimeAll.Restart();
23+
_ = query.AsQueryable().Select("new { it.ID as Idee } ").ToDynamicList();
24+
Console.WriteLine($"Total 3rd Query: {(int)xTimeAll.Elapsed.TotalMilliseconds}ms");
25+
}
26+
27+
class Demo
28+
{
29+
public int ID { get; set; }
30+
public string Name { get; set; }
31+
public string Description { get; set; }
32+
}
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
55
<TargetFramework>net6.0</TargetFramework>
66
<RootNamespace>ConsoleApp_net6._0</RootNamespace>
77
<Nullable>enable</Nullable>
8+
<LangVersion>12</LangVersion>
89
</PropertyGroup>
910

1011
<ItemGroup>
11-
<ProjectReference Include="..\..\src\System.Linq.Dynamic.Core.NewtonsoftJson\System.Linq.Dynamic.Core.NewtonsoftJson.csproj" />
12-
<ProjectReference Include="..\..\src\System.Linq.Dynamic.Core.SystemTextJson\System.Linq.Dynamic.Core.SystemTextJson.csproj" />
12+
<ProjectReference Include="..\..\src\System.Linq.Dynamic.Core.NewtonsoftJson\System.Linq.Dynamic.Core.NewtonsoftJson.csproj" />
13+
<ProjectReference Include="..\..\src\System.Linq.Dynamic.Core.SystemTextJson\System.Linq.Dynamic.Core.SystemTextJson.csproj" />
1314
</ItemGroup>
1415

15-
</Project>
16+
</Project>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
using System.Collections;
3+
4+
namespace ConsoleApp_net6._0;
5+
6+
public class DataColumnOrdinalIgnoreCaseComparer : IComparer
7+
{
8+
public int Compare(object? x, object? y)
9+
{
10+
if (x == null && y == null)
11+
{
12+
return 0;
13+
}
14+
15+
if (x == null)
16+
{
17+
return -1;
18+
}
19+
20+
if (y == null)
21+
{
22+
return 1;
23+
}
24+
25+
if (x is string xAsString && y is string yAsString)
26+
{
27+
return StringComparer.OrdinalIgnoreCase.Compare(xAsString, yAsString);
28+
}
29+
30+
return Comparer.Default.Compare(x, y);
31+
}
32+
}

src-console/ConsoleApp_net6.0/Program.cs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,11 @@ class Program
4141
{
4242
static void Main(string[] args)
4343
{
44-
Q912a();
45-
Q912b();
44+
Issue918();
45+
return;
46+
47+
Issue912a();
48+
Issue912b();
4649
return;
4750

4851
Json();
@@ -68,7 +71,33 @@ static void Main(string[] args)
6871
Dynamic();
6972
}
7073

71-
private static void Q912a()
74+
private static void Issue918()
75+
{
76+
var persons = new DataTable();
77+
persons.Columns.Add("FirstName", typeof(string));
78+
persons.Columns.Add("Nickname", typeof(string));
79+
persons.Columns.Add("Income", typeof(decimal)).AllowDBNull = true;
80+
81+
// Adding sample data to the first DataTable
82+
persons.Rows.Add("alex", DBNull.Value, 5000.50m);
83+
persons.Rows.Add("MAGNUS", "Mag", 5000.50m);
84+
persons.Rows.Add("Terry", "Ter", 4000.20m);
85+
persons.Rows.Add("Charlotte", "Charl", DBNull.Value);
86+
87+
var linqQuery =
88+
from personsRow in persons.AsEnumerable()
89+
select personsRow;
90+
91+
var queryableRows = linqQuery.AsQueryable();
92+
93+
// Sorted at the top of the list
94+
var comparer = new DataColumnOrdinalIgnoreCaseComparer();
95+
var sortedRows = queryableRows.OrderBy("FirstName", comparer).ToList();
96+
97+
int xxx = 0;
98+
}
99+
100+
private static void Issue912a()
72101
{
73102
var extractedRows = new List<SalesData>
74103
{
@@ -97,7 +126,7 @@ private static void Q912a()
97126
int x = 9;
98127
}
99128

100-
private static void Q912b()
129+
private static void Issue912b()
101130
{
102131
var eInfoJoinTable = new DataTable();
103132
eInfoJoinTable.Columns.Add("Region", typeof(string));

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#if !NET35 && !UAP10_0 && !NETSTANDARD1_3
22
using System.Collections;
3+
using System.Collections.Concurrent;
34
using System.Collections.Generic;
45
using System.Dynamic;
56
using System.Linq.Expressions;
@@ -15,7 +16,15 @@ internal class DynamicGetMemberBinder : GetMemberBinder
1516
{
1617
private static readonly MethodInfo DynamicGetMemberMethod = typeof(DynamicGetMemberBinder).GetMethod(nameof(GetDynamicMember))!;
1718

18-
public DynamicGetMemberBinder(string name, ParsingConfig? config) : base(name, config?.IsCaseSensitive != true)
19+
// The _metaObjectCache uses a Tuple<Type, string, bool> as the key to cache DynamicMetaObject instances.
20+
// The key components are:
21+
// - Type: The LimitType of the target object, ensuring type-specific caching.
22+
// - string: The member name being accessed.
23+
// - bool: The IgnoreCase flag, indicating whether the member name comparison is case-insensitive.
24+
// This strategy ensures that the cache correctly handles different types, member names, and case-sensitivity settings.
25+
private readonly ConcurrentDictionary<Tuple<Type, string, bool>, DynamicMetaObject> _metaObjectCache = new();
26+
27+
internal DynamicGetMemberBinder(string name, ParsingConfig? config) : base(name, config?.IsCaseSensitive != true)
1928
{
2029
}
2130

@@ -28,8 +37,20 @@ public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, Dy
2837
Expression.Constant(IgnoreCase));
2938

3039
// 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!);
40+
// Fix #921: "Slow Performance"
41+
// Only add TypeRestriction if it's a Dynamic type and make sure to cache the DynamicMetaObject.
42+
if (target.Value is IDynamicMetaObjectProvider)
43+
{
44+
var key = new Tuple<Type, string, bool>(target.LimitType, Name, IgnoreCase);
45+
46+
return _metaObjectCache.GetOrAdd(key, _ =>
47+
{
48+
var restrictions = BindingRestrictions.GetTypeRestriction(target.Expression, target.LimitType);
49+
return new DynamicMetaObject(methodCallExpression, restrictions, target.Value);
50+
});
51+
}
52+
53+
return DynamicMetaObject.Create(target.Value!, methodCallExpression);
3354
}
3455

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

0 commit comments

Comments
 (0)