diff --git a/Changelog.md b/Changelog.md
index ea1af6a14..82ae85912 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,4 +1,9 @@
-# Unreleased
+# Unreleased (5.5.0)
+
+### Server
+* Reduced allocatgions when parsing queries on server (both WCF and AspNetCore hosting) in https://github.com/OpenRIAServices/OpenRiaServices/pull/485
+* Update nuget dependencies and add new dependencies to OpenRiaServices.Hosting.Wcf
+ * `System.Collections.Immutable` and `System.Memory` are new dependencies of Wcf hosting
* .NET Framework builds now includes the smaller portable pdb's instead of of the old "full" windows style pdb's
* NOTE: For .NET Framework apps ensure that *supportedRuntime* in *app.config* and corresponding setting in *web.config* does not specify an older runtime if you wan't line numbers in stack traces.
diff --git a/NuGet/OpenRiaServices.Hosting.Wcf/OpenRiaServices.Hosting.Wcf.nuspec b/NuGet/OpenRiaServices.Hosting.Wcf/OpenRiaServices.Hosting.Wcf.nuspec
index 4df76c2cb..4d77a4b39 100644
--- a/NuGet/OpenRiaServices.Hosting.Wcf/OpenRiaServices.Hosting.Wcf.nuspec
+++ b/NuGet/OpenRiaServices.Hosting.Wcf/OpenRiaServices.Hosting.Wcf.nuspec
@@ -16,13 +16,15 @@
Open RIA Services - Server-side assemblies and configuration
For release notes see https://github.com/OpenRIAServices/OpenRiaServices/releases
- 2019 Outercurve Foundation
+ 2024 .NET Foundation
en-US
WCF RIA Services RIAServices Server aspnet OpenRiaServices
+
-
+
+
@@ -33,4 +35,4 @@
-
\ No newline at end of file
+
diff --git a/src/OpenRiaServices.Hosting.AspNetCore/Test/OpenRiaServices.Hosting.AspNetCore.Test/OpenRiaServices.Hosting.AspNetCore.Test.csproj b/src/OpenRiaServices.Hosting.AspNetCore/Test/OpenRiaServices.Hosting.AspNetCore.Test/OpenRiaServices.Hosting.AspNetCore.Test.csproj
index 79cfe7206..fba6004aa 100644
--- a/src/OpenRiaServices.Hosting.AspNetCore/Test/OpenRiaServices.Hosting.AspNetCore.Test/OpenRiaServices.Hosting.AspNetCore.Test.csproj
+++ b/src/OpenRiaServices.Hosting.AspNetCore/Test/OpenRiaServices.Hosting.AspNetCore.Test/OpenRiaServices.Hosting.AspNetCore.Test.csproj
@@ -15,4 +15,7 @@
+
+
+
\ No newline at end of file
diff --git a/src/OpenRiaServices.Hosting.Wcf/Framework/Linq/DynamicQueryable.cs b/src/OpenRiaServices.Hosting.Wcf/Framework/Linq/DynamicQueryable.cs
index e8ac262f1..ab436cc60 100644
--- a/src/OpenRiaServices.Hosting.Wcf/Framework/Linq/DynamicQueryable.cs
+++ b/src/OpenRiaServices.Hosting.Wcf/Framework/Linq/DynamicQueryable.cs
@@ -1,11 +1,21 @@
-using System.Collections.Generic;
+using System.Collections.Frozen;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq.Expressions;
using System.Reflection;
+using System;
namespace System.Linq.Dynamic
{
+#if NETFRAMEWORK
+ static class StringExtensions
+ {
+ static public string Slice(this string str, int startIndex, int length)
+ => str.Substring(startIndex, length);
+ }
+#endif
+
internal static class DynamicQueryable
{
public static IQueryable Where(this IQueryable source, string predicate, QueryResolver queryResolver)
@@ -129,11 +139,11 @@ public ParseException(string message, int position)
internal partial class ExpressionParser
{
- struct Token
+ struct Token(TokenId id, int pos, int length)
{
- public TokenId id;
- public string text;
- public int pos;
+ public readonly TokenId id = id;
+ public readonly int pos = pos;
+ public readonly int length = length;
}
enum TokenId
@@ -287,7 +297,7 @@ interface IEnumerableSignatures
void Average(decimal? selector);
}
- static readonly Type[] predefinedTypes = {
+ static readonly Type[] s_predefinedTypes = {
typeof(Object),
typeof(Boolean),
typeof(Char),
@@ -313,37 +323,43 @@ interface IEnumerableSignatures
typeof(Uri)
};
- static readonly Expression trueLiteral = Expression.Constant(true);
- static readonly Expression falseLiteral = Expression.Constant(false);
- static readonly Expression nullLiteral = Expression.Constant(null);
+ static readonly ConstantExpression TrueLiteral = Expression.Constant(true);
+ static readonly ConstantExpression FalseLiteral = Expression.Constant(false);
+ static readonly ConstantExpression NullLiteral = Expression.Constant(null);
+
+ const string KeywordIt = "it";
+ const string KeywordIif = "iif";
+
+ static readonly FrozenDictionary s_keywords = CreateKeywords();
+
+ readonly Dictionary _symbols = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ readonly Dictionary> _literals = new();
+ ParameterExpression _it;
+ readonly string _text;
+ int _textPos;
+ char _ch;
+ TokenId _tokenId;
+ int _tokenStart;
+ readonly QueryResolver _queryResolver;
+
+ Token CurrentToken => new Token(_tokenId, _tokenStart, _textPos - _tokenStart);
- const string keywordIt = "it";
- const string keywordIif = "iif";
+ ReadOnlySpan GetText(Token token) =>
+ _text.AsSpan(token.pos, token.length);
- static readonly Dictionary keywords = CreateKeywords();
+ string GetString(Token token)
+ => _text.Substring(token.pos, token.length);
- Dictionary symbols;
- Dictionary literals;
- ParameterExpression it;
- string text;
- int textPos;
- int textLen;
- char ch;
- Token token;
- QueryResolver queryResolver;
public ExpressionParser(ParameterExpression[] parameters, string expression, QueryResolver queryResolver)
{
if (expression == null)
throw new ArgumentNullException(nameof(expression));
- this.queryResolver = queryResolver;
- symbols = new Dictionary(StringComparer.OrdinalIgnoreCase);
- literals = new Dictionary();
+ this._queryResolver = queryResolver;
if (parameters != null)
ProcessParameters(parameters);
- text = expression;
- textLen = text.Length;
+ _text = expression;
SetTextPos(0);
NextToken();
}
@@ -351,21 +367,21 @@ public ExpressionParser(ParameterExpression[] parameters, string expression, Que
void ProcessParameters(ParameterExpression[] parameters)
{
foreach (ParameterExpression pe in parameters)
- if (!String.IsNullOrEmpty(pe.Name))
+ if (!string.IsNullOrEmpty(pe.Name))
AddSymbol(pe.Name, pe);
- if (parameters.Length == 1 && String.IsNullOrEmpty(parameters[0].Name))
- it = parameters[0];
+ if (parameters.Length == 1 && string.IsNullOrEmpty(parameters[0].Name))
+ _it = parameters[0];
}
void AddSymbol(string name, object value)
{
- if (!symbols.TryAdd(name, value))
+ if (!_symbols.TryAdd(name, value))
throw ParseError(Resource.DuplicateIdentifier, name);
}
public Expression Parse(Type resultType)
{
- int exprPos = token.pos;
+ int exprPos = CurrentToken.pos;
Expression expr = ParseExpression();
if (resultType != null)
if ((expr = PromoteExpression(expr, resultType, true)) == null)
@@ -374,7 +390,6 @@ public Expression Parse(Type resultType)
return expr;
}
-#pragma warning disable 0219
public IEnumerable ParseOrdering()
{
List orderings = new List();
@@ -382,35 +397,38 @@ public IEnumerable ParseOrdering()
{
Expression expr = ParseExpression();
bool ascending = true;
- if (TokenIdentifierIs("asc") || TokenIdentifierIs("ascending"))
+ if (_tokenId == TokenId.Identifier)
{
- NextToken();
- }
- else if (TokenIdentifierIs("desc") || TokenIdentifierIs("descending"))
- {
- NextToken();
- ascending = false;
+ if (TokenIdentifierIs("asc") || TokenIdentifierIs("ascending"))
+ {
+ NextToken();
+ }
+ else if (TokenIdentifierIs("desc") || TokenIdentifierIs("descending"))
+ {
+ NextToken();
+ ascending = false;
+ }
}
+
orderings.Add(new DynamicOrdering
{
Selector = expr,
Ascending = ascending
});
- if (token.id != TokenId.Comma)
+ if (CurrentToken.id != TokenId.Comma)
break;
NextToken();
}
ValidateToken(TokenId.End, Resource.SyntaxError);
return orderings;
}
-#pragma warning restore 0219
// ?: operator
Expression ParseExpression()
{
- int errorPos = token.pos;
+ int errorPos = CurrentToken.pos;
Expression expr = ParseLogicalOr();
- if (token.id == TokenId.Question)
+ if (CurrentToken.id == TokenId.Question)
{
NextToken();
Expression expr1 = ParseExpression();
@@ -426,12 +444,12 @@ Expression ParseExpression()
Expression ParseLogicalOr()
{
Expression left = ParseLogicalAnd();
- while (token.id == TokenId.DoubleBar || TokenIdentifierIs("or"))
+ while (CurrentToken.id == TokenId.DoubleBar || TokenIdentifierIs("or"))
{
- Token op = token;
+ Token op = CurrentToken;
NextToken();
Expression right = ParseLogicalAnd();
- CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos);
+ CheckAndPromoteOperands(typeof(ILogicalSignatures), op, ref left, ref right);
left = Expression.OrElse(left, right);
}
return left;
@@ -441,12 +459,12 @@ Expression ParseLogicalOr()
Expression ParseLogicalAnd()
{
Expression left = ParseHasOperator();
- while (token.id == TokenId.DoubleAmphersand || TokenIdentifierIs("and"))
+ while (CurrentToken.id == TokenId.DoubleAmphersand || TokenIdentifierIs("and"))
{
- Token op = token;
+ Token op = CurrentToken;
NextToken();
Expression right = ParseHasOperator();
- CheckAndPromoteOperands(typeof(ILogicalSignatures), op.text, ref left, ref right, op.pos);
+ CheckAndPromoteOperands(typeof(ILogicalSignatures), op, ref left, ref right);
left = Expression.AndAlso(left, right);
}
return left;
@@ -462,7 +480,7 @@ Expression ParseHasOperator()
Expression left = ParseComparison();
while (TokenIdentifierIs("has"))
{
- Token op = token;
+ Token op = CurrentToken;
NextToken();
Type enumType = left.Type;
@@ -476,26 +494,26 @@ Expression ParseHasOperator()
NextToken();
//Verify next is dot and then remove it
- if (token.id != TokenId.Dot)
- throw new ParseException("Expected a dot", token.pos);
+ if (CurrentToken.id != TokenId.Dot)
+ throw new ParseException("Expected a dot", CurrentToken.pos);
NextToken();
// Read the enum field name, parse it and remove it
- if (token.id != TokenId.Identifier)
- throw new ParseException("Expected an Identifier", token.pos);
- right = Expression.Constant(ParseEnum(token.text, enumType), enumType);
+ if (CurrentToken.id != TokenId.Identifier)
+ throw new ParseException("Expected an Identifier", CurrentToken.pos);
+ right = Expression.Constant(ParseEnum(GetText(CurrentToken), enumType), enumType);
NextToken();
}
else // Either numeric value or member access
- {
+ {
right = ParseComparison();
}
left = ConvertEnumExpression(left, right);
right = ConvertEnumExpression(right, left);
- CheckAndPromoteOperands(typeof(IArithmeticSignatures), op.text, ref left, ref right, op.pos);
-
+ CheckAndPromoteOperands(typeof(IArithmeticSignatures), op, ref left, ref right);
+
// Treat as (left & right) == right which is the same behaviour as calling Enum.HasFlag
// but it will work with entity framework and probably most other query providers
left = Expression.Equal(Expression.And(left, right), right);
@@ -507,16 +525,17 @@ Expression ParseHasOperator()
Expression ParseComparison()
{
Expression left = ParseAdditive();
- while (token.id == TokenId.Equal || token.id == TokenId.DoubleEqual ||
- token.id == TokenId.ExclamationEqual || token.id == TokenId.LessGreater ||
- token.id == TokenId.GreaterThan || token.id == TokenId.GreaterThanEqual ||
- token.id == TokenId.LessThan || token.id == TokenId.LessThanEqual)
+
+ while (CurrentToken.id is TokenId.Equal or TokenId.DoubleEqual or
+ TokenId.ExclamationEqual or TokenId.LessGreater or
+ TokenId.GreaterThan or TokenId.GreaterThanEqual or
+ TokenId.LessThan or TokenId.LessThanEqual)
{
- Token op = token;
+ Token op = CurrentToken;
NextToken();
Expression right = ParseAdditive();
- bool isEquality = op.id == TokenId.Equal || op.id == TokenId.DoubleEqual ||
- op.id == TokenId.ExclamationEqual || op.id == TokenId.LessGreater;
+ bool isEquality = op.id is TokenId.Equal or TokenId.DoubleEqual or
+ TokenId.ExclamationEqual or TokenId.LessGreater;
if (isEquality && !left.Type.IsValueType && !right.Type.IsValueType)
{
if (left.Type != right.Type)
@@ -531,7 +550,7 @@ Expression ParseComparison()
}
else
{
- throw IncompatibleOperandsError(op.text, left, right, op.pos);
+ throw IncompatibleOperandsError(GetString(op), left, right, op.pos);
}
}
}
@@ -542,12 +561,12 @@ Expression ParseComparison()
right = ConvertEnumExpression(right, left);
CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures),
- op.text, ref left, ref right, op.pos);
+ op, ref left, ref right);
}
else
{
CheckAndPromoteOperands(isEquality ? typeof(IEqualitySignatures) : typeof(IRelationalSignatures),
- op.text, ref left, ref right, op.pos);
+ op, ref left, ref right);
}
switch (op.id)
{
@@ -607,10 +626,10 @@ static Expression ConvertEnumExpression(Expression expr, Expression otherExpr)
Expression ParseAdditive()
{
Expression left = ParseMultiplicative();
- while (token.id == TokenId.Plus || token.id == TokenId.Minus ||
- token.id == TokenId.Amphersand)
+ while (CurrentToken.id is TokenId.Plus or TokenId.Minus or
+ TokenId.Amphersand)
{
- Token op = token;
+ Token op = CurrentToken;
NextToken();
Expression right = ParseMultiplicative();
switch (op.id)
@@ -618,11 +637,11 @@ Expression ParseAdditive()
case TokenId.Plus:
if (left.Type == typeof(string) || right.Type == typeof(string))
goto case TokenId.Amphersand;
- CheckAndPromoteOperands(typeof(IAddSignatures), op.text, ref left, ref right, op.pos);
+ CheckAndPromoteOperands(typeof(IAddSignatures), op, ref left, ref right);
left = GenerateAdd(left, right);
break;
case TokenId.Minus:
- CheckAndPromoteOperands(typeof(ISubtractSignatures), op.text, ref left, ref right, op.pos);
+ CheckAndPromoteOperands(typeof(ISubtractSignatures), op, ref left, ref right);
left = GenerateSubtract(left, right);
break;
case TokenId.Amphersand:
@@ -637,13 +656,13 @@ Expression ParseAdditive()
Expression ParseMultiplicative()
{
Expression left = ParseUnary();
- while (token.id == TokenId.Asterisk || token.id == TokenId.Slash ||
- token.id == TokenId.Percent || TokenIdentifierIs("mod"))
+ while (CurrentToken.id == TokenId.Asterisk || CurrentToken.id == TokenId.Slash ||
+ CurrentToken.id == TokenId.Percent || TokenIdentifierIs("mod"))
{
- Token op = token;
+ Token op = CurrentToken;
NextToken();
Expression right = ParseUnary();
- CheckAndPromoteOperands(typeof(IArithmeticSignatures), op.text, ref left, ref right, op.pos);
+ CheckAndPromoteOperands(typeof(IArithmeticSignatures), op, ref left, ref right);
switch (op.id)
{
case TokenId.Asterisk:
@@ -664,27 +683,27 @@ Expression ParseMultiplicative()
// -, !, not unary operators
Expression ParseUnary()
{
- if (token.id == TokenId.Minus || token.id == TokenId.Exclamation ||
+ if (CurrentToken.id == TokenId.Minus || CurrentToken.id == TokenId.Exclamation ||
TokenIdentifierIs("not"))
{
- Token op = token;
+ Token op = CurrentToken;
NextToken();
- if (op.id == TokenId.Minus && (token.id == TokenId.IntegerLiteral ||
- token.id == TokenId.RealLiteral))
+ if (op.id == TokenId.Minus && (CurrentToken.id == TokenId.IntegerLiteral ||
+ CurrentToken.id == TokenId.RealLiteral))
{
- token.text = "-" + token.text;
- token.pos = op.pos;
+ // Reset token start to also include "-" sign
+ _tokenStart = op.pos;
return ParsePrimary();
}
Expression expr = ParseUnary();
if (op.id == TokenId.Minus)
{
- CheckAndPromoteOperand(typeof(INegationSignatures), op.text, ref expr, op.pos);
+ CheckAndPromoteOperand(typeof(INegationSignatures), op, ref expr);
expr = Expression.Negate(expr);
}
else
{
- CheckAndPromoteOperand(typeof(INotSignatures), op.text, ref expr, op.pos);
+ CheckAndPromoteOperand(typeof(INotSignatures), op, ref expr);
expr = Expression.Not(expr);
}
return expr;
@@ -697,12 +716,12 @@ Expression ParsePrimary()
Expression expr = ParsePrimaryStart();
while (true)
{
- if (token.id == TokenId.Dot)
+ if (CurrentToken.id == TokenId.Dot)
{
NextToken();
expr = ParseMemberAccess(null, expr);
}
- else if (token.id == TokenId.OpenBracket)
+ else if (CurrentToken.id == TokenId.OpenBracket)
{
expr = ParseElementAccess(expr);
}
@@ -716,7 +735,7 @@ Expression ParsePrimary()
Expression ParsePrimaryStart()
{
- switch (token.id)
+ switch (CurrentToken.id)
{
case TokenId.Identifier:
return ParseIdentifier();
@@ -736,9 +755,10 @@ Expression ParsePrimaryStart()
Expression ParseStringLiteral()
{
ValidateToken(TokenId.StringLiteral);
- char quote = token.text[0];
+ var text = GetText(CurrentToken);
+ char quote = text[0];
// Unwrap string (remove surrounding quotes) and unwrap backslashes.
- string s = token.text.Substring(1, token.text.Length - 2).Replace("\\\\", "\\");
+ string s = text.Slice(1, text.Length - 2).ToString().Replace("\\\\", "\\");
if (quote == '\'')
{
// Unwrap quotes.
@@ -760,74 +780,90 @@ Expression ParseStringLiteral()
Expression ParseIntegerLiteral()
{
+ Token token = CurrentToken;
ValidateToken(TokenId.IntegerLiteral);
- string text = token.text;
+#if NET
+ ReadOnlySpan text = GetText(token);
+#else
+ string text = GetString(token);
+#endif
if (text[0] != '-')
{
ulong value;
- if (!UInt64.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out value))
- throw ParseError(Resource.InvalidIntegerLiteral, text);
+ if (!ulong.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out value))
+ throw ParseError(Resource.InvalidIntegerLiteral, text.ToString());
NextToken();
- if (value <= (ulong)Int32.MaxValue)
- return CreateLiteral((int)value, text);
- if (value <= (ulong)UInt32.MaxValue)
- return CreateLiteral((uint)value, text);
- if (value <= (ulong)Int64.MaxValue)
- return CreateLiteral((long)value, text);
- return CreateLiteral(value, text);
+ if (value <= (ulong)int.MaxValue)
+ return CreateLiteral((int)value, token);
+ if (value <= (ulong)uint.MaxValue)
+ return CreateLiteral((uint)value, token);
+ if (value <= (ulong)long.MaxValue)
+ return CreateLiteral((long)value, token);
+ return CreateLiteral(value, token);
}
else
{
long value;
- if (!Int64.TryParse(text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out value))
- throw ParseError(Resource.InvalidIntegerLiteral, text);
+ if (!long.TryParse(text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out value))
+ throw ParseError(Resource.InvalidIntegerLiteral, text.ToString());
NextToken();
- if (value >= Int32.MinValue && value <= Int32.MaxValue)
- return CreateLiteral((int)value, text);
- return CreateLiteral(value, text);
+ if (value >= int.MinValue && value <= int.MaxValue)
+ return CreateLiteral((int)value, token);
+ return CreateLiteral(value, token);
}
}
Expression ParseRealLiteral()
{
ValidateToken(TokenId.RealLiteral);
- string text = token.text;
+ Token token = CurrentToken;
+#if NET
+ ReadOnlySpan text = GetText(token);
+#else
+ string text = GetString(token);
+#endif
object value = null;
char last = text[text.Length - 1];
- if (last == 'F' || last == 'f')
+ if (last is 'F' or 'f')
{
float f;
- if (Single.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out f))
+ if (float.TryParse(text.Slice(0, text.Length - 1), NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out f))
value = f;
}
- else if (last == 'M' || last == 'm')
+ else if (last is 'M' or 'm')
{
decimal m;
- if (Decimal.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out m))
+ if (decimal.TryParse(text.Slice(0, text.Length - 1), NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out m))
value = m;
}
- else if (last == 'D' || last == 'd')
+ else if (last is 'D' or 'd')
{
double d;
- if (Double.TryParse(text.Substring(0, text.Length - 1), NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out d))
+ if (double.TryParse(text.Slice(0, text.Length - 1), NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out d))
value = d;
}
else
{
double d;
- if (Double.TryParse(text, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out d))
+ if (double.TryParse(text, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out d))
value = d;
}
if (value == null)
- throw ParseError(Resource.InvalidRealLiteral, text);
+ throw ParseError(Resource.InvalidRealLiteral, text.ToString());
NextToken();
- return CreateLiteral(value, text);
+ return CreateLiteral(value, token);
}
Expression CreateLiteral(object value, string valueAsString)
{
ConstantExpression expr = Expression.Constant(value);
- literals.Add(expr, valueAsString);
+ _literals.Add(expr, valueAsString.AsMemory());
+ return expr;
+ }
+ Expression CreateLiteral(object value, Token token)
+ {
+ ConstantExpression expr = Expression.Constant(value);
+ _literals.Add(expr, _text.AsMemory(token.pos, token.length));
return expr;
}
@@ -845,18 +881,20 @@ Expression ParseIdentifier()
{
ValidateToken(TokenId.Identifier);
object value;
- if (keywords.TryGetValue(token.text, out value))
+ string text = GetString(CurrentToken);
+
+ if (s_keywords.TryGetValue(text, out value))
{
- if (value is Type)
- return ParseTypeAccess((Type)value);
- if (value == (object)keywordIt)
+ if (value is Type type)
+ return ParseTypeAccess(type);
+ if (object.ReferenceEquals(value, KeywordIt))
return ParseIt();
- if (value == (object)keywordIif)
+ if (object.ReferenceEquals(value, KeywordIif))
return ParseIif();
NextToken();
return (Expression)value;
}
- if (symbols.TryGetValue(token.text, out value))
+ if (_symbols.TryGetValue(text, out value))
{
Expression expr = value as Expression;
if (expr == null)
@@ -866,22 +904,22 @@ Expression ParseIdentifier()
NextToken();
return expr;
}
- if (it != null)
- return ParseMemberAccess(null, it);
- throw ParseError(Resource.UnknownIdentifier, token.text);
+ if (_it != null)
+ return ParseMemberAccess(null, _it);
+ throw ParseError(Resource.UnknownIdentifier, text);
}
Expression ParseIt()
{
- if (it == null)
+ if (_it == null)
throw ParseError(Resource.NoItInScope);
NextToken();
- return it;
+ return _it;
}
Expression ParseIif()
{
- int errorPos = token.pos;
+ int errorPos = CurrentToken.pos;
NextToken();
Expression[] args = ParseArgumentList();
if (args.Length != 3)
@@ -895,8 +933,8 @@ Expression GenerateConditional(Expression test, Expression expr1, Expression exp
throw ParseError(errorPos, Resource.FirstExprMustBeBool);
if (expr1.Type != expr2.Type)
{
- Expression expr1as2 = expr2 != nullLiteral ? PromoteExpression(expr1, expr2.Type, true) : null;
- Expression expr2as1 = expr1 != nullLiteral ? PromoteExpression(expr2, expr1.Type, true) : null;
+ Expression expr1as2 = expr2 != NullLiteral ? PromoteExpression(expr1, expr2.Type, true) : null;
+ Expression expr2as1 = expr1 != NullLiteral ? PromoteExpression(expr2, expr1.Type, true) : null;
if (expr1as2 != null && expr2as1 == null)
{
expr1 = expr1as2;
@@ -907,8 +945,8 @@ Expression GenerateConditional(Expression test, Expression expr1, Expression exp
}
else
{
- string type1 = expr1 != nullLiteral ? expr1.Type.Name : "null";
- string type2 = expr2 != nullLiteral ? expr2.Type.Name : "null";
+ string type1 = expr1 != NullLiteral ? expr1.Type.Name : "null";
+ string type2 = expr2 != NullLiteral ? expr2.Type.Name : "null";
if (expr1as2 != null && expr2as1 != null)
throw ParseError(errorPos, Resource.BothTypesConvertToOther, type1, type2);
throw ParseError(errorPos, Resource.NeitherTypeConvertsToOther, type1, type2);
@@ -919,16 +957,16 @@ Expression GenerateConditional(Expression test, Expression expr1, Expression exp
Expression ParseTypeAccess(Type type)
{
- int errorPos = token.pos;
+ int errorPos = CurrentToken.pos;
NextToken();
- if (token.id == TokenId.Question)
+ if (CurrentToken.id == TokenId.Question)
{
if (!type.IsValueType || IsNullableType(type))
throw ParseError(errorPos, Resource.TypeHasNoNullableForm, GetTypeName(type));
type = typeof(Nullable<>).MakeGenericType(type);
NextToken();
}
- if (token.id == TokenId.OpenParen)
+ if (CurrentToken.id == TokenId.OpenParen)
{
Expression[] args = ParseArgumentList();
MethodBase method;
@@ -977,10 +1015,10 @@ Expression ParseMemberAccess(Type type, Expression instance)
{
if (instance != null)
type = instance.Type;
- int errorPos = token.pos;
+ int errorPos = CurrentToken.pos;
string id = GetIdentifier();
NextToken();
- if (token.id == TokenId.OpenParen)
+ if (CurrentToken.id == TokenId.OpenParen)
{
if (instance != null && type != typeof(string))
{
@@ -1018,9 +1056,9 @@ Expression ParseMemberAccess(Type type, Expression instance)
MemberInfo member = FindPropertyOrField(type, id, instance == null);
if (member == null)
{
- if (this.queryResolver != null)
+ if (this._queryResolver != null)
{
- MemberExpression mex = queryResolver.ResolveMember(type, id, instance);
+ MemberExpression mex = _queryResolver.ResolveMember(type, id, instance);
if (mex != null)
{
return mex;
@@ -1057,16 +1095,16 @@ static Type FindGenericType(Type generic, Type type)
Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos)
{
- ParameterExpression outerIt = it;
+ ParameterExpression outerIt = _it;
ParameterExpression innerIt = Expression.Parameter(elementType, "");
- it = innerIt;
+ _it = innerIt;
Expression[] args = ParseArgumentList();
- it = outerIt;
+ _it = outerIt;
MethodBase signature;
if (FindMethod(typeof(IEnumerableSignatures), methodName, false, args, out signature) != 1)
throw ParseError(errorPos, Resource.NoApplicableAggregate, methodName);
Type[] typeArgs;
- if (signature.Name == "Min" || signature.Name == "Max")
+ if (signature.Name is "Min" or "Max")
{
typeArgs = new Type[] { elementType, args[0].Type };
}
@@ -1089,7 +1127,7 @@ Expression[] ParseArgumentList()
{
ValidateToken(TokenId.OpenParen, Resource.OpenParenExpected);
NextToken();
- Expression[] args = token.id != TokenId.CloseParen ? ParseArguments() : Array.Empty();
+ Expression[] args = CurrentToken.id != TokenId.CloseParen ? ParseArguments() : Array.Empty();
ValidateToken(TokenId.CloseParen, Resource.CloseParenOrCommaExpected);
NextToken();
return args;
@@ -1101,7 +1139,7 @@ Expression[] ParseArguments()
while (true)
{
argList.Add(ParseExpression());
- if (token.id != TokenId.Comma)
+ if (CurrentToken.id != TokenId.Comma)
break;
NextToken();
}
@@ -1110,7 +1148,7 @@ Expression[] ParseArguments()
Expression ParseElementAccess(Expression expr)
{
- int errorPos = token.pos;
+ int errorPos = CurrentToken.pos;
ValidateToken(TokenId.OpenBracket, Resource.OpenParenExpected);
NextToken();
Expression[] args = ParseArguments();
@@ -1145,7 +1183,7 @@ Expression ParseElementAccess(Expression expr)
static bool IsPredefinedType(Type type)
{
type = GetNonNullableType(type);
- foreach (Type t in predefinedTypes)
+ foreach (Type t in s_predefinedTypes)
if (t == type)
return true;
return false;
@@ -1217,22 +1255,22 @@ static bool IsEnumType(Type type)
return GetNonNullableType(type).IsEnum;
}
- void CheckAndPromoteOperand(Type signatures, string opName, ref Expression expr, int errorPos)
+ void CheckAndPromoteOperand(Type signatures, Token op, ref Expression expr)
{
Expression[] args = new Expression[] { expr };
MethodBase method;
if (FindMethod(signatures, "F", false, args, out method) != 1)
- throw ParseError(errorPos, Resource.IncompatibleOperand,
- opName, GetTypeName(args[0].Type));
+ throw ParseError(op.pos, Resource.IncompatibleOperand,
+ GetString(op), GetTypeName(args[0].Type));
expr = args[0];
}
- void CheckAndPromoteOperands(Type signatures, string opName, ref Expression left, ref Expression right, int errorPos)
+ void CheckAndPromoteOperands(Type signatures, Token op, ref Expression left, ref Expression right)
{
Expression[] args = new Expression[] { left, right };
MethodBase method;
if (FindMethod(signatures, "F", false, args, out method) != 1)
- throw IncompatibleOperandsError(opName, left, right, errorPos);
+ throw IncompatibleOperandsError(GetString(op), left, right, op.pos);
left = args[0];
right = args[1];
}
@@ -1386,18 +1424,17 @@ Expression PromoteExpression(Expression expr, Type type, bool exact)
if (expr is ConstantExpression)
{
ConstantExpression ce = (ConstantExpression)expr;
- if (ce == nullLiteral)
+ if (ce == NullLiteral)
{
if (!type.IsValueType || IsNullableType(type))
return Expression.Constant(null, type);
}
else
{
- string text;
- if (literals.TryGetValue(ce, out text))
+ if (_literals.TryGetValue(ce, out ReadOnlyMemory text))
{
Type target = GetNonNullableType(type);
- Object value = null;
+ object value = null;
switch (Type.GetTypeCode(ce.Type))
{
case TypeCode.Int32:
@@ -1407,7 +1444,7 @@ Expression PromoteExpression(Expression expr, Type type, bool exact)
if (target.IsEnum)
{
// promoting from a number to an enum
- value = Enum.Parse(target, text);
+ value = Enum.ToObject(target, ce.Value);
}
else if (target == typeof(char))
{
@@ -1424,15 +1461,17 @@ Expression PromoteExpression(Expression expr, Type type, bool exact)
}
else
{
- value = ParseNumber(text, target);
+ value = ParseNumber(text.Span, target);
}
break;
case TypeCode.Double:
+ // Wanted decimal but it got parsed as double, needs to reparse to avoid loosing precision
if (target == typeof(decimal))
- value = ParseNumber(text, target);
+ value = ParseNumber(text.Span, target);
break;
case TypeCode.String:
- value = ParseEnum(text, target);
+ // We parsed as text but wanted it as something else, probaly an enum
+ value = ParseEnum(text.Span, target);
break;
}
if (value != null)
@@ -1449,8 +1488,14 @@ Expression PromoteExpression(Expression expr, Type type, bool exact)
return null;
}
- static object ParseNumber(string text, Type type)
+#if NETFRAMEWORK
+ static object ParseNumber(ReadOnlySpan span, Type type)
+ {
+ string text = span.ToString();
+#else
+ static object ParseNumber(ReadOnlySpan text, Type type)
{
+#endif
switch (Type.GetTypeCode(GetNonNullableType(type)))
{
case TypeCode.SByte:
@@ -1512,15 +1557,20 @@ static object ParseNumber(string text, Type type)
return null;
}
- static object ParseEnum(string name, Type type)
+ static object ParseEnum(ReadOnlySpan name, Type type)
{
if (type.IsEnum)
{
+#if NET
+ if (Enum.TryParse(type, name, ignoreCase: true, out var result))
+ return result;
+#else
MemberInfo[] memberInfos = type.FindMembers(MemberTypes.Field,
BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static,
- Type.FilterNameIgnoreCase, name);
+ Type.FilterNameIgnoreCase, name.ToString());
if (memberInfos.Length != 0)
return ((FieldInfo)memberInfos[0]).GetValue(null);
+#endif
}
return null;
}
@@ -1789,28 +1839,28 @@ static Expression GenerateStaticMethodCall(string methodName, Expression left, E
void SetTextPos(int pos)
{
- textPos = pos;
- ch = textPos < textLen ? text[textPos] : '\0';
+ _textPos = pos;
+ _ch = _textPos < _text.Length ? _text[_textPos] : '\0';
}
void NextChar()
{
- if (textPos < textLen)
- textPos++;
- ch = textPos < textLen ? text[textPos] : '\0';
+ if (_textPos < _text.Length)
+ _textPos++;
+ _ch = _textPos < _text.Length ? _text[_textPos] : '\0';
}
void NextToken()
{
- while (Char.IsWhiteSpace(ch))
+ while (char.IsWhiteSpace(_ch))
NextChar();
TokenId t;
- int tokenPos = textPos;
- switch (ch)
+ _tokenStart = _textPos;
+ switch (_ch)
{
case '!':
NextChar();
- if (ch == '=')
+ if (_ch == '=')
{
NextChar();
t = TokenId.ExclamationEqual;
@@ -1826,7 +1876,7 @@ void NextToken()
break;
case '&':
NextChar();
- if (ch == '&')
+ if (_ch == '&')
{
NextChar();
t = TokenId.DoubleAmphersand;
@@ -1874,12 +1924,12 @@ void NextToken()
break;
case '<':
NextChar();
- if (ch == '=')
+ if (_ch == '=')
{
NextChar();
t = TokenId.LessThanEqual;
}
- else if (ch == '>')
+ else if (_ch == '>')
{
NextChar();
t = TokenId.LessGreater;
@@ -1891,7 +1941,7 @@ void NextToken()
break;
case '=':
NextChar();
- if (ch == '=')
+ if (_ch == '=')
{
NextChar();
t = TokenId.DoubleEqual;
@@ -1903,7 +1953,7 @@ void NextToken()
break;
case '>':
NextChar();
- if (ch == '=')
+ if (_ch == '=')
{
NextChar();
t = TokenId.GreaterThanEqual;
@@ -1927,7 +1977,7 @@ void NextToken()
break;
case '|':
NextChar();
- if (ch == '|')
+ if (_ch == '|')
{
NextChar();
t = TokenId.DoubleBar;
@@ -1939,13 +1989,13 @@ void NextToken()
break;
case '"':
case '\'':
- char quote = ch;
+ char quote = _ch;
do
{
NextChar();
- while (textPos < textLen && ch != quote)
+ while (_textPos < _text.Length && _ch != quote)
{
- if (ch == '\\')
+ if (_ch == '\\')
{
NextChar();
}
@@ -1953,30 +2003,30 @@ void NextToken()
NextChar();
}
- if (textPos == textLen)
- throw ParseError(textPos, Resource.UnterminatedStringLiteral);
+ if (_textPos == _text.Length)
+ throw ParseError(_textPos, Resource.UnterminatedStringLiteral);
NextChar();
- } while (ch == quote);
+ } while (_ch == quote);
t = TokenId.StringLiteral;
break;
default:
- if (IsIdentifierStart(ch) || ch == '@' || ch == '_')
+ if (IsIdentifierStart(_ch) || _ch == '@' || _ch == '_')
{
do
{
NextChar();
- } while (IsIdentifierPart(ch) || ch == '_');
+ } while (IsIdentifierPart(_ch) || _ch == '_');
t = TokenId.Identifier;
break;
}
- if (Char.IsDigit(ch))
+ if (char.IsDigit(_ch))
{
t = TokenId.IntegerLiteral;
do
{
NextChar();
- } while (Char.IsDigit(ch));
- if (ch == '.')
+ } while (char.IsDigit(_ch));
+ if (_ch == '.')
{
t = TokenId.RealLiteral;
NextChar();
@@ -1984,37 +2034,36 @@ void NextToken()
do
{
NextChar();
- } while (Char.IsDigit(ch));
+ } while (char.IsDigit(_ch));
}
- if (ch == 'E' || ch == 'e')
+ if (_ch is 'E' or 'e')
{
t = TokenId.RealLiteral;
NextChar();
- if (ch == '+' || ch == '-')
+ if (_ch is '+' or '-')
NextChar();
ValidateDigit();
do
{
NextChar();
- } while (Char.IsDigit(ch));
+ } while (char.IsDigit(_ch));
}
- if (ch == 'F' || ch == 'f' || ch == 'M' || ch == 'm' || ch == 'D' || ch == 'd')
+ if (_ch is 'F' or 'f' or 'M' or 'm' or 'D' or 'd')
{
t = TokenId.RealLiteral;
NextChar();
}
break;
}
- if (textPos == textLen)
+ if (_textPos == _text.Length)
{
t = TokenId.End;
break;
}
- throw ParseError(textPos, Resource.InvalidCharacter, ch);
+ throw ParseError(_textPos, Resource.InvalidCharacter, _ch);
}
- token.id = t;
- token.text = text.Substring(tokenPos, textPos - tokenPos);
- token.pos = tokenPos;
+
+ _tokenId = t;
}
static bool IsIdentifierStart(char ch)
@@ -2026,7 +2075,7 @@ static bool IsIdentifierStart(char ch)
1 << (int)UnicodeCategory.ModifierLetter |
1 << (int)UnicodeCategory.OtherLetter |
1 << (int)UnicodeCategory.LetterNumber;
- return (1 << (int)Char.GetUnicodeCategory(ch) & mask) != 0;
+ return (1 << (int)char.GetUnicodeCategory(ch) & mask) != 0;
}
static bool IsIdentifierPart(char ch)
@@ -2043,44 +2092,47 @@ static bool IsIdentifierPart(char ch)
1 << (int)UnicodeCategory.NonSpacingMark |
1 << (int)UnicodeCategory.SpacingCombiningMark |
1 << (int)UnicodeCategory.Format;
- return (1 << (int)Char.GetUnicodeCategory(ch) & mask) != 0;
+ return (1 << (int)char.GetUnicodeCategory(ch) & mask) != 0;
}
bool TokenIdentifierIs(string id)
{
- return token.id == TokenId.Identifier && String.Equals(id, token.text, StringComparison.OrdinalIgnoreCase);
+ Token token = this.CurrentToken;
+ return _tokenId == TokenId.Identifier
+ && token.length == id.Length
+ && GetText(token).Equals(id.AsSpan(), StringComparison.OrdinalIgnoreCase);
}
string GetIdentifier()
{
ValidateToken(TokenId.Identifier, Resource.IdentifierExpected);
- string id = token.text;
+ var id = GetText(CurrentToken);
if (id.Length > 1 && id[0] == '@')
- id = id.Substring(1);
- return id;
+ id = id.Slice(1);
+ return id.ToString();
}
void ValidateDigit()
{
- if (!Char.IsDigit(ch))
- throw ParseError(textPos, Resource.DigitExpected);
+ if (!char.IsDigit(_ch))
+ throw ParseError(_textPos, Resource.DigitExpected);
}
void ValidateToken(TokenId t, string errorMessage)
{
- if (token.id != t)
+ if (_tokenId != t)
throw ParseError(errorMessage);
}
void ValidateToken(TokenId t)
{
- if (token.id != t)
+ if (_tokenId != t)
throw ParseError(Resource.SyntaxError);
}
Exception ParseError(string format, params object[] args)
{
- return ParseError(token.pos, format, args);
+ return ParseError(CurrentToken.pos, format, args);
}
static Exception ParseError(int pos, string format, params object[] args)
@@ -2088,17 +2140,20 @@ static Exception ParseError(int pos, string format, params object[] args)
return new ParseException(string.Format(CultureInfo.CurrentCulture, format, args), pos);
}
- static Dictionary CreateKeywords()
+ static FrozenDictionary CreateKeywords()
{
- Dictionary d = new Dictionary(StringComparer.OrdinalIgnoreCase);
- d.Add("true", trueLiteral);
- d.Add("false", falseLiteral);
- d.Add("null", nullLiteral);
- d.Add(keywordIt, keywordIt);
- d.Add(keywordIif, keywordIif);
- foreach (Type type in predefinedTypes)
+ Dictionary d = new(capacity: 5 + s_predefinedTypes.Length, StringComparer.OrdinalIgnoreCase)
+ {
+ { "true", TrueLiteral },
+ { "false", FalseLiteral },
+ { "null", NullLiteral },
+ { KeywordIt, KeywordIt },
+ { KeywordIif, KeywordIif }
+ };
+ foreach (Type type in s_predefinedTypes)
d.Add(type.Name, type);
- return d;
+
+ return d.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
}
}
}
diff --git a/src/OpenRiaServices.Hosting.Wcf/Framework/OpenRiaServices.Hosting.Wcf.csproj b/src/OpenRiaServices.Hosting.Wcf/Framework/OpenRiaServices.Hosting.Wcf.csproj
index e66531131..fa9f57ea6 100644
--- a/src/OpenRiaServices.Hosting.Wcf/Framework/OpenRiaServices.Hosting.Wcf.csproj
+++ b/src/OpenRiaServices.Hosting.Wcf/Framework/OpenRiaServices.Hosting.Wcf.csproj
@@ -18,7 +18,9 @@
-
+
+
+
diff --git a/src/OpenRiaServices.Hosting.Wcf/Test/Linq/QueryDeserializerTest.cs b/src/OpenRiaServices.Hosting.Wcf/Test/Linq/QueryDeserializerTest.cs
index eaac90390..89718e0e3 100644
--- a/src/OpenRiaServices.Hosting.Wcf/Test/Linq/QueryDeserializerTest.cs
+++ b/src/OpenRiaServices.Hosting.Wcf/Test/Linq/QueryDeserializerTest.cs
@@ -2,7 +2,6 @@
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Runtime.Serialization;
-using OpenRiaServices.Client.Test;
using OpenRiaServices.Server;
using OpenRiaServices.Hosting;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -45,7 +44,7 @@ public void TestConstructorAccessChecked()
new ServiceQueryPart("where", "it.Double.ToString() == String('a', 1000)")
};
- ExceptionHelper.ExpectException(delegate
+ Assert.ThrowsException(() =>
{
QueryDeserializer.Deserialize(DomainServiceDescription.GetDescription(typeof(QueryDeserializerDomainService)), queryable, queryParts);
}, string.Format(CultureInfo.CurrentCulture, System.Linq.Dynamic.Resource.MethodsAreInaccessible + " (at index 24)", typeof(String).Name));
@@ -69,7 +68,7 @@ private static void AccessMember(Type entityType, string memberToAccess)
new ServiceQueryPart("where", String.Format("it.{0} == \"Whatever\"", memberToAccess))
};
- ExceptionHelper.ExpectInvalidOperationException(delegate
+ Assert.ThrowsException(() =>
{
QueryDeserializer.Deserialize(DomainServiceDescription.GetDescription(typeof(QueryDeserializerDomainService)), queryable, queryParts);
}, string.Format(CultureInfo.CurrentCulture, System.Linq.Dynamic.Resource.UnknownPropertyOrField, memberToAccess, entityType.Name));
diff --git a/src/Test/WebsiteFullTrust/Web.config b/src/Test/WebsiteFullTrust/Web.config
index 0205fe8ee..3ce7c9e17 100644
--- a/src/Test/WebsiteFullTrust/Web.config
+++ b/src/Test/WebsiteFullTrust/Web.config
@@ -78,16 +78,20 @@
-
+
-
+
+
+
+
+
diff --git a/src/Test/WebsiteFullTrust/WebsiteFullTrust.csproj b/src/Test/WebsiteFullTrust/WebsiteFullTrust.csproj
index ec5b293a9..8cd153988 100644
--- a/src/Test/WebsiteFullTrust/WebsiteFullTrust.csproj
+++ b/src/Test/WebsiteFullTrust/WebsiteFullTrust.csproj
@@ -149,6 +149,9 @@
8.0.0
+
+ 8.0.0
+
4.5.4