Skip to content

Commit 8bc7080

Browse files
committed
Refactor KeywordsHelper, TypeFinder and update comments on ParsingConfig
1 parent cac9576 commit 8bc7080

File tree

14 files changed

+264
-239
lines changed

14 files changed

+264
-239
lines changed

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using JetBrains.Annotations;
1010
using System.Linq.Dynamic.Core.Parser;
1111
using System.Linq.Dynamic.Core.Util;
12+
1213
#if !(SILVERLIGHT)
1314
using System.Diagnostics;
1415
#endif
@@ -314,7 +315,7 @@ public static IQueryable Cast(this IQueryable source, Type type)
314315
Check.NotNull(source);
315316
Check.NotNull(type);
316317

317-
var optimized = OptimizeExpression(Expression.Call(null, _cast.MakeGenericMethod(new[] { type }), new[] { source.Expression }));
318+
var optimized = OptimizeExpression(Expression.Call(null, _cast.MakeGenericMethod(type), source.Expression));
318319

319320
return source.Provider.CreateQuery(optimized);
320321
}
@@ -330,10 +331,13 @@ public static IQueryable Cast(this IQueryable source, ParsingConfig config, stri
330331
{
331332
Check.NotNull(source);
332333
Check.NotNull(config);
333-
Check.NotEmpty(typeName, nameof(typeName));
334+
Check.NotEmpty(typeName);
334335

335336
var finder = new TypeFinder(config, new KeywordsHelper(config));
336-
Type type = finder.FindTypeByName(typeName, null, true)!;
337+
if (!finder.TryFindTypeByName(typeName, null, true, out var type))
338+
{
339+
throw new ParseException(string.Format(CultureInfo.CurrentCulture, Res.TypeNotFound, typeName));
340+
}
337341

338342
return Cast(source, type);
339343
}
@@ -1445,7 +1449,10 @@ public static IQueryable OfType(this IQueryable source, ParsingConfig config, st
14451449
Check.NotEmpty(typeName);
14461450

14471451
var finder = new TypeFinder(config, new KeywordsHelper(config));
1448-
Type type = finder.FindTypeByName(typeName, null, true)!;
1452+
if (!finder.TryFindTypeByName(typeName, null, true, out var type))
1453+
{
1454+
throw new ParseException(string.Format(CultureInfo.CurrentCulture, Res.TypeNotFound, typeName));
1455+
}
14491456

14501457
return OfType(source, type);
14511458
}

src/System.Linq.Dynamic.Core/Exceptions/ParseException.cs

Lines changed: 67 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -4,74 +4,85 @@
44
using System.Runtime.Serialization;
55
#endif
66

7-
namespace System.Linq.Dynamic.Core.Exceptions
7+
namespace System.Linq.Dynamic.Core.Exceptions;
8+
9+
/// <summary>
10+
/// Represents errors that occur while parsing dynamic linq string expressions.
11+
/// </summary>
12+
#if !(SILVERLIGHT || UAP10_0 || NETSTANDARD || PORTABLE || WPSL || NETSTANDARD2_0)
13+
[Serializable]
14+
#endif
15+
public sealed class ParseException : Exception
816
{
17+
private const int UnknownPosition = -1;
18+
919
/// <summary>
10-
/// Represents errors that occur while parsing dynamic linq string expressions.
20+
/// The location in the parsed string that produced the <see cref="ParseException"/>.
21+
/// If the value is <c>-1</c>, the position is unknown.
1122
/// </summary>
12-
#if !(SILVERLIGHT || UAP10_0 || NETSTANDARD || PORTABLE || WPSL || NETSTANDARD2_0)
13-
[Serializable]
14-
#endif
15-
public sealed class ParseException : Exception
23+
public int Position { get; }
24+
25+
/// <summary>
26+
/// Initializes a new instance of the <see cref="ParseException"/> class with a specified error message and position.
27+
/// </summary>
28+
/// <param name="message">The message that describes the error.</param>
29+
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
30+
public ParseException(string message, Exception? innerException = null) : this(message, UnknownPosition, innerException)
1631
{
17-
/// <summary>
18-
/// The location in the parsed string that produced the <see cref="ParseException"/>.
19-
/// </summary>
20-
public int Position { get; }
32+
}
2133

22-
/// <summary>
23-
/// Initializes a new instance of the <see cref="ParseException"/> class with a specified error message and position.
24-
/// </summary>
25-
/// <param name="message">The message that describes the error.</param>
26-
/// <param name="position">The location in the parsed string that produced the <see cref="ParseException"/></param>
27-
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
28-
public ParseException(string message, int position, Exception? innerException = null) : base(message, innerException)
34+
/// <summary>
35+
/// Initializes a new instance of the <see cref="ParseException"/> class with a specified error message and position.
36+
/// </summary>
37+
/// <param name="message">The message that describes the error.</param>
38+
/// <param name="position">The location in the parsed string that produced the <see cref="ParseException"/></param>
39+
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference if no inner exception is specified.</param>
40+
public ParseException(string message, int position, Exception? innerException = null) : base(message, innerException)
41+
{
42+
Position = position;
43+
}
44+
45+
/// <summary>
46+
/// Creates and returns a string representation of the current exception.
47+
/// </summary>
48+
/// <returns>A string representation of the current exception.</returns>
49+
public override string ToString()
50+
{
51+
var text = string.Format(CultureInfo.CurrentCulture, Res.ParseExceptionFormat, Message, Position);
52+
53+
if (InnerException != null)
2954
{
30-
Position = position;
55+
text = $"{text} ---> {InnerException}{Environment.NewLine} --- End of inner exception stack trace ---";
3156
}
3257

33-
/// <summary>
34-
/// Creates and returns a string representation of the current exception.
35-
/// </summary>
36-
/// <returns>A string representation of the current exception.</returns>
37-
public override string ToString()
58+
if (StackTrace != null)
3859
{
39-
var text = string.Format(CultureInfo.CurrentCulture, Res.ParseExceptionFormat, Message, Position);
40-
41-
if (InnerException != null)
42-
{
43-
text = $"{text} ---> {InnerException}{Environment.NewLine} --- End of inner exception stack trace ---";
44-
}
45-
46-
if (StackTrace != null)
47-
{
48-
text = $"{text}{Environment.NewLine}{StackTrace}";
49-
}
50-
51-
return text;
60+
text = $"{text}{Environment.NewLine}{StackTrace}";
5261
}
5362

63+
return text;
64+
}
65+
5466
#if !(SILVERLIGHT || UAP10_0 || NETSTANDARD || PORTABLE || WPSL || NETSTANDARD2_0)
55-
private ParseException(SerializationInfo info, StreamingContext context) : base(info, context)
56-
{
57-
Position = (int)info.GetValue("position", typeof(int));
58-
}
67+
private ParseException(SerializationInfo info, StreamingContext context) : base(info, context)
68+
{
69+
Position = (int)info.GetValue("position", typeof(int))!;
70+
}
5971

60-
/// <summary>
61-
/// When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo" /> with information about the exception.
62-
/// </summary>
63-
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
64-
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
65-
/// <PermissionSet>
66-
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*" />
67-
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="SerializationFormatter" />
68-
/// </PermissionSet>
69-
public override void GetObjectData(SerializationInfo info, StreamingContext context)
70-
{
71-
base.GetObjectData(info, context);
72+
/// <summary>
73+
/// When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo" /> with information about the exception.
74+
/// </summary>
75+
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
76+
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
77+
/// <PermissionSet>
78+
/// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*" />
79+
/// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="SerializationFormatter" />
80+
/// </PermissionSet>
81+
public override void GetObjectData(SerializationInfo info, StreamingContext context)
82+
{
83+
base.GetObjectData(info, context);
7284

73-
info.AddValue("position", Position);
74-
}
75-
#endif
85+
info.AddValue("position", Position);
7686
}
77-
}
87+
#endif
88+
}

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

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -906,8 +906,7 @@ private AnyOf<Expression, Type> ParseStringLiteral(bool forceParseAsString)
906906
if (_parsingConfig.SupportCastingToFullyQualifiedTypeAsString && !forceParseAsString && parsedStringValue.Length > 2 && parsedStringValue.Contains('.'))
907907
{
908908
// Try to resolve this string as a type
909-
var type = _typeFinder.FindTypeByName(parsedStringValue, null, false);
910-
if (type is { })
909+
if (_typeFinder.TryFindTypeByName(parsedStringValue, null, false, out var type))
911910
{
912911
return type;
913912
}
@@ -970,7 +969,7 @@ private Expression ParseIdentifier()
970969
{
971970
_textParser.ValidateToken(TokenId.Identifier);
972971

973-
var isValidKeyWord = _keywordsHelper.TryGetValue(_textParser.CurrentToken.Text, out var keywordOrType);
972+
var isValid = _keywordsHelper.TryGetValue(_textParser.CurrentToken.Text, out var keywordOrType);
974973
var shouldPrioritizeType = true;
975974

976975
if (_parsingConfig.PrioritizePropertyOrFieldOverTheType && keywordOrType.IsThird)
@@ -983,7 +982,7 @@ private Expression ParseIdentifier()
983982
}
984983
}
985984

986-
if (isValidKeyWord && shouldPrioritizeType)
985+
if (isValid && shouldPrioritizeType)
987986
{
988987
var keywordOrFunctionAllowed = !_usedForOrderBy || _usedForOrderBy && !_parsingConfig.RestrictOrderByToPropertyOrField;
989988
if (!keywordOrFunctionAllowed)
@@ -1397,8 +1396,7 @@ private Expression ParseNew()
13971396
_textParser.NextToken();
13981397
}
13991398

1400-
newType = _typeFinder.FindTypeByName(newTypeName, new[] { _it, _parent, _root }, false);
1401-
if (newType == null)
1399+
if (!_typeFinder.TryFindTypeByName(newTypeName, [_it, _parent, _root], false, out newType))
14021400
{
14031401
throw ParseError(_textParser.CurrentToken.Pos, Res.TypeNotFound, newTypeName);
14041402
}
@@ -1543,14 +1541,15 @@ private Expression CreateNewExpression(List<DynamicProperty> properties, List<Ex
15431541
{
15441542
propertyInfos = propertyInfos.Where(x => x.Name != "Item").ToArray();
15451543
}
1544+
15461545
var propertyTypes = propertyInfos.Select(p => p.PropertyType).ToArray();
15471546
var ctor = type.GetConstructor(propertyTypes);
15481547
if (ctor != null)
15491548
{
15501549
var constructorParameters = ctor.GetParameters();
15511550
if (constructorParameters.Length == expressions.Count)
15521551
{
1553-
bool bindParametersSequentially = !properties.All(p => constructorParameters
1552+
var bindParametersSequentially = !properties.All(p => constructorParameters
15541553
.Any(cp => cp.Name == p.Name && (cp.ParameterType == p.Type || p.Type == Nullable.GetUnderlyingType(cp.ParameterType))));
15551554
var expressionsPromoted = new List<Expression?>();
15561555

@@ -1564,9 +1563,10 @@ private Expression CreateNewExpression(List<DynamicProperty> properties, List<Ex
15641563
else
15651564
{
15661565
Type propertyType = constructorParameters[i].ParameterType;
1567-
string cParameterName = constructorParameters[i].Name;
1566+
var cParameterName = constructorParameters[i].Name;
15681567
var propertyAndIndex = properties.Select((p, index) => new { p, index })
15691568
.First(p => p.p.Name == cParameterName && (p.p.Type == propertyType || p.p.Type == Nullable.GetUnderlyingType(propertyType)));
1569+
15701570
// Promote from Type to Nullable Type if needed
15711571
expressionsPromoted.Add(_parsingConfig.ExpressionPromoter.Promote(expressions[propertyAndIndex.index], propertyType, true, true));
15721572
}
@@ -2027,8 +2027,7 @@ private Expression ParseAsEnumOrNestedClass(string id)
20272027
}
20282028

20292029
var typeAsString = string.Concat(parts.Take(parts.Count - 2).ToArray());
2030-
var type = _typeFinder.FindTypeByName(typeAsString, null, true);
2031-
if (type == null)
2030+
if (!_typeFinder.TryFindTypeByName(typeAsString, null, true, out var type))
20322031
{
20332032
throw ParseError(_textParser.CurrentToken.Pos, Res.TypeNotFound, typeAsString);
20342033
}
@@ -2233,20 +2232,20 @@ private Type ResolveTypeFromExpressionValue(string functionName, ConstantExpress
22332232

22342233
private Type ResolveTypeStringFromArgument(string typeName)
22352234
{
2236-
bool typeIsNullable = false;
2235+
var typeIsNullable = false;
2236+
22372237
if (typeName.EndsWith("?"))
22382238
{
22392239
typeName = typeName.TrimEnd('?');
22402240
typeIsNullable = true;
22412241
}
22422242

2243-
var resultType = _typeFinder.FindTypeByName(typeName, new[] { _it, _parent, _root }, true);
2244-
if (resultType == null)
2243+
if (!_typeFinder.TryFindTypeByName(typeName, [_it, _parent, _root], true, out var type))
22452244
{
22462245
throw ParseError(_textParser.CurrentToken.Pos, Res.TypeNotFound, typeName);
22472246
}
22482247

2249-
return typeIsNullable ? TypeHelper.ToNullableType(resultType) : resultType;
2248+
return typeIsNullable ? TypeHelper.ToNullableType(type) : type;
22502249
}
22512250

22522251
private Expression[] ParseArgumentList()

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace System.Linq.Dynamic.Core.Parser;
55

6-
interface IKeywordsHelper
6+
internal interface IKeywordsHelper
77
{
8-
bool TryGetValue(string name, out AnyOf<string, Expression, Type> keywordOrType);
8+
bool TryGetValue(string text, out AnyOf<string, Expression, Type> value);
99
}
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
using System.Linq.Expressions;
1+
using System.Diagnostics.CodeAnalysis;
2+
using System.Linq.Expressions;
23

3-
namespace System.Linq.Dynamic.Core.Parser
4+
namespace System.Linq.Dynamic.Core.Parser;
5+
6+
internal interface ITypeFinder
47
{
5-
interface ITypeFinder
6-
{
7-
Type? FindTypeByName(string name, ParameterExpression?[]? expressions, bool forceUseCustomTypeProvider);
8-
}
8+
bool TryFindTypeByName(string name, ParameterExpression?[]? expressions, bool forceUseCustomTypeProvider, [NotNullWhen(true)] out Type? type);
99
}

0 commit comments

Comments
 (0)