Skip to content

Commit 807df3e

Browse files
authored
By default, also support WellKnownTypes like StringComparer (#984)
* By default, also support WellKnownTypes like StringComparer * .
1 parent 16d0eb5 commit 807df3e

5 files changed

Lines changed: 147 additions & 61 deletions

File tree

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using System.Collections.Concurrent;
2+
using System.Collections.Generic;
3+
using System.Reflection;
4+
using System.Xml;
5+
using System.Xml.Linq;
6+
7+
namespace System.Linq.Dynamic.Core.Parser;
8+
9+
internal static class EnumerationsAndWellKnownTypesFromMscorlib
10+
{
11+
private readonly static string SystemPrivateCoreLib = typeof(StringComparer).GetTypeInfo().Assembly.FullName!;
12+
private readonly static string SystemPrivateUri = typeof(UriFormat).GetTypeInfo().Assembly.FullName!;
13+
private readonly static string SystemPrivateXml = typeof(XmlNodeType).GetTypeInfo().Assembly.FullName!;
14+
private readonly static string SystemPrivateXmlLinq = typeof(XObject).GetTypeInfo().Assembly.FullName!;
15+
16+
/// <summary>
17+
/// Enum types and well-known types.
18+
/// </summary>
19+
public static readonly ConcurrentDictionary<string, Type> PredefinedEnumerationTypes = new(StringComparer.OrdinalIgnoreCase);
20+
21+
static EnumerationsAndWellKnownTypesFromMscorlib()
22+
{
23+
var list = AddEnumsAndWellKnownTypesFromAssembly(SystemPrivateUri);
24+
list.AddRange(AddEnumsAndWellKnownTypesFromAssembly(SystemPrivateCoreLib));
25+
list.AddRange(AddEnumsAndWellKnownTypesFromAssembly(SystemPrivateXml));
26+
list.AddRange(AddEnumsAndWellKnownTypesFromAssembly(SystemPrivateXmlLinq));
27+
28+
#if !(NET35 || NETSTANDARD1_3)
29+
var systemPrivateDataContractSerialization = typeof(Runtime.Serialization.DataContractResolver).GetTypeInfo().Assembly.FullName!;
30+
list.AddRange(AddEnumsAndWellKnownTypesFromAssembly(systemPrivateDataContractSerialization));
31+
#endif
32+
foreach (var group in list.GroupBy(t => t.Name))
33+
{
34+
Add(group);
35+
}
36+
}
37+
38+
private static List<Type> AddEnumsAndWellKnownTypesFromAssembly(string assemblyName)
39+
{
40+
try
41+
{
42+
var assembly = Assembly.Load(new AssemblyName(assemblyName));
43+
var types = assembly.GetTypes().ToArray();
44+
45+
var enumTypes = types.Where(t => t.GetTypeInfo().IsEnum && t.GetTypeInfo().IsPublic);
46+
var enumLikeTypes = FindEnumLikeTypes(types.Where(x => x == typeof(StringComparer)).ToArray());
47+
48+
return enumTypes.Union(enumLikeTypes).ToList();
49+
}
50+
catch
51+
{
52+
return [];
53+
}
54+
}
55+
56+
private static Type[] FindEnumLikeTypes(Type[] types)
57+
{
58+
try
59+
{
60+
return types
61+
.Where(t => t.GetTypeInfo().IsPublic && !t.GetTypeInfo().IsEnum && HasStaticPropertiesOrFieldsOfOwnType(t))
62+
.ToArray();
63+
}
64+
catch
65+
{
66+
return [];
67+
}
68+
}
69+
70+
private static bool HasStaticPropertiesOrFieldsOfOwnType(Type type)
71+
{
72+
var baseType = type.GetTypeInfo().BaseType;
73+
74+
var anyStaticProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Static)
75+
.Any(p => p.PropertyType == type || p.PropertyType == baseType);
76+
77+
if (anyStaticProperties)
78+
{
79+
return true;
80+
}
81+
82+
var anyStaticFields = type.GetFields(BindingFlags.Public | BindingFlags.Static)
83+
.Any(f => f.FieldType == type || f.FieldType == baseType);
84+
85+
return anyStaticFields;
86+
}
87+
88+
private static void Add(IGrouping<string, Type> group)
89+
{
90+
if (group.Count() == 1)
91+
{
92+
var singleType = group.Single();
93+
PredefinedEnumerationTypes.TryAdd(group.Key, singleType);
94+
PredefinedEnumerationTypes.TryAdd(singleType.FullName!, singleType);
95+
}
96+
else
97+
{
98+
foreach (var fullType in group)
99+
{
100+
PredefinedEnumerationTypes.TryAdd(fullType.FullName!, fullType);
101+
}
102+
}
103+
}
104+
}

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

Lines changed: 0 additions & 59 deletions
This file was deleted.

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2317,7 +2317,16 @@ private bool TryParseEnumerable(Expression instance, Type enumerableType, string
23172317
{
23182318
if (new[] { "Concat", "Contains", "ContainsKey", "DefaultIfEmpty", "Except", "Intersect", "Skip", "Take", "Union", "SequenceEqual" }.Contains(methodName))
23192319
{
2320-
args = [instance, args[0]];
2320+
if (args.Length == 1)
2321+
{
2322+
args = [instance, args[0]];
2323+
}
2324+
else
2325+
{
2326+
var argsAsList = new List<Expression> { instance };
2327+
argsAsList.AddRange(args);
2328+
args = argsAsList.ToArray();
2329+
}
23212330
}
23222331
else
23232332
{

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public bool TryGetValue(string text, out AnyOf<string, Expression, Type> value)
118118
}
119119

120120
// 4. Try to get as an enum from the system namespace
121-
if (_config.SupportEnumerationsFromSystemNamespace && EnumerationsFromMscorlib.PredefinedEnumerationTypes.TryGetValue(text, out var predefinedEnumType))
121+
if (_config.SupportEnumerationsFromSystemNamespace && EnumerationsAndWellKnownTypesFromMscorlib.PredefinedEnumerationTypes.TryGetValue(text, out var predefinedEnumType))
122122
{
123123
value = predefinedEnumType;
124124
return true;

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
using System.Linq.Dynamic.Core.Exceptions;
66
using System.Linq.Dynamic.Core.Tests.Helpers;
77
using System.Linq.Dynamic.Core.Tests.Helpers.Models;
8+
using System.Net.WebSockets;
9+
using System.Xml;
810
using FluentAssertions;
911
using Moq;
1012
using Newtonsoft.Json.Linq;
@@ -853,6 +855,36 @@ public void ExpressionTests_Enum()
853855
Check.That(resultEqualStringMixedCaseParamRight.Single()).Equals(TestEnum.Var5);
854856
}
855857

858+
[Fact]
859+
public void ExpressionTests_Enum_XmlNodeType()
860+
{
861+
// Arrange
862+
var lst = new[] { XmlNodeType.Text, XmlNodeType.Element };
863+
var qry = lst.AsQueryable();
864+
865+
// Act
866+
var result = lst.Count(it => new[] { XmlNodeType.Text }.Contains(it));
867+
var dynamicResult = qry.Count("new XmlNodeType[] { XmlNodeType.Text }.Contains(it)");
868+
869+
// Assert
870+
dynamicResult.Should().Be(result);
871+
}
872+
873+
[Fact]
874+
public void ExpressionTests_WellKnownTypes_StringComparer()
875+
{
876+
// Arrange
877+
var lst = new[] { "test" };
878+
var qry = lst.AsQueryable();
879+
880+
// Act
881+
var result = lst.Count(it => new string[] { "Test" }.Contains(it, StringComparer.OrdinalIgnoreCase));
882+
var dynamicResult = qry.Count("new string[] { \"Test\" }.Contains(it, StringComparer.OrdinalIgnoreCase)");
883+
884+
// Assert
885+
dynamicResult.Should().Be(result);
886+
}
887+
856888
[Fact]
857889
public void ExpressionTests_Enum_Property_Equality_Using_Argument()
858890
{

0 commit comments

Comments
 (0)