diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs
index 9768b1ec..917dc745 100644
--- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs
+++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs
@@ -9,6 +9,7 @@
using JetBrains.Annotations;
using System.Linq.Dynamic.Core.Parser;
using System.Linq.Dynamic.Core.Util;
+
#if !(SILVERLIGHT)
using System.Diagnostics;
#endif
@@ -314,7 +315,7 @@ public static IQueryable Cast(this IQueryable source, Type type)
Check.NotNull(source);
Check.NotNull(type);
- var optimized = OptimizeExpression(Expression.Call(null, _cast.MakeGenericMethod(new[] { type }), new[] { source.Expression }));
+ var optimized = OptimizeExpression(Expression.Call(null, _cast.MakeGenericMethod(type), source.Expression));
return source.Provider.CreateQuery(optimized);
}
@@ -330,10 +331,13 @@ public static IQueryable Cast(this IQueryable source, ParsingConfig config, stri
{
Check.NotNull(source);
Check.NotNull(config);
- Check.NotEmpty(typeName, nameof(typeName));
+ Check.NotEmpty(typeName);
var finder = new TypeFinder(config, new KeywordsHelper(config));
- Type type = finder.FindTypeByName(typeName, null, true)!;
+ if (!finder.TryFindTypeByName(typeName, null, true, out var type))
+ {
+ throw new ParseException(string.Format(CultureInfo.CurrentCulture, Res.TypeNotFound, typeName));
+ }
return Cast(source, type);
}
@@ -1445,7 +1449,10 @@ public static IQueryable OfType(this IQueryable source, ParsingConfig config, st
Check.NotEmpty(typeName);
var finder = new TypeFinder(config, new KeywordsHelper(config));
- Type type = finder.FindTypeByName(typeName, null, true)!;
+ if (!finder.TryFindTypeByName(typeName, null, true, out var type))
+ {
+ throw new ParseException(string.Format(CultureInfo.CurrentCulture, Res.TypeNotFound, typeName));
+ }
return OfType(source, type);
}
diff --git a/src/System.Linq.Dynamic.Core/Exceptions/ParseException.cs b/src/System.Linq.Dynamic.Core/Exceptions/ParseException.cs
index fba8b3d1..42cca670 100644
--- a/src/System.Linq.Dynamic.Core/Exceptions/ParseException.cs
+++ b/src/System.Linq.Dynamic.Core/Exceptions/ParseException.cs
@@ -4,74 +4,85 @@
using System.Runtime.Serialization;
#endif
-namespace System.Linq.Dynamic.Core.Exceptions
+namespace System.Linq.Dynamic.Core.Exceptions;
+
+///
+/// Represents errors that occur while parsing dynamic linq string expressions.
+///
+#if !(SILVERLIGHT || UAP10_0 || NETSTANDARD || PORTABLE || WPSL || NETSTANDARD2_0)
+[Serializable]
+#endif
+public sealed class ParseException : Exception
{
+ private const int UnknownPosition = -1;
+
///
- /// Represents errors that occur while parsing dynamic linq string expressions.
+ /// The location in the parsed string that produced the .
+ /// If the value is -1, the position is unknown.
///
-#if !(SILVERLIGHT || UAP10_0 || NETSTANDARD || PORTABLE || WPSL || NETSTANDARD2_0)
- [Serializable]
-#endif
- public sealed class ParseException : Exception
+ public int Position { get; }
+
+ ///
+ /// Initializes a new instance of the class with a specified error message and position.
+ ///
+ /// The message that describes the error.
+ /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified.
+ public ParseException(string message, Exception? innerException = null) : this(message, UnknownPosition, innerException)
{
- ///
- /// The location in the parsed string that produced the .
- ///
- public int Position { get; }
+ }
- ///
- /// Initializes a new instance of the class with a specified error message and position.
- ///
- /// The message that describes the error.
- /// The location in the parsed string that produced the
- /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified.
- public ParseException(string message, int position, Exception? innerException = null) : base(message, innerException)
+ ///
+ /// Initializes a new instance of the class with a specified error message and position.
+ ///
+ /// The message that describes the error.
+ /// The location in the parsed string that produced the
+ /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified.
+ public ParseException(string message, int position, Exception? innerException = null) : base(message, innerException)
+ {
+ Position = position;
+ }
+
+ ///
+ /// Creates and returns a string representation of the current exception.
+ ///
+ /// A string representation of the current exception.
+ public override string ToString()
+ {
+ var text = string.Format(CultureInfo.CurrentCulture, Res.ParseExceptionFormat, Message, Position);
+
+ if (InnerException != null)
{
- Position = position;
+ text = $"{text} ---> {InnerException}{Environment.NewLine} --- End of inner exception stack trace ---";
}
- ///
- /// Creates and returns a string representation of the current exception.
- ///
- /// A string representation of the current exception.
- public override string ToString()
+ if (StackTrace != null)
{
- var text = string.Format(CultureInfo.CurrentCulture, Res.ParseExceptionFormat, Message, Position);
-
- if (InnerException != null)
- {
- text = $"{text} ---> {InnerException}{Environment.NewLine} --- End of inner exception stack trace ---";
- }
-
- if (StackTrace != null)
- {
- text = $"{text}{Environment.NewLine}{StackTrace}";
- }
-
- return text;
+ text = $"{text}{Environment.NewLine}{StackTrace}";
}
+ return text;
+ }
+
#if !(SILVERLIGHT || UAP10_0 || NETSTANDARD || PORTABLE || WPSL || NETSTANDARD2_0)
- private ParseException(SerializationInfo info, StreamingContext context) : base(info, context)
- {
- Position = (int)info.GetValue("position", typeof(int));
- }
+ private ParseException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ Position = (int)info.GetValue("position", typeof(int))!;
+ }
- ///
- /// When overridden in a derived class, sets the with information about the exception.
- ///
- /// The that holds the serialized object data about the exception being thrown.
- /// The that contains contextual information about the source or destination.
- ///
- ///
- ///
- ///
- public override void GetObjectData(SerializationInfo info, StreamingContext context)
- {
- base.GetObjectData(info, context);
+ ///
+ /// When overridden in a derived class, sets the with information about the exception.
+ ///
+ /// The that holds the serialized object data about the exception being thrown.
+ /// The that contains contextual information about the source or destination.
+ ///
+ ///
+ ///
+ ///
+ public override void GetObjectData(SerializationInfo info, StreamingContext context)
+ {
+ base.GetObjectData(info, context);
- info.AddValue("position", Position);
- }
-#endif
+ info.AddValue("position", Position);
}
-}
+#endif
+}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Extensions/ListExtensions.cs b/src/System.Linq.Dynamic.Core/Extensions/ListExtensions.cs
new file mode 100644
index 00000000..8885fc4e
--- /dev/null
+++ b/src/System.Linq.Dynamic.Core/Extensions/ListExtensions.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+
+namespace System.Linq.Dynamic.Core.Extensions;
+
+internal static class ListExtensions
+{
+ internal static void AddIfNotNull(this IList list, T? value)
+ {
+ if (value != null)
+ {
+ list.Add(value);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
index 63587615..1dddb75e 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
@@ -906,8 +906,7 @@ private AnyOf ParseStringLiteral(bool forceParseAsString)
if (_parsingConfig.SupportCastingToFullyQualifiedTypeAsString && !forceParseAsString && parsedStringValue.Length > 2 && parsedStringValue.Contains('.'))
{
// Try to resolve this string as a type
- var type = _typeFinder.FindTypeByName(parsedStringValue, null, false);
- if (type is { })
+ if (_typeFinder.TryFindTypeByName(parsedStringValue, null, false, out var type))
{
return type;
}
@@ -970,7 +969,7 @@ private Expression ParseIdentifier()
{
_textParser.ValidateToken(TokenId.Identifier);
- var isValidKeyWord = _keywordsHelper.TryGetValue(_textParser.CurrentToken.Text, out var keywordOrType);
+ var isValid = _keywordsHelper.TryGetValue(_textParser.CurrentToken.Text, out var keywordOrType);
var shouldPrioritizeType = true;
if (_parsingConfig.PrioritizePropertyOrFieldOverTheType && keywordOrType.IsThird)
@@ -983,7 +982,7 @@ private Expression ParseIdentifier()
}
}
- if (isValidKeyWord && shouldPrioritizeType)
+ if (isValid && shouldPrioritizeType)
{
var keywordOrFunctionAllowed = !_usedForOrderBy || _usedForOrderBy && !_parsingConfig.RestrictOrderByToPropertyOrField;
if (!keywordOrFunctionAllowed)
@@ -1397,8 +1396,7 @@ private Expression ParseNew()
_textParser.NextToken();
}
- newType = _typeFinder.FindTypeByName(newTypeName, new[] { _it, _parent, _root }, false);
- if (newType == null)
+ if (!_typeFinder.TryFindTypeByName(newTypeName, [_it, _parent, _root], false, out newType))
{
throw ParseError(_textParser.CurrentToken.Pos, Res.TypeNotFound, newTypeName);
}
@@ -1496,7 +1494,10 @@ private Expression CreateArrayInitializerExpression(List expressions
if (newType != null)
{
- return Expression.NewArrayInit(newType, expressions.Select(expression => _parsingConfig.ExpressionPromoter.Promote(expression, newType, true, true)));
+ var promotedExpressions = expressions
+ .Select(expression => _parsingConfig.ExpressionPromoter.Promote(expression, newType, true, true))
+ .OfType();
+ return Expression.NewArrayInit(newType, promotedExpressions);
}
return Expression.NewArrayInit(expressions.All(expression => expression.Type == expressions[0].Type) ? expressions[0].Type : typeof(object), expressions);
@@ -1543,6 +1544,7 @@ private Expression CreateNewExpression(List properties, List x.Name != "Item").ToArray();
}
+
var propertyTypes = propertyInfos.Select(p => p.PropertyType).ToArray();
var ctor = type.GetConstructor(propertyTypes);
if (ctor != null)
@@ -1550,25 +1552,26 @@ private Expression CreateNewExpression(List properties, List constructorParameters
+ var bindParametersSequentially = !properties.All(p => constructorParameters
.Any(cp => cp.Name == p.Name && (cp.ParameterType == p.Type || p.Type == Nullable.GetUnderlyingType(cp.ParameterType))));
- var expressionsPromoted = new List();
+ var expressionsPromoted = new List();
// Loop all expressions and promote if needed
for (int i = 0; i < constructorParameters.Length; i++)
{
if (bindParametersSequentially)
{
- expressionsPromoted.Add(_parsingConfig.ExpressionPromoter.Promote(expressions[i], propertyTypes[i], true, true));
+ expressionsPromoted.AddIfNotNull(_parsingConfig.ExpressionPromoter.Promote(expressions[i], propertyTypes[i], true, true));
}
else
{
- Type propertyType = constructorParameters[i].ParameterType;
- string cParameterName = constructorParameters[i].Name;
+ var propertyType = constructorParameters[i].ParameterType;
+ var cParameterName = constructorParameters[i].Name;
var propertyAndIndex = properties.Select((p, index) => new { p, index })
.First(p => p.p.Name == cParameterName && (p.p.Type == propertyType || p.p.Type == Nullable.GetUnderlyingType(propertyType)));
+
// Promote from Type to Nullable Type if needed
- expressionsPromoted.Add(_parsingConfig.ExpressionPromoter.Promote(expressions[propertyAndIndex.index], propertyType, true, true));
+ expressionsPromoted.AddIfNotNull(_parsingConfig.ExpressionPromoter.Promote(expressions[propertyAndIndex.index], propertyType, true, true));
}
}
@@ -1584,6 +1587,7 @@ private Expression CreateNewExpression(List properties, List _parsingConfig.ExpressionPromoter.Promote(expressions[i], t.ParameterType, true, true))
+ .OfType()
.ToArray();
return Expression.New(exactConstructor, expressionsPromoted);
@@ -1661,14 +1665,14 @@ private Expression ParseTypeAccess(Type type, bool getNext)
}
// This is a shorthand for explicitly converting a string to something
- bool shorthand = _textParser.CurrentToken.Id == TokenId.StringLiteral;
- if (_textParser.CurrentToken.Id == TokenId.OpenParen || shorthand)
+ var isShorthand = _textParser.CurrentToken.Id == TokenId.StringLiteral;
+ if (_textParser.CurrentToken.Id == TokenId.OpenParen || isShorthand)
{
Expression[] args;
- if (shorthand)
+ if (isShorthand)
{
var expressionOrType = ParseStringLiteral(true);
- args = new[] { expressionOrType.First };
+ args = [expressionOrType.First];
}
else
{
@@ -1685,7 +1689,7 @@ private Expression ParseTypeAccess(Type type, bool getNext)
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
if (args.Length == 1 && (args[0] == null || args[0] is ConstantExpression) && TryGenerateConversion(args[0], type, out var generatedExpression))
{
- return generatedExpression!;
+ return generatedExpression;
}
// If only 1 argument, and if the type is a ValueType and argType is also a ValueType, just Convert
@@ -2027,8 +2031,7 @@ private Expression ParseAsEnumOrNestedClass(string id)
}
var typeAsString = string.Concat(parts.Take(parts.Count - 2).ToArray());
- var type = _typeFinder.FindTypeByName(typeAsString, null, true);
- if (type == null)
+ if (!_typeFinder.TryFindTypeByName(typeAsString, null, true, out var type))
{
throw ParseError(_textParser.CurrentToken.Pos, Res.TypeNotFound, typeAsString);
}
@@ -2233,20 +2236,20 @@ private Type ResolveTypeFromExpressionValue(string functionName, ConstantExpress
private Type ResolveTypeStringFromArgument(string typeName)
{
- bool typeIsNullable = false;
+ var typeIsNullable = false;
+
if (typeName.EndsWith("?"))
{
typeName = typeName.TrimEnd('?');
typeIsNullable = true;
}
- var resultType = _typeFinder.FindTypeByName(typeName, new[] { _it, _parent, _root }, true);
- if (resultType == null)
+ if (!_typeFinder.TryFindTypeByName(typeName, [_it, _parent, _root], true, out var type))
{
throw ParseError(_textParser.CurrentToken.Pos, Res.TypeNotFound, typeName);
}
- return typeIsNullable ? TypeHelper.ToNullableType(resultType) : resultType;
+ return typeIsNullable ? TypeHelper.ToNullableType(type) : type;
}
private Expression[] ParseArgumentList()
diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs
index edcd48b5..43f7acbf 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionPromoter.cs
@@ -1,136 +1,135 @@
using System.Linq.Expressions;
using System.Reflection;
-namespace System.Linq.Dynamic.Core.Parser
+namespace System.Linq.Dynamic.Core.Parser;
+
+///
+/// ExpressionPromoter
+///
+public class ExpressionPromoter : IExpressionPromoter
{
+ private readonly NumberParser _numberParser;
+ private readonly ConstantExpressionHelper _constantExpressionHelper;
+
///
- /// ExpressionPromoter
+ /// Initializes a new instance of the class.
///
- public class ExpressionPromoter : IExpressionPromoter
+ /// The ParsingConfig.
+ public ExpressionPromoter(ParsingConfig config)
{
- private readonly NumberParser _numberParser;
- private readonly ConstantExpressionHelper _constantExpressionHelper;
+ _numberParser = new NumberParser(config);
+ _constantExpressionHelper = ConstantExpressionHelperFactory.GetInstance(config);
+ }
- ///
- /// Initializes a new instance of the class.
- ///
- /// The ParsingConfig.
- public ExpressionPromoter(ParsingConfig config)
+ ///
+ public virtual Expression? Promote(Expression sourceExpression, Type type, bool exact, bool convertExpression)
+ {
+ Type returnType;
+ if (sourceExpression is LambdaExpression lambdaExpression)
{
- _numberParser = new NumberParser(config);
- _constantExpressionHelper = ConstantExpressionHelperFactory.GetInstance(config);
+ returnType = lambdaExpression.GetReturnType();
}
-
- ///
- public virtual Expression? Promote(Expression expr, Type type, bool exact, bool convertExpr)
+ else
{
- Type returnType;
- if (expr is LambdaExpression lambdaExpression)
- {
- returnType = lambdaExpression.GetReturnType();
- }
- else
- {
- returnType = expr.Type;
- }
+ returnType = sourceExpression.Type;
+ }
- if (returnType == type || type.IsGenericParameter)
- {
- return expr;
- }
+ if (returnType == type || type.IsGenericParameter)
+ {
+ return sourceExpression;
+ }
- if (expr is ConstantExpression ce)
+ if (sourceExpression is ConstantExpression ce)
+ {
+ if (Constants.IsNull(ce))
{
- if (Constants.IsNull(ce))
+ if (!type.GetTypeInfo().IsValueType || TypeHelper.IsNullableType(type))
{
- if (!type.GetTypeInfo().IsValueType || TypeHelper.IsNullableType(type))
- {
- return Expression.Constant(null, type);
- }
+ return Expression.Constant(null, type);
}
- else
+ }
+ else
+ {
+ if (_constantExpressionHelper.TryGetText(ce, out var text))
{
- if (_constantExpressionHelper.TryGetText(ce, out var text))
- {
- Type target = TypeHelper.GetNonNullableType(type);
- object? value = null;
+ Type target = TypeHelper.GetNonNullableType(type);
+ object? value = null;
#if !(UAP10_0 || NETSTANDARD)
- switch (Type.GetTypeCode(ce.Type))
- {
- case TypeCode.Int32:
- case TypeCode.UInt32:
- case TypeCode.Int64:
- case TypeCode.UInt64:
- value = _numberParser.ParseNumber(text, target);
-
- // Make sure an enum value stays an enum value
- if (target.IsEnum)
- {
- value = Enum.ToObject(target, value!);
- }
- break;
-
- case TypeCode.Double:
- if (target == typeof(decimal) || target == typeof(double))
- {
- value = _numberParser.ParseNumber(text, target);
- }
- break;
+ switch (Type.GetTypeCode(ce.Type))
+ {
+ case TypeCode.Int32:
+ case TypeCode.UInt32:
+ case TypeCode.Int64:
+ case TypeCode.UInt64:
+ value = _numberParser.ParseNumber(text, target);
- case TypeCode.String:
- TypeHelper.TryParseEnum(text, target, out value);
- break;
- }
-#else
- if (ce.Type == typeof(int) || ce.Type == typeof(uint) || ce.Type == typeof(long) || ce.Type == typeof(ulong))
- {
- // If target is an enum value, just use the Value from the ConstantExpression
- if (target.GetTypeInfo().IsEnum)
- {
- value = Enum.ToObject(target, ce.Value);
- }
- else
+ // Make sure an enum value stays an enum value
+ if (target.IsEnum)
{
- value = _numberParser.ParseNumber(text!, target);
+ value = Enum.ToObject(target, value!);
}
- }
- else if (ce.Type == typeof(double))
- {
+ break;
+
+ case TypeCode.Double:
if (target == typeof(decimal) || target == typeof(double))
{
value = _numberParser.ParseNumber(text, target);
}
+ break;
+
+ case TypeCode.String:
+ TypeHelper.TryParseEnum(text, target, out value);
+ break;
+ }
+#else
+ if (ce.Type == typeof(int) || ce.Type == typeof(uint) || ce.Type == typeof(long) || ce.Type == typeof(ulong))
+ {
+ // If target is an enum value, just use the Value from the ConstantExpression
+ if (target.GetTypeInfo().IsEnum)
+ {
+ value = Enum.ToObject(target, ce.Value);
}
- else if (ce.Type == typeof(string) && TypeHelper.TryParseEnum(text, target, out value))
+ else
{
- // Empty if
+ value = _numberParser.ParseNumber(text!, target);
}
-#endif
- if (value != null)
+ }
+ else if (ce.Type == typeof(double))
+ {
+ if (target == typeof(decimal) || target == typeof(double))
{
- return Expression.Constant(value, type);
+ value = _numberParser.ParseNumber(text, target);
}
}
+ else if (ce.Type == typeof(string) && TypeHelper.TryParseEnum(text, target, out value))
+ {
+ // Empty if
+ }
+#endif
+ if (value != null)
+ {
+ return Expression.Constant(value, type);
+ }
}
}
+ }
- if (TypeHelper.IsCompatibleWith(returnType, type))
+ if (TypeHelper.IsCompatibleWith(returnType, type))
+ {
+ if (type == typeof(decimal) && TypeHelper.IsEnumType(sourceExpression.Type))
{
- if (type == typeof(decimal) && TypeHelper.IsEnumType(expr.Type))
- {
- return Expression.Convert(Expression.Convert(expr, Enum.GetUnderlyingType(expr.Type)), type);
- }
-
- if (type.GetTypeInfo().IsValueType || exact || expr.Type.GetTypeInfo().IsValueType && convertExpr)
- {
- return Expression.Convert(expr, type);
- }
+ return Expression.Convert(Expression.Convert(sourceExpression, Enum.GetUnderlyingType(sourceExpression.Type)), type);
+ }
- return expr;
+ if (type.GetTypeInfo().IsValueType || exact || sourceExpression.Type.GetTypeInfo().IsValueType && convertExpression)
+ {
+ return Expression.Convert(sourceExpression, type);
}
- return null;
+ return sourceExpression;
}
+
+ return null;
}
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/IExpressionPromoter.cs b/src/System.Linq.Dynamic.Core/Parser/IExpressionPromoter.cs
index 29d8ba7f..cf100c98 100644
--- a/src/System.Linq.Dynamic.Core/Parser/IExpressionPromoter.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/IExpressionPromoter.cs
@@ -1,22 +1,19 @@
using System.Linq.Expressions;
-namespace System.Linq.Dynamic.Core.Parser
+namespace System.Linq.Dynamic.Core.Parser;
+
+///
+/// Expression promoter is used to promote object or value types to their destination type when an automatic promotion is available such as: int to int?.
+///
+public interface IExpressionPromoter
{
///
- /// Expression promoter is used to promote object or value types
- /// to their destination type when an automatic promotion is available
- /// such as: int to int?
+ /// Promote an expression.
///
- public interface IExpressionPromoter
- {
- ///
- /// Promote an expression
- ///
- /// Source expression
- /// Destination data type to promote
- /// If the match must be exact
- /// Convert expression
- /// The promoted or null.
- Expression? Promote(Expression expr, Type type, bool exact, bool convertExpr);
- }
+ /// Source expression
+ /// Destination data type to promote
+ /// If the match must be exact
+ /// Convert expression
+ /// The promoted or null.
+ Expression? Promote(Expression sourceExpression, Type type, bool exact, bool convertExpression);
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/IKeywordsHelper.cs b/src/System.Linq.Dynamic.Core/Parser/IKeywordsHelper.cs
index 6fbd28bd..c0d5b5ed 100644
--- a/src/System.Linq.Dynamic.Core/Parser/IKeywordsHelper.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/IKeywordsHelper.cs
@@ -3,7 +3,7 @@
namespace System.Linq.Dynamic.Core.Parser;
-interface IKeywordsHelper
+internal interface IKeywordsHelper
{
- bool TryGetValue(string name, out AnyOf keywordOrType);
+ bool TryGetValue(string text, out AnyOf value);
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/ITypeFinder.cs b/src/System.Linq.Dynamic.Core/Parser/ITypeFinder.cs
index ece62c52..1d477547 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ITypeFinder.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ITypeFinder.cs
@@ -1,9 +1,9 @@
-using System.Linq.Expressions;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq.Expressions;
-namespace System.Linq.Dynamic.Core.Parser
+namespace System.Linq.Dynamic.Core.Parser;
+
+internal interface ITypeFinder
{
- interface ITypeFinder
- {
- Type? FindTypeByName(string name, ParameterExpression?[]? expressions, bool forceUseCustomTypeProvider);
- }
+ bool TryFindTypeByName(string name, ParameterExpression?[]? expressions, bool forceUseCustomTypeProvider, [NotNullWhen(true)] out Type? type);
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/KeywordsHelper.cs b/src/System.Linq.Dynamic.Core/Parser/KeywordsHelper.cs
index dc55bc35..380e3384 100644
--- a/src/System.Linq.Dynamic.Core/Parser/KeywordsHelper.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/KeywordsHelper.cs
@@ -15,20 +15,20 @@ internal class KeywordsHelper : IKeywordsHelper
public const string SYMBOL_PARENT = "^";
public const string SYMBOL_ROOT = "~";
+ public const string FUNCTION_AS = "as";
+ public const string FUNCTION_CAST = "cast";
public const string FUNCTION_IIF = "iif";
+ public const string FUNCTION_IS = "is";
public const string FUNCTION_ISNULL = "isnull";
public const string FUNCTION_NEW = "new";
public const string FUNCTION_NULLPROPAGATION = "np";
- public const string FUNCTION_IS = "is";
- public const string FUNCTION_AS = "as";
- public const string FUNCTION_CAST = "cast";
private readonly ParsingConfig _config;
- // Keywords compare case depends on the value from ParsingConfig.IsCaseSensitive
- private readonly Dictionary> _keywordMapping;
+ // Keywords, symbols and functions compare case depends on the value from ParsingConfig.IsCaseSensitive
+ private readonly Dictionary> _mappings;
- // PreDefined Types are not IgnoreCase
+ // Pre-defined Types are not IgnoreCase
private static readonly Dictionary PreDefinedTypeMapping = new();
// Custom DefinedTypes are not IgnoreCase
@@ -47,7 +47,7 @@ public KeywordsHelper(ParsingConfig config)
{
_config = Check.NotNull(config);
- _keywordMapping = new(config.IsCaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase)
+ _mappings = new(config.IsCaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase)
{
{ "true", Expression.Constant(true) },
{ "false", Expression.Constant(false) },
@@ -68,9 +68,9 @@ public KeywordsHelper(ParsingConfig config)
if (config.AreContextKeywordsEnabled)
{
- _keywordMapping.Add(KEYWORD_IT, KEYWORD_IT);
- _keywordMapping.Add(KEYWORD_PARENT, KEYWORD_PARENT);
- _keywordMapping.Add(KEYWORD_ROOT, KEYWORD_ROOT);
+ _mappings.Add(KEYWORD_IT, KEYWORD_IT);
+ _mappings.Add(KEYWORD_PARENT, KEYWORD_PARENT);
+ _mappings.Add(KEYWORD_ROOT, KEYWORD_ROOT);
}
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
@@ -84,45 +84,45 @@ public KeywordsHelper(ParsingConfig config)
}
}
- public bool TryGetValue(string name, out AnyOf keywordOrType)
+ public bool TryGetValue(string text, out AnyOf value)
{
- // 1. Try to get as keyword
- if (_keywordMapping.TryGetValue(name, out var keyWord))
+ // 1. Try to get as constant-expression, keyword, symbol or functions
+ if (_mappings.TryGetValue(text, out var expressionOrKeywordOrSymbolOrFunction))
{
- keywordOrType = keyWord;
+ value = expressionOrKeywordOrSymbolOrFunction;
return true;
}
- // 2. Try to get as predefined shorttype ("bool", "char", ...)
- if (PredefinedTypesHelper.PredefinedTypesShorthands.TryGetValue(name, out var predefinedShortHandType))
+ // 2. Try to get as predefined short-type ("bool", "char", ...)
+ if (PredefinedTypesHelper.PredefinedTypesShorthands.TryGetValue(text, out var predefinedShortHandType))
{
- keywordOrType = predefinedShortHandType;
+ value = predefinedShortHandType;
return true;
}
// 3. Try to get as predefined type ("Boolean", "System.Boolean", ..., "DateTime", "System.DateTime", ...)
- if (PreDefinedTypeMapping.TryGetValue(name, out var predefinedType))
+ if (PreDefinedTypeMapping.TryGetValue(text, out var predefinedType))
{
- keywordOrType = predefinedType;
+ value = predefinedType;
return true;
}
// 4. Try to get as an enum from the system namespace
- if (_config.SupportEnumerationsFromSystemNamespace && EnumerationsFromMscorlib.PredefinedEnumerationTypes.TryGetValue(name, out var predefinedEnumType))
+ if (_config.SupportEnumerationsFromSystemNamespace && EnumerationsFromMscorlib.PredefinedEnumerationTypes.TryGetValue(text, out var predefinedEnumType))
{
- keywordOrType = predefinedEnumType;
+ value = predefinedEnumType;
return true;
}
// 5. Try to get as custom type
- if (_customTypeMapping.TryGetValue(name, out var customType))
+ if (_customTypeMapping.TryGetValue(text, out var customType))
{
- keywordOrType = customType;
+ value = customType;
return true;
}
- // 6. Not found, return false
- keywordOrType = default;
+ // Not found, return false
+ value = default;
return false;
}
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Parser/TypeFinder.cs b/src/System.Linq.Dynamic.Core/Parser/TypeFinder.cs
index 0ee9f600..368cbe66 100644
--- a/src/System.Linq.Dynamic.Core/Parser/TypeFinder.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/TypeFinder.cs
@@ -1,91 +1,90 @@
-using System.Linq.Dynamic.Core.Validation;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq.Dynamic.Core.Validation;
using System.Linq.Expressions;
-namespace System.Linq.Dynamic.Core.Parser
+namespace System.Linq.Dynamic.Core.Parser;
+
+internal class TypeFinder : ITypeFinder
{
- internal class TypeFinder : ITypeFinder
+ private readonly IKeywordsHelper _keywordsHelper;
+ private readonly ParsingConfig _parsingConfig;
+
+ public TypeFinder(ParsingConfig parsingConfig, IKeywordsHelper keywordsHelper)
{
- private readonly IKeywordsHelper _keywordsHelper;
- private readonly ParsingConfig _parsingConfig;
+ _parsingConfig = Check.NotNull(parsingConfig);
+ _keywordsHelper = Check.NotNull(keywordsHelper);
+ }
- public TypeFinder(ParsingConfig parsingConfig, IKeywordsHelper keywordsHelper)
- {
- Check.NotNull(parsingConfig);
- Check.NotNull(keywordsHelper);
+ public bool TryFindTypeByName(string name, ParameterExpression?[]? expressions, bool forceUseCustomTypeProvider, [NotNullWhen(true)] out Type? type)
+ {
+ Check.NotEmpty(name);
- _keywordsHelper = keywordsHelper;
- _parsingConfig = parsingConfig;
+ if (_keywordsHelper.TryGetValue(name, out var keywordOrType) && keywordOrType.IsThird)
+ {
+ type = keywordOrType.Third;
+ return true;
}
- public Type? FindTypeByName(string name, ParameterExpression?[]? expressions, bool forceUseCustomTypeProvider)
+ if (expressions != null && TryResolveTypeUsingExpressions(name, expressions, out type))
{
- Check.NotEmpty(name);
+ return true;
+ }
+
+ return TryResolveTypeByUsingCustomTypeProvider(name, forceUseCustomTypeProvider, out type);
+ }
- _keywordsHelper.TryGetValue(name, out var keywordOrType);
+ private bool TryResolveTypeByUsingCustomTypeProvider(string name, bool forceUseCustomTypeProvider, [NotNullWhen(true)] out Type? resolvedType)
+ {
+ resolvedType = default;
- if (keywordOrType.IsThird)
+ if ((forceUseCustomTypeProvider || _parsingConfig.AllowNewToEvaluateAnyType) && _parsingConfig.CustomTypeProvider != null)
+ {
+ resolvedType = _parsingConfig.CustomTypeProvider.ResolveType(name);
+ if (resolvedType != null)
{
- return keywordOrType.Third;
+ return true;
}
- if (expressions != null && TryResolveTypeUsingExpressions(name, expressions, out var resolvedType))
+ // In case the type is not found based on fullname, try to get the type on simple-name if allowed
+ if (_parsingConfig.ResolveTypesBySimpleName)
{
- return resolvedType;
+ resolvedType = _parsingConfig.CustomTypeProvider.ResolveTypeBySimpleName(name);
+ return resolvedType != null;
}
-
- return ResolveTypeByUsingCustomTypeProvider(name, forceUseCustomTypeProvider);
}
- private Type? ResolveTypeByUsingCustomTypeProvider(string name, bool forceUseCustomTypeProvider)
+ return false;
+ }
+
+ private bool TryResolveTypeUsingExpressions(string name, ParameterExpression?[] expressions, [NotNullWhen(true)] out Type? result)
+ {
+ foreach (var expression in expressions.OfType())
{
- if ((forceUseCustomTypeProvider || _parsingConfig.AllowNewToEvaluateAnyType) && _parsingConfig.CustomTypeProvider != null)
+ if (name == expression.Type.Name)
{
- var resolvedType = _parsingConfig.CustomTypeProvider.ResolveType(name);
- if (resolvedType != null)
- {
- return resolvedType;
- }
-
- // In case the type is not found based on fullname, try to get the type on simplename if allowed
- if (_parsingConfig.ResolveTypesBySimpleName)
- {
- return _parsingConfig.CustomTypeProvider.ResolveTypeBySimpleName(name);
- }
+ result = expression.Type;
+ return true;
}
- return null;
- }
-
- private bool TryResolveTypeUsingExpressions(string name, ParameterExpression?[] expressions, out Type? result)
- {
- foreach (var expression in expressions.Where(e => e != null))
+ if (name == $"{expression.Type.Namespace}.{expression.Type.Name}")
{
- if (name == expression!.Type.Name)
- {
- result = expression.Type;
- return true;
- }
+ result = expression.Type;
+ return true;
+ }
- if (name == $"{expression.Type.Namespace}.{expression.Type.Name}")
+ if (_parsingConfig is { ResolveTypesBySimpleName: true, CustomTypeProvider: not null })
+ {
+ var possibleFullName = $"{expression.Type.Namespace}.{name}";
+ var resolvedType = _parsingConfig.CustomTypeProvider.ResolveType(possibleFullName);
+ if (resolvedType != null)
{
- result = expression.Type;
+ result = resolvedType;
return true;
}
-
- if (_parsingConfig.ResolveTypesBySimpleName && _parsingConfig.CustomTypeProvider != null)
- {
- string possibleFullName = $"{expression.Type.Namespace}.{name}";
- var resolvedType = _parsingConfig.CustomTypeProvider.ResolveType(possibleFullName);
- if (resolvedType != null)
- {
- result = resolvedType;
- return true;
- }
- }
}
-
- result = null;
- return false;
}
+
+ result = null;
+ return false;
}
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/ParsingConfig.cs b/src/System.Linq.Dynamic.Core/ParsingConfig.cs
index 43fc199a..70acd35c 100644
--- a/src/System.Linq.Dynamic.Core/ParsingConfig.cs
+++ b/src/System.Linq.Dynamic.Core/ParsingConfig.cs
@@ -39,7 +39,13 @@ public class ParsingConfig
};
///
- /// Gets or sets if parameter, method, and properties resolution should be case-sensitive or not.
+ /// Defines if the resolution should be case-sensitive for:
+ /// - fields and properties
+ /// - (extension) methods
+ /// - constant expressions ("null", "true", "false")
+ /// - keywords ("it", "parent", "root")
+ /// - functions ("as", "cast", "iif", "is", "isnull", "new", "np")
+ /// - operator aliases ("eq", "equal", "ne", "notequal", "neq", "lt", "LessThan", "le", "LessThanEqual", "gt", "GreaterThan", "ge", "GreaterThanEqual", "and", "AndAlso", "or", "OrElse", "not", "mod")
///
/// Default value is false.
///
@@ -286,5 +292,5 @@ public IQueryableAnalyzer QueryableAnalyzer
///
/// Default value is false.
///
- public bool RestrictOrderByToPropertyOrField { get; set; } = false;
+ public bool RestrictOrderByToPropertyOrField { get; set; }
}
\ No newline at end of file
diff --git a/src/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs b/src/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs
index 158b0768..c040a634 100644
--- a/src/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs
+++ b/src/System.Linq.Dynamic.Core/Tokenizer/TextParser.cs
@@ -7,10 +7,10 @@ namespace System.Linq.Dynamic.Core.Tokenizer;
///
/// TextParser which can be used to parse a text into tokens.
///
-public class TextParser
+internal class TextParser
{
private const char DefaultNumberDecimalSeparator = '.';
- private static readonly char[] EscapeCharacters = { '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v' };
+ private static readonly char[] EscapeCharacters = ['\\', 'a', 'b', 'f', 'n', 'r', 't', 'v'];
private readonly char _numberDecimalSeparator;
private readonly string _text;
diff --git a/test/System.Linq.Dynamic.Core.Tests/EntitiesTests.cs b/test/System.Linq.Dynamic.Core.Tests/EntitiesTests.cs
index f29fde4e..7ea211e4 100644
--- a/test/System.Linq.Dynamic.Core.Tests/EntitiesTests.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/EntitiesTests.cs
@@ -27,7 +27,7 @@ public EntitiesTests(EntitiesTestsDatabaseFixture fixture)
{
builder.UseSqlServer(fixture.ConnectionString);
}
-
+
_context = new BlogContext(builder.Options);
_context.Database.EnsureCreated();
#else
@@ -42,14 +42,14 @@ private void InternalPopulateTestData()
{
return;
}
-
+
for (int i = 0; i < 25; i++)
{
var blog = new Blog
{
- X = i.ToString(),
- Name = "Blog" + (i + 1),
- BlogId = 1000 + i,
+ X = i.ToString(),
+ Name = "Blog" + (i + 1),
+ BlogId = 1000 + i,
Created = DateTime.Now.AddDays(-Rnd.Next(0, 100))
};
@@ -57,13 +57,15 @@ private void InternalPopulateTestData()
for (int j = 0; j < 10; j++)
{
+ var postDate = DateTime.Today.AddDays(-Rnd.Next(0, 100)).AddSeconds(Rnd.Next(0, 30000));
var post = new Post
{
PostId = 10000 + i * 10 + j,
Blog = blog,
Title = $"Blog {i + 1} - Post {j + 1}",
Content = "My Content",
- PostDate = DateTime.Today.AddDays(-Rnd.Next(0, 100)).AddSeconds(Rnd.Next(0, 30000)),
+ PostDate = postDate,
+ CloseDate = Rnd.Next(0, 10) < 5 ? postDate.AddDays(1) : null,
NumberOfReads = Rnd.Next(0, 5000)
};
diff --git a/test/System.Linq.Dynamic.Core.Tests/Helpers/Entities/BlogContext.cs b/test/System.Linq.Dynamic.Core.Tests/Helpers/Entities/BlogContext.cs
index 809a7350..0d4cfb85 100644
--- a/test/System.Linq.Dynamic.Core.Tests/Helpers/Entities/BlogContext.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/Helpers/Entities/BlogContext.cs
@@ -21,7 +21,7 @@ public BlogContext(DbContextOptions options)
public void EnableLogging()
{
var serviceProvider = this.GetInfrastructure();
- var loggerFactory = serviceProvider.GetService();
+ var loggerFactory = serviceProvider.GetRequiredService();
loggerFactory.AddProvider(new DbLoggerProvider());
}
diff --git a/test/System.Linq.Dynamic.Core.Tests/Helpers/Entities/Post.cs b/test/System.Linq.Dynamic.Core.Tests/Helpers/Entities/Post.cs
index 1406b4e9..00d2e9a6 100644
--- a/test/System.Linq.Dynamic.Core.Tests/Helpers/Entities/Post.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/Helpers/Entities/Post.cs
@@ -20,4 +20,6 @@ public class Post
public int NumberOfReads { get; set; }
public DateTime PostDate { get; set; }
+
+ public DateTime? CloseDate { get; set; }
}
\ No newline at end of file
diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionPromoterTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionPromoterTests.cs
index 6178deeb..f500a5b8 100644
--- a/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionPromoterTests.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionPromoterTests.cs
@@ -5,53 +5,52 @@
using System.Linq.Expressions;
using Xunit;
-namespace System.Linq.Dynamic.Core.Tests.Parser
+namespace System.Linq.Dynamic.Core.Tests.Parser;
+
+public class ExpressionPromoterTests
{
- public class ExpressionPromoterTests
+ public class SampleDto
{
- public class SampleDto
- {
- public Guid data { get; set; }
- }
+ public Guid Data { get; set; }
+ }
- private readonly Mock _expressionPromoterMock;
- private readonly Mock _dynamicLinkCustomTypeProviderMock;
+ private readonly Mock _expressionPromoterMock;
+ private readonly Mock _dynamicLinkCustomTypeProviderMock;
- public ExpressionPromoterTests()
- {
- _dynamicLinkCustomTypeProviderMock = new Mock();
- _dynamicLinkCustomTypeProviderMock.Setup(d => d.GetCustomTypes()).Returns(new HashSet());
- _dynamicLinkCustomTypeProviderMock.Setup(d => d.ResolveType(It.IsAny())).Returns(typeof(SampleDto));
+ public ExpressionPromoterTests()
+ {
+ _dynamicLinkCustomTypeProviderMock = new Mock();
+ _dynamicLinkCustomTypeProviderMock.Setup(d => d.GetCustomTypes()).Returns(new HashSet());
+ _dynamicLinkCustomTypeProviderMock.Setup(d => d.ResolveType(It.IsAny())).Returns(typeof(SampleDto));
- _expressionPromoterMock = new Mock();
- _expressionPromoterMock.Setup(e => e.Promote(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(Expression.Constant(Guid.NewGuid()));
- }
+ _expressionPromoterMock = new Mock();
+ _expressionPromoterMock.Setup(e => e.Promote(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(Expression.Constant(Guid.NewGuid()));
+ }
- [Fact]
- public void DynamicExpressionParser_ParseLambda_WithCustomExpressionPromoter()
+ [Fact]
+ public void DynamicExpressionParser_ParseLambda_WithCustomExpressionPromoter()
+ {
+ // Assign
+ var parsingConfig = new ParsingConfig()
{
- // Assign
- var parsingConfig = new ParsingConfig()
- {
- AllowNewToEvaluateAnyType = true,
- CustomTypeProvider = _dynamicLinkCustomTypeProviderMock.Object,
- ExpressionPromoter = _expressionPromoterMock.Object
- };
-
- // Act
- string query = $"new {typeof(SampleDto).FullName}(@0 as data)";
- LambdaExpression expression = DynamicExpressionParser.ParseLambda(parsingConfig, null, query, new object[] { Guid.NewGuid().ToString() });
- Delegate del = expression.Compile();
- SampleDto result = (SampleDto)del.DynamicInvoke();
-
- // Assert
- Assert.NotNull(result);
-
- // Verify
- _dynamicLinkCustomTypeProviderMock.Verify(d => d.GetCustomTypes(), Times.Once);
- _dynamicLinkCustomTypeProviderMock.Verify(d => d.ResolveType($"{typeof(SampleDto).FullName}"), Times.Once);
-
- _expressionPromoterMock.Verify(e => e.Promote(It.IsAny(), typeof(Guid), true, true), Times.Once);
- }
+ AllowNewToEvaluateAnyType = true,
+ CustomTypeProvider = _dynamicLinkCustomTypeProviderMock.Object,
+ ExpressionPromoter = _expressionPromoterMock.Object
+ };
+
+ // Act
+ string query = $"new {typeof(SampleDto).FullName}(@0 as Data)";
+ LambdaExpression expression = DynamicExpressionParser.ParseLambda(parsingConfig, null, query, Guid.NewGuid().ToString());
+ Delegate del = expression.Compile();
+ SampleDto result = (SampleDto)del.DynamicInvoke();
+
+ // Assert
+ Assert.NotNull(result);
+
+ // Verify
+ _dynamicLinkCustomTypeProviderMock.Verify(d => d.GetCustomTypes(), Times.Once);
+ _dynamicLinkCustomTypeProviderMock.Verify(d => d.ResolveType($"{typeof(SampleDto).FullName}"), Times.Once);
+
+ _expressionPromoterMock.Verify(e => e.Promote(It.IsAny(), typeof(Guid), true, true), Times.Once);
}
-}
+}
\ No newline at end of file
diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/TypeFinderTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/TypeFinderTests.cs
index 904e1f9a..a4175e0a 100644
--- a/test/System.Linq.Dynamic.Core.Tests/Parser/TypeFinderTests.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/Parser/TypeFinderTests.cs
@@ -1,77 +1,76 @@
-using Moq;
-using NFluent;
-using System.Linq.Dynamic.Core.CustomTypeProviders;
+using System.Linq.Dynamic.Core.CustomTypeProviders;
using System.Linq.Dynamic.Core.Parser;
using System.Linq.Dynamic.Core.Tests.Entities;
using System.Linq.Expressions;
+using FluentAssertions;
+using Moq;
using Xunit;
-namespace System.Linq.Dynamic.Core.Tests.Parser
+namespace System.Linq.Dynamic.Core.Tests.Parser;
+
+public class TypeFinderTests
{
- public class TypeFinderTests
- {
- private readonly ParsingConfig _parsingConfig = new ParsingConfig();
- private readonly Mock _keywordsHelperMock;
- private readonly Mock _dynamicTypeProviderMock;
+ private readonly ParsingConfig _parsingConfig;
- private readonly TypeFinder _sut;
+ private readonly TypeFinder _sut;
- public TypeFinderTests()
- {
- _dynamicTypeProviderMock = new Mock();
- _dynamicTypeProviderMock.Setup(dt => dt.ResolveType(typeof(BaseEmployee).FullName)).Returns(typeof(BaseEmployee));
- _dynamicTypeProviderMock.Setup(dt => dt.ResolveType(typeof(Boss).FullName)).Returns(typeof(Boss));
- _dynamicTypeProviderMock.Setup(dt => dt.ResolveType(typeof(Worker).FullName)).Returns(typeof(Worker));
- _dynamicTypeProviderMock.Setup(dt => dt.ResolveTypeBySimpleName("Boss")).Returns(typeof(Boss));
+ public TypeFinderTests()
+ {
+ var dynamicTypeProviderMock = new Mock();
+ dynamicTypeProviderMock.Setup(dt => dt.ResolveType(typeof(BaseEmployee).FullName!)).Returns(typeof(BaseEmployee));
+ dynamicTypeProviderMock.Setup(dt => dt.ResolveType(typeof(Boss).FullName!)).Returns(typeof(Boss));
+ dynamicTypeProviderMock.Setup(dt => dt.ResolveType(typeof(Worker).FullName!)).Returns(typeof(Worker));
+ dynamicTypeProviderMock.Setup(dt => dt.ResolveTypeBySimpleName("Boss")).Returns(typeof(Boss));
- _parsingConfig = new ParsingConfig
- {
- CustomTypeProvider = _dynamicTypeProviderMock.Object
- };
+ _parsingConfig = new ParsingConfig
+ {
+ CustomTypeProvider = dynamicTypeProviderMock.Object
+ };
- _keywordsHelperMock = new Mock();
+ var keywordsHelperMock = new Mock();
- _sut = new TypeFinder(_parsingConfig, _keywordsHelperMock.Object);
- }
+ _sut = new TypeFinder(_parsingConfig, keywordsHelperMock.Object);
+ }
- [Fact]
- public void TypeFinder_FindTypeByName_With_SimpleTypeName_forceUseCustomTypeProvider_equals_false()
- {
- // Assign
- _parsingConfig.ResolveTypesBySimpleName = true;
+ [Fact]
+ public void TypeFinder_TryFindTypeByName_With_SimpleTypeName_forceUseCustomTypeProvider_Equals_false()
+ {
+ // Assign
+ _parsingConfig.ResolveTypesBySimpleName = true;
- // Act
- Type result = _sut.FindTypeByName("Boss", null, forceUseCustomTypeProvider: false);
+ // Act
+ var result = _sut.TryFindTypeByName("Boss", null, forceUseCustomTypeProvider: false, out var type);
- // Assert
- Check.That(result).IsNull();
- }
+ // Assert
+ result.Should().BeFalse();
+ }
- [Fact]
- public void TypeFinder_FindTypeByName_With_SimpleTypeName_forceUseCustomTypeProvider_equals_true()
- {
- // Assign
- _parsingConfig.ResolveTypesBySimpleName = true;
+ [Fact]
+ public void TypeFinder_TryFindTypeByName_With_SimpleTypeName_forceUseCustomTypeProvider_Equals_true()
+ {
+ // Assign
+ _parsingConfig.ResolveTypesBySimpleName = true;
- // Act
- Type result = _sut.FindTypeByName("Boss", null, forceUseCustomTypeProvider: true);
+ // Act
+ var result = _sut.TryFindTypeByName("Boss", null, forceUseCustomTypeProvider: true, out var type);
- // Assert
- Check.That(result).Equals(typeof(Boss));
- }
+ // Assert
+ result.Should().BeTrue();
+ type.Should().Be();
+ }
- [Fact]
- public void TypeFinder_FindTypeByName_With_SimpleTypeName_basedon_it()
- {
- // Assign
- _parsingConfig.ResolveTypesBySimpleName = true;
- var expressions = new[] { Expression.Parameter(typeof(BaseEmployee)) };
+ [Fact]
+ public void TypeFinder_TryFindTypeByName_With_SimpleTypeName_BasedOn_it()
+ {
+ // Assign
+ _parsingConfig.ResolveTypesBySimpleName = true;
+ var expressions = new[] { Expression.Parameter(typeof(BaseEmployee)) };
- // Act
- Type result = _sut.FindTypeByName("Boss", expressions, forceUseCustomTypeProvider: false);
+ // Act
+ var result = _sut.TryFindTypeByName("Boss", expressions, forceUseCustomTypeProvider: false, out var type);
- // Assert
- Check.That(result).Equals(typeof(Boss));
- }
+ // Assert
+ result.Should().BeTrue();
+ type.Should().Be();
}
-}
+}
\ No newline at end of file
diff --git a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs
index 4d029103..1622a54e 100644
--- a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Where.cs
@@ -82,9 +82,9 @@ public void Where_Dynamic_DateTime_NotEquals_Null()
IQueryable queryable = new[] { new Post() }.AsQueryable();
// Act
- var expected = queryable.Where(p => p.PostDate != null).ToArray();
- var result1 = queryable.Where("PostDate != null").ToArray();
- var result2 = queryable.Where("null != PostDate").ToArray();
+ var expected = queryable.Where(p => p.CloseDate != null).ToArray();
+ var result1 = queryable.Where("CloseDate != null").ToArray();
+ var result2 = queryable.Where("null != CloseDate").ToArray();
// Assert
Assert.Equal(expected, result1);
@@ -98,9 +98,9 @@ public void Where_Dynamic_DateTime_Equals_Null()
IQueryable queryable = new[] { new Post() }.AsQueryable();
// Act
- var expected = queryable.Where(p => p.PostDate == null).ToArray();
- var result1 = queryable.Where("PostDate == null").ToArray();
- var result2 = queryable.Where("null == PostDate").ToArray();
+ var expected = queryable.Where(p => p.CloseDate == null).ToArray();
+ var result1 = queryable.Where("CloseDate == null").ToArray();
+ var result2 = queryable.Where("null == CloseDate").ToArray();
// Assert
Assert.Equal(expected, result1);