diff --git a/Directory.Build.props b/Directory.Build.props
index 7cee1f75..be1a526f 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -4,7 +4,7 @@
- 1.0.10
+ 1.0.11
diff --git a/src-console/ConsoleAppEF2.0.2_InMemory/ConsoleApp_netcore2.0_EF2.0.2_InMemory.csproj b/src-console/ConsoleAppEF2.0.2_InMemory/ConsoleApp_netcore2.0_EF2.0.2_InMemory.csproj
index 6a17a6a2..3994f45d 100644
--- a/src-console/ConsoleAppEF2.0.2_InMemory/ConsoleApp_netcore2.0_EF2.0.2_InMemory.csproj
+++ b/src-console/ConsoleAppEF2.0.2_InMemory/ConsoleApp_netcore2.0_EF2.0.2_InMemory.csproj
@@ -13,6 +13,7 @@
+
diff --git a/src-console/ConsoleAppEF2.0.2_InMemory/Database/TestContext.cs b/src-console/ConsoleAppEF2.0.2_InMemory/Database/TestContext.cs
index 061d8cf8..ebc6597e 100644
--- a/src-console/ConsoleAppEF2.0.2_InMemory/Database/TestContext.cs
+++ b/src-console/ConsoleAppEF2.0.2_InMemory/Database/TestContext.cs
@@ -14,6 +14,8 @@ public class TestContext : DbContext
public virtual DbSet BaseDtos { get; set; }
+ public virtual DbSet ComplexDtos { get; set; }
+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLoggerFactory(MyLoggerFactory); // Warning: Do not create a new ILoggerFactory instance each time
@@ -27,6 +29,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.Entity().HasKey(c => c.Key);
modelBuilder.Entity().HasKey(b => b.BrandType);
modelBuilder.Entity().HasKey(t => t.Key);
+ modelBuilder.Entity().HasKey(t => t.Key);
}
// https://stackoverflow.com/questions/46212704/how-do-i-write-ef-functions-extension-method
diff --git a/src-console/ConsoleAppEF2.0.2_InMemory/Program.cs b/src-console/ConsoleAppEF2.0.2_InMemory/Program.cs
index 5dbd40ea..295eb8d8 100644
--- a/src-console/ConsoleAppEF2.0.2_InMemory/Program.cs
+++ b/src-console/ConsoleAppEF2.0.2_InMemory/Program.cs
@@ -41,6 +41,12 @@ public Type ResolveType(string typeName)
return ResolveType(assemblies, typeName);
}
+
+ public Type ResolveTypeBySimpleName(string typeName)
+ {
+ var assemblies = AppDomain.CurrentDomain.GetAssemblies();
+ return ResolveTypeBySimpleName(assemblies, typeName);
+ }
}
private static IQueryable GetQueryable()
diff --git a/src-console/ConsoleAppEF2.0/Database/ComplexDto.cs b/src-console/ConsoleAppEF2.0/Database/ComplexDto.cs
new file mode 100644
index 00000000..02dfd3ca
--- /dev/null
+++ b/src-console/ConsoleAppEF2.0/Database/ComplexDto.cs
@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace ConsoleAppEF2.Database
+{
+ public class ComplexDto
+ {
+ [Key]
+ public int Key { get; set; }
+
+ public string X { get; set; }
+
+ public IEnumerable ListOfBaseDtos { get; set; }
+ }
+}
diff --git a/src-console/ConsoleAppEF2.0/Program.cs b/src-console/ConsoleAppEF2.0/Program.cs
index ecce6aac..a0ed25a0 100644
--- a/src-console/ConsoleAppEF2.0/Program.cs
+++ b/src-console/ConsoleAppEF2.0/Program.cs
@@ -30,6 +30,12 @@ public Type ResolveType(string typeName)
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
return ResolveType(assemblies, typeName);
}
+
+ public Type ResolveTypeBySimpleName(string typeName)
+ {
+ var assemblies = AppDomain.CurrentDomain.GetAssemblies();
+ return ResolveTypeBySimpleName(assemblies, typeName);
+ }
}
private static IQueryable GetQueryable()
diff --git a/src-console/ConsoleAppEF2.1.1/Program.cs b/src-console/ConsoleAppEF2.1.1/Program.cs
index 70b8a1b2..645a545b 100644
--- a/src-console/ConsoleAppEF2.1.1/Program.cs
+++ b/src-console/ConsoleAppEF2.1.1/Program.cs
@@ -31,6 +31,12 @@ public Type ResolveType(string typeName)
return ResolveType(assemblies, typeName);
}
+
+ public Type ResolveTypeBySimpleName(string typeName)
+ {
+ var assemblies = AppDomain.CurrentDomain.GetAssemblies();
+ return ResolveTypeBySimpleName(assemblies, typeName);
+ }
}
private static IQueryable GetQueryable()
diff --git a/src-console/ConsoleAppEF2.1.1_InMemory/ConsoleApp_netcore2.1_EF2.1.1_InMemory.csproj b/src-console/ConsoleAppEF2.1.1_InMemory/ConsoleApp_netcore2.1_EF2.1.1_InMemory.csproj
index ce431f98..e61a14ff 100644
--- a/src-console/ConsoleAppEF2.1.1_InMemory/ConsoleApp_netcore2.1_EF2.1.1_InMemory.csproj
+++ b/src-console/ConsoleAppEF2.1.1_InMemory/ConsoleApp_netcore2.1_EF2.1.1_InMemory.csproj
@@ -14,6 +14,7 @@
+
diff --git a/src-console/ConsoleAppEF2.1.1_InMemory/Program.cs b/src-console/ConsoleAppEF2.1.1_InMemory/Program.cs
index dda0d3bc..1e1f7e08 100644
--- a/src-console/ConsoleAppEF2.1.1_InMemory/Program.cs
+++ b/src-console/ConsoleAppEF2.1.1_InMemory/Program.cs
@@ -32,9 +32,9 @@ public class NestedDto3
public int Id { get; set; }
}
- class NetCore21CustomTypeProvider : AbstractDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider
+ class TestCustomTypeProvider : DefaultDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider
{
- public HashSet GetCustomTypes()
+ public new HashSet GetCustomTypes()
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
@@ -45,68 +45,60 @@ public HashSet GetCustomTypes()
return set;
}
-
- public Type ResolveType(string typeName)
- {
- var assemblies = AppDomain.CurrentDomain.GetAssemblies();
- return ResolveType(assemblies, typeName);
- }
}
static void Main(string[] args)
{
- var q = new[] { new NestedDto(), new NestedDto { NestedDto2 = new NestedDto2 { NestedDto3 = new NestedDto3 { Id = 42 } } } }.AsQueryable();
+ //var q = new[] { new NestedDto(), new NestedDto { NestedDto2 = new NestedDto2 { NestedDto3 = new NestedDto3 { Id = 42 } } } }.AsQueryable();
- var np1 = q.Select("np(it.NestedDto2.NestedDto3.Id, 0)");
- var npResult1 = np1.ToDynamicList();
- Console.WriteLine("npResult1 {0}", JsonConvert.SerializeObject(npResult1, Formatting.Indented));
+ //var np1 = q.Select("np(it.NestedDto2.NestedDto3.Id, 0)");
+ //var npResult1 = np1.ToDynamicList();
+ //Console.WriteLine("npResult1 {0}", JsonConvert.SerializeObject(npResult1, Formatting.Indented));
- var np2 = q.Select("np(it.NestedDto2.NestedDto3.Id)");
- var npResult2 = np2.ToDynamicList();
- Console.WriteLine("npResult2 {0}", JsonConvert.SerializeObject(npResult2, Formatting.Indented));
+ //var np2 = q.Select("np(it.NestedDto2.NestedDto3.Id)");
+ //var npResult2 = np2.ToDynamicList();
+ //Console.WriteLine("npResult2 {0}", JsonConvert.SerializeObject(npResult2, Formatting.Indented));
- var r1 = q.Select("it != null && it.NestedDto2 != null ? it.NestedDto2.Id : null");
- var list1 = r1.ToDynamicList();
+ //var r1 = q.Select("it != null && it.NestedDto2 != null ? it.NestedDto2.Id : null");
+ //var list1 = r1.ToDynamicList();
- var r2 = q.Select("it != null && it.NestedDto2 != null ? it.NestedDto2 : null");
- var list2 = r2.ToDynamicList();
+ //var r2 = q.Select("it != null && it.NestedDto2 != null ? it.NestedDto2 : null");
+ //var list2 = r2.ToDynamicList();
var config = new ParsingConfig
{
AllowNewToEvaluateAnyType = true,
- CustomTypeProvider = new NetCore21CustomTypeProvider()
- };
-
- // Act
- var testDataAsQueryable = new List { "name1", "name2" }.AsQueryable();
- var projectedData = (IQueryable)testDataAsQueryable.Select(config, $"new {typeof(NestedDto).FullName}(~ as Name)");
- Console.WriteLine(projectedData.First().Name);
- Console.WriteLine(projectedData.Last().Name);
-
- var all = new
- {
- test1 = new List { 1, 2, 3 }.ToDynamicList(typeof(int)),
- test2 = new List { 4, 5, 6 }.ToDynamicList(typeof(int)),
- test3 = new List
-
-
-
-
-
@@ -112,6 +101,10 @@
+
+
+
+
diff --git a/src/System.Linq.Dynamic.Core/Compatibility/CustomTypeBuilderExtensions.cs b/src/System.Linq.Dynamic.Core/Compatibility/CustomTypeBuilderExtensions.cs
index 539850ba..c77ba8d0 100644
--- a/src/System.Linq.Dynamic.Core/Compatibility/CustomTypeBuilderExtensions.cs
+++ b/src/System.Linq.Dynamic.Core/Compatibility/CustomTypeBuilderExtensions.cs
@@ -12,7 +12,7 @@ public static Type CreateType(this TypeBuilder tb)
}
#endif
-#if NET35 || NET40 || SILVERLIGHT || WPSL
+#if NET35 || NET40 || SILVERLIGHT || WPSL || NETCOREAPP
public static PropertyBuilder DefineProperty(this TypeBuilder tb, string name, PropertyAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes)
{
return tb.DefineProperty(name, attributes, returnType, parameterTypes);
@@ -32,4 +32,4 @@ public static Type AsType(this GenericTypeParameterBuilder builder)
}
#endif
}
-}
\ No newline at end of file
+}
diff --git a/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs b/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs
index 40e25650..0b9ab268 100644
--- a/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs
+++ b/src/System.Linq.Dynamic.Core/CustomTypeProviders/AbstractDynamicLinqCustomTypeProvider.cs
@@ -47,6 +47,35 @@ protected Type ResolveType([NotNull] IEnumerable assemblies, [NotNull]
return null;
}
+ ///
+ /// Resolve a type by the simple name which is registered in the current application domain.
+ ///
+ /// The assemblies to inspect.
+ /// The simple typename to resolve.
+ /// A resolved or null when not found.
+ protected Type ResolveTypeBySimpleName([NotNull] IEnumerable assemblies, [NotNull] string simpleTypeName)
+ {
+ Check.NotNull(assemblies, nameof(assemblies));
+ Check.NotEmpty(simpleTypeName, nameof(simpleTypeName));
+
+ foreach (var assembly in assemblies)
+ {
+ var fullnames = assembly.GetTypes().Select(t => t.FullName).Distinct();
+ var firstMatchingFullname = fullnames.FirstOrDefault(fn => fn.EndsWith($".{simpleTypeName}"));
+
+ if (firstMatchingFullname != null)
+ {
+ Type resolvedType = assembly.GetType(firstMatchingFullname, false, true);
+ if (resolvedType != null)
+ {
+ return resolvedType;
+ }
+ }
+ }
+
+ return null;
+ }
+
#if (WINDOWS_APP || DOTNET5_1 || UAP10_0 || NETSTANDARD)
///
/// Gets the assembly types annotated with in an Exception friendly way.
diff --git a/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs b/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs
index ad91dc39..757352e9 100644
--- a/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs
+++ b/src/System.Linq.Dynamic.Core/CustomTypeProviders/DefaultDynamicLinqCustomTypeProvider.cs
@@ -54,6 +54,15 @@ public Type ResolveType(string typeName)
return ResolveType(assemblies, typeName);
}
+ ///
+ public Type ResolveTypeBySimpleName(string simpleTypeName)
+ {
+ Check.NotEmpty(simpleTypeName, nameof(simpleTypeName));
+
+ IEnumerable assemblies = _assemblyHelper.GetAssemblies();
+ return ResolveTypeBySimpleName(assemblies, simpleTypeName);
+ }
+
private HashSet GetCustomTypesInternal()
{
IEnumerable assemblies = _assemblyHelper.GetAssemblies();
diff --git a/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs b/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs
index a56a9a93..3a8c0ed0 100644
--- a/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs
+++ b/src/System.Linq.Dynamic.Core/CustomTypeProviders/IDynamicLinkCustomTypeProvider.cs
@@ -15,10 +15,17 @@ public interface IDynamicLinkCustomTypeProvider
HashSet GetCustomTypes();
///
- /// Resolve any type which is registered in the current application domain.
+ /// Resolve any type by fullname which is registered in the current application domain.
///
/// The typename to resolve.
/// A resolved or null when not found.
Type ResolveType([NotNull] string typeName);
+
+ ///
+ /// Resolve any type by the simple name which is registered in the current application domain.
+ ///
+ /// The typename to resolve.
+ /// A resolved or null when not found.
+ Type ResolveTypeBySimpleName([NotNull] string simpleTypeName);
}
}
diff --git a/src/System.Linq.Dynamic.Core/DefaultAssemblyHelper.cs b/src/System.Linq.Dynamic.Core/DefaultAssemblyHelper.cs
index 31794923..0880c324 100644
--- a/src/System.Linq.Dynamic.Core/DefaultAssemblyHelper.cs
+++ b/src/System.Linq.Dynamic.Core/DefaultAssemblyHelper.cs
@@ -29,7 +29,7 @@ public Assembly[] GetAssemblies()
return assemblies.ToArray();
}
-#elif (DOTNET5_1 || WINDOWS_APP || UAP10_0 || NETSTANDARD || WPSL)
+#elif DOTNET5_1 || WINDOWS_APP || UAP10_0 || NETSTANDARD || WPSL
public Assembly[] GetAssemblies()
{
throw new NotSupportedException();
@@ -37,8 +37,12 @@ public Assembly[] GetAssemblies()
#else
public Assembly[] GetAssemblies()
{
+#if NETCORE1_1
+ return AppDomain.NetCoreApp.AppDomain.CurrentDomain.GetAssemblies(thisType).ToArray();
+#else
return AppDomain.CurrentDomain.GetAssemblies();
+#endif
}
#endif
}
-}
\ No newline at end of file
+ }
diff --git a/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs b/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs
index fa5ab9f1..d644aaba 100644
--- a/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs
+++ b/src/System.Linq.Dynamic.Core/DynamicExpressionParser.cs
@@ -1,9 +1,8 @@
-using System.Linq.Dynamic.Core.Parser;
+using JetBrains.Annotations;
+using System.Linq.Dynamic.Core.Parser;
using System.Linq.Dynamic.Core.Util;
-using JetBrains.Annotations;
using System.Linq.Dynamic.Core.Validation;
using System.Linq.Expressions;
-using System.Collections.Generic;
namespace System.Linq.Dynamic.Core
{
@@ -71,7 +70,7 @@ public static LambdaExpression ParseLambda([CanBeNull] ParsingConfig parsingConf
{
var renamer = new ParameterExpressionRenamer(parser.ItName);
parsedExpression = renamer.Rename(parsedExpression, out ParameterExpression newParameterExpression);
-
+
return Expression.Lambda(parsedExpression, new[] { newParameterExpression });
}
else
diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs
index 6e5b21ea..bd58c7c1 100644
--- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs
+++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs
@@ -197,6 +197,56 @@ public static IEnumerable AsEnumerable([NotNull] this IQueryable source
}
#endregion AsEnumerable
+ #region Cast
+ private static readonly MethodInfo _cast = GetGenericMethod(nameof(Queryable.Cast));
+
+ ///
+ /// Converts the elements of an to the specified type.
+ ///
+ /// The that contains the elements to be converted.
+ /// The type to convert the elements of source to.
+ /// An that contains each element of the source sequence converted to the specified type.
+ public static IQueryable Cast([NotNull] this IQueryable source, [NotNull] Type type)
+ {
+ Check.NotNull(source, nameof(source));
+ Check.NotNull(type, nameof(type));
+
+ var optimized = OptimizeExpression(Expression.Call(null, _cast.MakeGenericMethod(new Type[] { type }), new Expression[] { source.Expression }));
+
+ return source.Provider.CreateQuery(optimized);
+ }
+
+ ///
+ /// Converts the elements of an to the specified type.
+ ///
+ /// The that contains the elements to be converted.
+ /// The .
+ /// The type to convert the elements of source to.
+ /// An that contains each element of the source sequence converted to the specified type.
+ public static IQueryable Cast([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] string typeName)
+ {
+ Check.NotNull(source, nameof(source));
+ Check.NotNull(config, nameof(config));
+ Check.NotEmpty(typeName, nameof(typeName));
+
+ var finder = new TypeFinder(config, new KeywordsHelper(config));
+ Type type = finder.FindTypeByName(typeName, null, true);
+
+ return Cast(source, type);
+ }
+
+ ///
+ /// Converts the elements of an to the specified type.
+ ///
+ /// The that contains the elements to be converted.
+ /// The type to convert the elements of source to.
+ /// An that contains each element of the source sequence converted to the specified type.
+ public static IQueryable Cast([NotNull] this IQueryable source, [NotNull] string typeName)
+ {
+ return Cast(source, ParsingConfig.Default, typeName);
+ }
+ #endregion Cast
+
#region Count
private static readonly MethodInfo _count = GetMethod(nameof(Queryable.Count));
@@ -943,6 +993,56 @@ public static dynamic LastOrDefault([NotNull] this IQueryable source, [NotNull]
}
#endregion LastOrDefault
+ #region OfType
+ private static readonly MethodInfo _ofType = GetGenericMethod(nameof(Queryable.OfType));
+
+ ///
+ /// Filters the elements of an based on a specified type.
+ ///
+ /// An whose elements to filter.
+ /// The type to filter the elements of the sequence on.
+ /// A collection that contains the elements from source that have the type.
+ public static IQueryable OfType([NotNull] this IQueryable source, [NotNull] Type type)
+ {
+ Check.NotNull(source, nameof(source));
+ Check.NotNull(type, nameof(type));
+
+ var optimized = OptimizeExpression(Expression.Call(null, _ofType.MakeGenericMethod(new Type[] { type }), new Expression[] { source.Expression }));
+
+ return source.Provider.CreateQuery(optimized);
+ }
+
+ ///
+ /// Filters the elements of an based on a specified type.
+ ///
+ /// An whose elements to filter.
+ /// The .
+ /// The type to filter the elements of the sequence on.
+ /// A collection that contains the elements from source that have the type.
+ public static IQueryable OfType([NotNull] this IQueryable source, [NotNull] ParsingConfig config, [NotNull] string typeName)
+ {
+ Check.NotNull(source, nameof(source));
+ Check.NotNull(config, nameof(config));
+ Check.NotEmpty(typeName, nameof(typeName));
+
+ var finder = new TypeFinder(config, new KeywordsHelper(config));
+ Type type = finder.FindTypeByName(typeName, null, true);
+
+ return OfType(source, type);
+ }
+
+ ///
+ /// Filters the elements of an based on a specified type.
+ ///
+ /// An whose elements to filter.
+ /// The type to filter the elements of the sequence on.
+ /// A collection that contains the elements from source that have the type.
+ public static IQueryable OfType([NotNull] this IQueryable source, [NotNull] string typeName)
+ {
+ return OfType(source, ParsingConfig.Default, typeName);
+ }
+ #endregion OfType
+
#region OrderBy
///
/// Sorts the elements of a sequence in ascending or descending order according to a key.
@@ -2054,6 +2154,11 @@ private static TResult Execute(MethodInfo operatorMethodInfo, IQueryabl
return source.Provider.Execute(optimized);
}
+ private static MethodInfo GetGenericMethod(string name)
+ {
+ return typeof(Queryable).GetTypeInfo().GetDeclaredMethods(name).Single(mi => mi.IsGenericMethod);
+ }
+
private static MethodInfo GetMethod(string name, int parameterCount = 0, Func predicate = null)
{
try
diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
index 87a3af72..8fc4eeaa 100644
--- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
@@ -25,9 +25,10 @@ public class ExpressionParser
private readonly ParsingConfig _parsingConfig;
private readonly MethodFinder _methodFinder;
- private readonly KeywordsHelper _keywordsHelper;
+ private readonly IKeywordsHelper _keywordsHelper;
private readonly TextParser _textParser;
private readonly IExpressionHelper _expressionHelper;
+ private readonly ITypeFinder _typeFinder;
private readonly Dictionary _internals;
private readonly Dictionary _symbols;
@@ -73,6 +74,7 @@ public ExpressionParser([CanBeNull] ParameterExpression[] parameters, [NotNull]
_textParser = new TextParser(expression);
_methodFinder = new MethodFinder(_parsingConfig);
_expressionHelper = new ExpressionHelper(_parsingConfig);
+ _typeFinder = new TypeFinder(_parsingConfig, _keywordsHelper);
}
void ProcessParameters(ParameterExpression[] parameters)
@@ -240,20 +242,6 @@ Expression ParseLambdaOperator()
return expr;
}
- // isnull(a,b) function
- Expression ParseFunctionIsNull()
- {
- int errorPos = _textParser.CurrentToken.Pos;
- _textParser.NextToken();
- Expression[] args = ParseArgumentList();
- if (args.Length != 2)
- {
- throw ParseError(errorPos, Res.IsNullRequiresTwoArgs);
- }
-
- return Expression.Coalesce(args[0], args[1]);
- }
-
// ||, or operator
Expression ParseOrOperator()
{
@@ -709,7 +697,7 @@ Expression ParsePrimary()
}
else if (_textParser.CurrentToken.Id == TokenId.NullPropagation)
{
- throw new NotSupportedException("An expression tree lambda may not contain a null propagating operator.");
+ throw new NotSupportedException("An expression tree lambda may not contain a null propagating operator. Use the 'np()' or 'np(...)' (null-propagation) function instead.");
}
else if (_textParser.CurrentToken.Id == TokenId.OpenBracket)
{
@@ -953,18 +941,41 @@ Expression ParseIdentifier()
return ParseTypeAccess(typeValue);
}
- if (value == (object)KeywordsHelper.KEYWORD_IT) return ParseIt();
- if (value == (object)KeywordsHelper.KEYWORD_PARENT) return ParseParent();
- if (value == (object)KeywordsHelper.KEYWORD_ROOT) return ParseRoot();
+ switch (value)
+ {
+ case KeywordsHelper.KEYWORD_IT:
+ case KeywordsHelper.SYMBOL_IT:
+ return ParseIt();
- if (value == (object)KeywordsHelper.SYMBOL_IT) return ParseIt();
- if (value == (object)KeywordsHelper.SYMBOL_PARENT) return ParseParent();
- if (value == (object)KeywordsHelper.SYMBOL_ROOT) return ParseRoot();
+ case KeywordsHelper.KEYWORD_PARENT:
+ case KeywordsHelper.SYMBOL_PARENT:
+ return ParseParent();
- if (value == (object)KeywordsHelper.FUNCTION_IIF) return ParseFunctionIif();
- if (value == (object)KeywordsHelper.FUNCTION_ISNULL) return ParseFunctionIsNull();
- if (value == (object)KeywordsHelper.FUNCTION_NEW) return ParseNew();
- if (value == (object)KeywordsHelper.FUNCTION_NULLPROPAGATION) return ParseFunctionNullPropagation();
+ case KeywordsHelper.KEYWORD_ROOT:
+ case KeywordsHelper.SYMBOL_ROOT:
+ return ParseRoot();
+
+ case KeywordsHelper.FUNCTION_IIF:
+ return ParseFunctionIif();
+
+ case KeywordsHelper.FUNCTION_ISNULL:
+ return ParseFunctionIsNull();
+
+ case KeywordsHelper.FUNCTION_NEW:
+ return ParseNew();
+
+ case KeywordsHelper.FUNCTION_NULLPROPAGATION:
+ return ParseFunctionNullPropagation();
+
+ case KeywordsHelper.FUNCTION_IS:
+ return ParseFunctionIs();
+
+ case KeywordsHelper.FUNCTION_AS:
+ return ParseFunctionAs();
+
+ case KeywordsHelper.FUNCTION_CAST:
+ return ParseFunctionCast();
+ }
_textParser.NextToken();
@@ -1032,6 +1043,20 @@ Expression ParseRoot()
return _root;
}
+ // isnull(a,b) function
+ Expression ParseFunctionIsNull()
+ {
+ int errorPos = _textParser.CurrentToken.Pos;
+ _textParser.NextToken();
+ Expression[] args = ParseArgumentList();
+ if (args.Length != 2)
+ {
+ throw ParseError(errorPos, Res.IsNullRequiresTwoArgs);
+ }
+
+ return Expression.Coalesce(args[0], args[1]);
+ }
+
// iif(test, ifTrue, ifFalse) function
Expression ParseFunctionIif()
{
@@ -1072,6 +1097,63 @@ Expression ParseFunctionNullPropagation()
throw ParseError(errorPos, Res.NullPropagationRequiresMemberExpression);
}
+ // Is(...) function
+ Expression ParseFunctionIs()
+ {
+ int errorPos = _textParser.CurrentToken.Pos;
+ string functionName = _textParser.CurrentToken.Text;
+ _textParser.NextToken();
+
+ Expression[] args = ParseArgumentList();
+
+ if (args.Length != 1)
+ {
+ throw ParseError(errorPos, Res.FunctionRequiresOneArg, functionName);
+ }
+
+ Type resolvedType = ResolveTypeFromArgumentExpression(functionName, args[0]);
+
+ return Expression.TypeIs(_it, resolvedType);
+ }
+
+ // As(...) function
+ Expression ParseFunctionAs()
+ {
+ int errorPos = _textParser.CurrentToken.Pos;
+ string functionName = _textParser.CurrentToken.Text;
+ _textParser.NextToken();
+
+ Expression[] args = ParseArgumentList();
+
+ if (args.Length != 1)
+ {
+ throw ParseError(errorPos, Res.FunctionRequiresOneArg, functionName);
+ }
+
+ Type resolvedType = ResolveTypeFromArgumentExpression(functionName, args[0]);
+
+ return Expression.TypeAs(_it, resolvedType);
+ }
+
+ // Cast(...) function
+ Expression ParseFunctionCast()
+ {
+ int errorPos = _textParser.CurrentToken.Pos;
+ string functionName = _textParser.CurrentToken.Text;
+ _textParser.NextToken();
+
+ Expression[] args = ParseArgumentList();
+
+ if (args.Length != 1)
+ {
+ throw ParseError(errorPos, Res.FunctionRequiresOneArg, functionName);
+ }
+
+ Type resolvedType = ResolveTypeFromArgumentExpression(functionName, args[0]);
+
+ return Expression.ConvertChecked(_it, resolvedType);
+ }
+
Expression GenerateConditional(Expression test, Expression expr1, Expression expr2, int errorPos)
{
if (test.Type != typeof(bool))
@@ -1162,7 +1244,7 @@ Expression ParseNew()
_textParser.NextToken();
}
- newType = FindType(newTypeName);
+ newType = _typeFinder.FindTypeByName(newTypeName, new[] { _it, _parent, _root }, false);
if (newType == null)
{
throw ParseError(_textParser.CurrentToken.Pos, Res.TypeNotFound, newTypeName);
@@ -1270,7 +1352,7 @@ private Expression CreateNewExpression(List properties, List();
for (int i = 0; i < expressions.Count; i++)
@@ -1289,7 +1371,7 @@ private Expression CreateNewExpression(List properties, List _keywords = new Dictionary(StringComparer.OrdinalIgnoreCase)
{
@@ -41,6 +44,9 @@ public KeywordsHelper(ParsingConfig config)
_keywords.Add(FUNCTION_ISNULL, FUNCTION_ISNULL);
_keywords.Add(FUNCTION_NEW, FUNCTION_NEW);
_keywords.Add(FUNCTION_NULLPROPAGATION, FUNCTION_NULLPROPAGATION);
+ _keywords.Add(FUNCTION_IS, FUNCTION_IS);
+ _keywords.Add(FUNCTION_AS, FUNCTION_AS);
+ _keywords.Add(FUNCTION_CAST, FUNCTION_CAST);
foreach (Type type in PredefinedTypesHelper.PredefinedTypes.OrderBy(kvp => kvp.Value).Select(kvp => kvp.Key))
{
diff --git a/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/IEnumerableSignatures.cs b/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/IEnumerableSignatures.cs
index 0af0de06..c561694a 100644
--- a/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/IEnumerableSignatures.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/IEnumerableSignatures.cs
@@ -15,25 +15,33 @@ internal interface IEnumerableSignatures
void Average(int selector);
void Average(long? selector);
void Average(long selector);
+ void Cast(string type);
void Contains(object selector);
void Count();
void Count(bool predicate);
void DefaultIfEmpty();
void DefaultIfEmpty(object defaultValue);
void Distinct();
+ void First();
void First(bool predicate);
+ void FirstOrDefault();
void FirstOrDefault(bool predicate);
void GroupBy(object keySelector);
void GroupBy(object keySelector, object elementSelector);
+ void Last();
void Last(bool predicate);
+ void LastOrDefault();
void LastOrDefault(bool predicate);
void Max(object selector);
void Min(object selector);
+ void OfType(string type);
void OrderBy(object selector);
void OrderByDescending(object selector);
void Select(object selector);
void SelectMany(object selector);
+ void Single();
void Single(bool predicate);
+ void SingleOrDefault();
void SingleOrDefault(bool predicate);
void Skip(int count);
void SkipWhile(bool predicate);
@@ -54,12 +62,6 @@ internal interface IEnumerableSignatures
void Where(bool predicate);
// Executors
- void First();
- void FirstOrDefault();
- void Last();
- void LastOrDefault();
- void Single();
- void SingleOrDefault();
void ToArray();
void ToList();
}
diff --git a/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/IQueryableSignatures.cs b/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/IQueryableSignatures.cs
index 97d810fb..2e188ae4 100644
--- a/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/IQueryableSignatures.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/IQueryableSignatures.cs
@@ -15,24 +15,32 @@ internal interface IQueryableSignatures
void Average(int selector);
void Average(long? selector);
void Average(long selector);
+ void Cast(string type);
void Count();
void Count(bool predicate);
void DefaultIfEmpty();
void DefaultIfEmpty(object defaultValue);
void Distinct();
+ void First();
void First(bool predicate);
+ void FirstOrDefault();
void FirstOrDefault(bool predicate);
void GroupBy(object keySelector);
void GroupBy(object keySelector, object elementSelector);
+ void Last();
void Last(bool predicate);
+ void LastOrDefault();
void LastOrDefault(bool predicate);
void Max(object selector);
void Min(object selector);
+ void OfType(string type);
void OrderBy(object selector);
void OrderByDescending(object selector);
void Select(object selector);
void SelectMany(object selector);
+ void Single();
void Single(bool predicate);
+ void SingleOrDefault();
void SingleOrDefault(bool predicate);
void Skip(int count);
void SkipWhile(bool predicate);
@@ -51,13 +59,5 @@ internal interface IQueryableSignatures
void ThenBy(object selector);
void ThenByDescending(object selector);
void Where(bool predicate);
-
- // Executors
- void First();
- void FirstOrDefault();
- void Last();
- void LastOrDefault();
- void Single();
- void SingleOrDefault();
}
}
diff --git a/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs b/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs
index 93b73604..0bb83c63 100644
--- a/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs
+++ b/src/System.Linq.Dynamic.Core/Parser/SupportedMethods/MethodFinder.cs
@@ -19,7 +19,7 @@ public MethodFinder(ParsingConfig parsingConfig)
public bool ContainsMethod(Type type, string methodName, bool staticAccess, Expression[] args)
{
- return FindMethod(type, methodName, staticAccess, args, out var _) == 1;
+ return FindMethod(type, methodName, staticAccess, args, out _) == 1;
}
public int FindMethod(Type type, string methodName, bool staticAccess, Expression[] args, out MethodBase method)
diff --git a/src/System.Linq.Dynamic.Core/Parser/TypeFinder.cs b/src/System.Linq.Dynamic.Core/Parser/TypeFinder.cs
new file mode 100644
index 00000000..77176b20
--- /dev/null
+++ b/src/System.Linq.Dynamic.Core/Parser/TypeFinder.cs
@@ -0,0 +1,93 @@
+using JetBrains.Annotations;
+using System.Linq.Dynamic.Core.Validation;
+using System.Linq.Expressions;
+
+namespace System.Linq.Dynamic.Core.Parser
+{
+ internal class TypeFinder : ITypeFinder
+ {
+ private readonly IKeywordsHelper _keywordsHelper;
+ private readonly ParsingConfig _parsingConfig;
+
+ public TypeFinder([NotNull] ParsingConfig parsingConfig, [NotNull] IKeywordsHelper keywordsHelper)
+ {
+ Check.NotNull(parsingConfig, nameof(parsingConfig));
+ Check.NotNull(keywordsHelper, nameof(keywordsHelper));
+
+ _keywordsHelper = keywordsHelper;
+ _parsingConfig = parsingConfig;
+ }
+
+ public Type FindTypeByName(string name, ParameterExpression[] expressions, bool forceUseCustomTypeProvider)
+ {
+ Check.NotEmpty(name, nameof(name));
+
+ _keywordsHelper.TryGetValue(name, out object type);
+
+ Type result = type as Type;
+ if (result != null)
+ {
+ return result;
+ }
+
+ if (expressions != null && TryResolveTypeUsingExpressions(name, expressions, out result))
+ {
+ return result;
+ }
+
+ return ResolveTypeByUsingCustomTypeProvider(name, forceUseCustomTypeProvider);
+ }
+
+ private Type ResolveTypeByUsingCustomTypeProvider(string name, bool forceUseCustomTypeProvider)
+ {
+ if ((forceUseCustomTypeProvider || _parsingConfig.AllowNewToEvaluateAnyType) && _parsingConfig.CustomTypeProvider != null)
+ {
+ Type 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);
+ }
+ }
+
+ 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.Name)
+ {
+ result = expression.Type;
+ return true;
+ }
+
+ if (name == $"{expression.Type.Namespace}.{expression.Type.Name}")
+ {
+ result = expression.Type;
+ 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;
+ }
+ }
+}
diff --git a/src/System.Linq.Dynamic.Core/ParsingConfig.cs b/src/System.Linq.Dynamic.Core/ParsingConfig.cs
index a86562a9..9807ac05 100644
--- a/src/System.Linq.Dynamic.Core/ParsingConfig.cs
+++ b/src/System.Linq.Dynamic.Core/ParsingConfig.cs
@@ -35,7 +35,7 @@ public IDynamicLinkCustomTypeProvider CustomTypeProvider
get
{
#if !(DOTNET5_1 || WINDOWS_APP || UAP10_0 || NETSTANDARD)
- // only use DefaultDynamicLinqCustomTypeProvider for full .NET Framework
+ // only use DefaultDynamicLinqCustomTypeProvider for full .NET Framework and NET Core App 2.x
return _customTypeProvider ?? (_customTypeProvider = new DefaultDynamicLinqCustomTypeProvider());
#else
return _customTypeProvider;
@@ -131,5 +131,13 @@ public IQueryableAnalyzer QueryableAnalyzer
/// where a member access on a non existing member happens. Default value is false.
///
public bool DisableMemberAccessToIndexAccessorFallback { get; set; } = false;
+
+ ///
+ /// By default finding types by a simple name is not suported.
+ /// Use this flag to use the CustomTypeProvider to resolve types by a simple name like "Employee" instead of "MyDatabase.Entities.Employee".
+ /// Note that a first matching type is returned and this functionality needs to scan all types from all assemblies, so use with caution.
+ /// Default value is false.
+ ///
+ public bool ResolveTypesBySimpleName { get; set; } = false;
}
}
diff --git a/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs b/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs
index 0122483f..4cab5065 100644
--- a/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs
+++ b/src/System.Linq.Dynamic.Core/Properties/AssemblyInfo.cs
@@ -1,33 +1,4 @@
-//using System.Reflection;
+using System.Runtime.CompilerServices;
-//// General Information about an assembly is controlled through the following
-//// set of attributes. Change these attribute values to modify the information
-//// associated with an assembly.
-//[assembly: AssemblyTitle("System.Linq.Dynamic.Core")]
-//[assembly: AssemblyDescription("")]
-//[assembly: AssemblyConfiguration("")]
-//[assembly: AssemblyCompany("")]
-//[assembly: AssemblyProduct("System.Linq.Dynamic.Core")]
-//[assembly: AssemblyCopyright("Copyright © Stef Heyenrath 2016")]
-//[assembly: AssemblyTrademark("")]
-//[assembly: AssemblyCulture("")]
-//#if !(NETSTANDARD || WINDOWS_APP)
-//[assembly: System.Resources.NeutralResourcesLanguage("en")]
-//#endif
-
-//// Version information for an assembly consists of the following four values:
-////
-//// Major Version
-//// Minor Version
-//// Build Number
-//// Revision
-////
-//// You can specify all the values or you can default the Build and Revision Numbers
-//// by using the '*' as shown below:
-//// [assembly: AssemblyVersion("1.0.*")]
-//[assembly: AssemblyVersion("1.0.0.0")]
-//[assembly: AssemblyFileVersion("1.0.0.0")]
-
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")]
\ No newline at end of file
+[assembly: InternalsVisibleTo("System.Linq.Dynamic.Core.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001003daf4f4b7d160b1033de9a4a3275f4667a4558144296c3bb593aa0fd213dadf0ea4df5aa69e21763d409ada2a8f8925081bc2e81362be7916e22c624344309eba764edc4f8f84237ae053d2687ab3b888c9f4f3ff8a804bb5fee61e1ceadec97b08994580ef2df6bd7e077df4ad205c6d2bde479c512ab9be6ecc23c10694597")]
+[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
diff --git a/src/System.Linq.Dynamic.Core/Res.cs b/src/System.Linq.Dynamic.Core/Res.cs
index cff1a949..4abc995d 100644
--- a/src/System.Linq.Dynamic.Core/Res.cs
+++ b/src/System.Linq.Dynamic.Core/Res.cs
@@ -22,6 +22,8 @@ internal static class Res
public const string ExpressionExpected = "Expression expected";
public const string ExpressionTypeMismatch = "Expression of type '{0}' expected";
public const string FirstExprMustBeBool = "The first expression must be of type 'Boolean'";
+ public const string FunctionRequiresOneArg = "The '{0}' function requires one argument";
+ public const string FunctionRequiresOneNotNullArg = "The '{0}' function requires one argument which is not null.";
public const string HexCharExpected = "Hexadecimal character expected";
public const string IQueryableProviderNotAsync = "The provider for the source IQueryable doesn't implement IAsyncQueryProvider/IDbAsyncQueryProvider. Only providers that implement IAsyncQueryProvider/IDbAsyncQueryProvider can be used for Entity Framework asynchronous operations.";
public const string IdentifierExpected = "Identifier expected";
diff --git a/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj b/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj
index a11f41e7..cc8107dc 100644
--- a/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj
+++ b/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj
@@ -1,10 +1,9 @@
- This is a .NETStandard/ .NET Core port of the the Microsoft assembly for the .Net 4.0 Dynamic language functionality.
+ This is a .NETStandard / .NET Core port of the the Microsoft assembly for the .Net 4.0 Dynamic language functionality.
System.Linq.Dynamic.Core
-
Microsoft;Scott Guthrie;King Wilder;Nathan Arnott;Stef Heyenrath
- net35;net40;net45;net46;netstandard1.3;netstandard2.0;uap10.0
+ net35;net40;net45;net46;netstandard1.3;netstandard2.0;uap10.0;netcoreapp2.1
true
System.Linq.Dynamic.Core
System.Linq.Dynamic.Core.snk
@@ -24,7 +23,7 @@
- net40;net45;net46;netstandard1.3;netstandard2.0
+ net40;net45;net46;netstandard1.3;netstandard2.0;netcoreapp2.1
@@ -58,29 +57,8 @@
1.1.1
-
-
-
diff --git a/test-uap/WindowsUniversalTestApp14393/WindowsAppCustomTypeProvider.cs b/test-uap/WindowsUniversalTestApp14393/WindowsAppCustomTypeProvider.cs
index 178bd645..bdb6b496 100644
--- a/test-uap/WindowsUniversalTestApp14393/WindowsAppCustomTypeProvider.cs
+++ b/test-uap/WindowsUniversalTestApp14393/WindowsAppCustomTypeProvider.cs
@@ -23,6 +23,12 @@ public Type ResolveType(string typeName)
return ResolveType(assemblies, typeName);
}
+ public Type ResolveTypeBySimpleName(string typeName)
+ {
+ var assemblies = GetAssemblyListAsync().Result;
+ return ResolveTypeBySimpleName(assemblies, typeName);
+ }
+
private static async Task> GetAssemblyListAsync()
{
List assemblies = new List();
diff --git a/test-uap/WindowsUniversalTestApp14393/WindowsUniversalTestApp14393.csproj b/test-uap/WindowsUniversalTestApp14393/WindowsUniversalTestApp14393.csproj
index 9a0a20a8..4aa699e5 100644
--- a/test-uap/WindowsUniversalTestApp14393/WindowsUniversalTestApp14393.csproj
+++ b/test-uap/WindowsUniversalTestApp14393/WindowsUniversalTestApp14393.csproj
@@ -143,7 +143,7 @@
- ..\..\src\System.Linq.Dynamic.Core\bin\$(Configuration)\uap10.0\System.Linq.Dynamic.Core.dll
+ ..\..\src\System.Linq.Dynamic.Core\bin\Debug\net35\System.Linq.Dynamic.Core.dll
diff --git a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
index a6928592..77359adc 100644
--- a/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs
@@ -112,6 +112,12 @@ public Type ResolveType(string typeName)
{
return Type.GetType(typeName);
}
+
+ public Type ResolveTypeBySimpleName(string typeName)
+ {
+ var assemblies = AppDomain.CurrentDomain.GetAssemblies();
+ return ResolveTypeBySimpleName(assemblies, typeName);
+ }
}
[Fact]
diff --git a/test/System.Linq.Dynamic.Core.Tests/Entities/BaseEmployee.cs b/test/System.Linq.Dynamic.Core.Tests/Entities/BaseEmployee.cs
new file mode 100644
index 00000000..05ab7218
--- /dev/null
+++ b/test/System.Linq.Dynamic.Core.Tests/Entities/BaseEmployee.cs
@@ -0,0 +1,7 @@
+namespace System.Linq.Dynamic.Core.Tests.Entities
+{
+ public class BaseEmployee
+ {
+ public string Name { get; set; }
+ }
+}
diff --git a/test/System.Linq.Dynamic.Core.Tests/Entities/Boss.cs b/test/System.Linq.Dynamic.Core.Tests/Entities/Boss.cs
new file mode 100644
index 00000000..0081eefd
--- /dev/null
+++ b/test/System.Linq.Dynamic.Core.Tests/Entities/Boss.cs
@@ -0,0 +1,7 @@
+namespace System.Linq.Dynamic.Core.Tests.Entities
+{
+ public class Boss : BaseEmployee
+ {
+ public string Function { get; set; }
+ }
+}
diff --git a/test/System.Linq.Dynamic.Core.Tests/Entities/CompanyWithBaseEmployees.cs b/test/System.Linq.Dynamic.Core.Tests/Entities/CompanyWithBaseEmployees.cs
new file mode 100644
index 00000000..85bfba64
--- /dev/null
+++ b/test/System.Linq.Dynamic.Core.Tests/Entities/CompanyWithBaseEmployees.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace System.Linq.Dynamic.Core.Tests.Entities
+{
+ public class CompanyWithBaseEmployees
+ {
+ public ICollection Employees { get; set; }
+ }
+}
diff --git a/test/System.Linq.Dynamic.Core.Tests/Entities/Worker.cs b/test/System.Linq.Dynamic.Core.Tests/Entities/Worker.cs
new file mode 100644
index 00000000..ad1c58d5
--- /dev/null
+++ b/test/System.Linq.Dynamic.Core.Tests/Entities/Worker.cs
@@ -0,0 +1,7 @@
+namespace System.Linq.Dynamic.Core.Tests.Entities
+{
+ public class Worker : BaseEmployee
+ {
+ public string Other { get; set; }
+ }
+}
diff --git a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs
index 7d592219..fa6c9345 100644
--- a/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs
@@ -572,7 +572,7 @@ public void ExpressionTests_Enum()
{
var config = new ParsingConfig();
#if NETCOREAPP
- config.CustomTypeProvider = new NetStandardCustomTypeProvider();
+ // config.CustomTypeProvider = new NetStandardCustomTypeProvider();
#endif
// Arrange
@@ -629,7 +629,7 @@ public void ExpressionTests_ConfigExtensions()
{
var config = new ParsingConfig();
#if NETCOREAPP
- config.CustomTypeProvider = new NetStandardCustomTypeProvider();
+ // config.CustomTypeProvider = new NetStandardCustomTypeProvider();
#endif
// Arrange
@@ -654,7 +654,7 @@ public void ExpressionTests_Enum_Nullable()
{
var config = new ParsingConfig();
#if NETSTANDARD
- config.CustomTypeProvider = new NetStandardCustomTypeProvider();
+ // config.CustomTypeProvider = new NetStandardCustomTypeProvider();
#endif
// Act
@@ -743,7 +743,7 @@ public void ExpressionTests_Guid_CompareTo_String()
{
var config = new ParsingConfig();
#if NETSTANDARD
- config.CustomTypeProvider = new NetStandardCustomTypeProvider();
+ // config.CustomTypeProvider = new NetStandardCustomTypeProvider();
#endif
// Arrange
@@ -867,7 +867,7 @@ public void ExpressionTests_In_Enum()
{
var config = new ParsingConfig();
#if NETSTANDARD
- config.CustomTypeProvider = new NetStandardCustomTypeProvider();
+ // config.CustomTypeProvider = new NetStandardCustomTypeProvider();
#endif
// Arrange
var model1 = new ModelWithEnum { TestEnum = TestEnum.Var1 };
@@ -1329,7 +1329,7 @@ public void ExpressionTests_Select_DynamicObjects()
Assert.Equal(new[] { 100, 200 }, result.ToDynamicArray());
}
-#if !NETCOREAPP1_1
+#if !NETCOREAPP
[Fact]
[Trait("Issue", "136")]
public void ExpressionTests_Select_ExpandoObjects()
diff --git a/test/System.Linq.Dynamic.Core.Tests/NetStandardCustomTypeProvider.cs b/test/System.Linq.Dynamic.Core.Tests/NetStandardCustomTypeProvider.cs
deleted file mode 100644
index c903e0d2..00000000
--- a/test/System.Linq.Dynamic.Core.Tests/NetStandardCustomTypeProvider.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System.Collections.Generic;
-using System.Linq.Dynamic.Core.CustomTypeProviders;
-
-namespace System.Linq.Dynamic.Core.Tests
-{
- class NetStandardCustomTypeProvider : AbstractDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider
- {
- public HashSet GetCustomTypes()
- {
- var thisType = GetType();
-
-#if NETCORE1_1
- var assemblies = AppDomain.NetCoreApp.AppDomain.CurrentDomain.GetAssemblies(thisType).Where(x => !x.IsDynamic).ToArray();
-#else
- var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToArray();
-#endif
- return new HashSet(FindTypesMarkedWithDynamicLinqTypeAttribute(assemblies));
- }
-
- public Type ResolveType(string typeName)
- {
- var thisType = GetType();
-
-#if NETCORE1_1
- var assemblies = AppDomain.NetCoreApp.AppDomain.CurrentDomain.GetAssemblies(thisType).ToArray();
-#else
- var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToArray();
-#endif
- return ResolveType(assemblies, typeName);
- }
- }
-}
diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/TypeFinderTests.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/TypeFinderTests.cs
new file mode 100644
index 00000000..904e1f9a
--- /dev/null
+++ b/test/System.Linq.Dynamic.Core.Tests/Parser/TypeFinderTests.cs
@@ -0,0 +1,77 @@
+using Moq;
+using NFluent;
+using System.Linq.Dynamic.Core.CustomTypeProviders;
+using System.Linq.Dynamic.Core.Parser;
+using System.Linq.Dynamic.Core.Tests.Entities;
+using System.Linq.Expressions;
+using Xunit;
+
+namespace System.Linq.Dynamic.Core.Tests.Parser
+{
+ public class TypeFinderTests
+ {
+ private readonly ParsingConfig _parsingConfig = new ParsingConfig();
+ private readonly Mock _keywordsHelperMock;
+ private readonly Mock _dynamicTypeProviderMock;
+
+ 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));
+
+ _parsingConfig = new ParsingConfig
+ {
+ CustomTypeProvider = _dynamicTypeProviderMock.Object
+ };
+
+ _keywordsHelperMock = new Mock();
+
+ _sut = new TypeFinder(_parsingConfig, _keywordsHelperMock.Object);
+ }
+
+ [Fact]
+ public void TypeFinder_FindTypeByName_With_SimpleTypeName_forceUseCustomTypeProvider_equals_false()
+ {
+ // Assign
+ _parsingConfig.ResolveTypesBySimpleName = true;
+
+ // Act
+ Type result = _sut.FindTypeByName("Boss", null, forceUseCustomTypeProvider: false);
+
+ // Assert
+ Check.That(result).IsNull();
+ }
+
+ [Fact]
+ public void TypeFinder_FindTypeByName_With_SimpleTypeName_forceUseCustomTypeProvider_equals_true()
+ {
+ // Assign
+ _parsingConfig.ResolveTypesBySimpleName = true;
+
+ // Act
+ Type result = _sut.FindTypeByName("Boss", null, forceUseCustomTypeProvider: true);
+
+ // Assert
+ Check.That(result).Equals(typeof(Boss));
+ }
+
+ [Fact]
+ public void TypeFinder_FindTypeByName_With_SimpleTypeName_basedon_it()
+ {
+ // Assign
+ _parsingConfig.ResolveTypesBySimpleName = true;
+ var expressions = new[] { Expression.Parameter(typeof(BaseEmployee)) };
+
+ // Act
+ Type result = _sut.FindTypeByName("Boss", expressions, forceUseCustomTypeProvider: false);
+
+ // Assert
+ Check.That(result).Equals(typeof(Boss));
+ }
+ }
+}
diff --git a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Is,OfType,As,Cast.cs b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Is,OfType,As,Cast.cs
new file mode 100644
index 00000000..be0718e3
--- /dev/null
+++ b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Is,OfType,As,Cast.cs
@@ -0,0 +1,269 @@
+using NFluent;
+using System.Linq.Dynamic.Core.Exceptions;
+using System.Linq.Dynamic.Core.Tests.Entities;
+using Xunit;
+
+namespace System.Linq.Dynamic.Core.Tests
+{
+ public partial class QueryableTests
+ {
+ [Fact]
+ public void OfType_WithType()
+ {
+ // Assign
+ var qry = new BaseEmployee[]
+ {
+ new Worker { Name = "e" }, new Boss { Name = "e" }
+ }.AsQueryable();
+
+ // Act
+ var oftype = qry.OfType().ToArray();
+ var oftypeDynamic = qry.OfType(typeof(Worker)).ToDynamicArray();
+
+ // Assert
+ Check.That(oftypeDynamic.Length).Equals(oftype.Length);
+ }
+
+ [Fact]
+ public void OfType_WithString()
+ {
+ // Assign
+ var qry = new BaseEmployee[]
+ {
+ new Worker { Name = "e" }, new Boss { Name = "e" }
+ }.AsQueryable();
+
+ // Act
+ var oftype = qry.OfType().ToArray();
+ var oftypeDynamic = qry.OfType(typeof(Worker).FullName).ToDynamicArray();
+
+ // Assert
+ Check.That(oftypeDynamic.Length).Equals(oftype.Length);
+ }
+
+ [Fact]
+ public void OfType_Dynamic()
+ {
+ // Assign
+ var qry = new[]
+ {
+ new CompanyWithBaseEmployees
+ {
+ Employees = new BaseEmployee[]
+ {
+ new Worker { Name = "e" }, new Boss { Name = "e" }
+ }
+ }
+ }.AsQueryable();
+
+ // Act
+ var oftype = qry.Select(c => c.Employees.OfType().Where(e => e.Name == "e")).ToArray();
+ var oftypeDynamic = qry.Select("Employees.OfType(\"System.Linq.Dynamic.Core.Tests.Entities.Worker\").Where(Name == \"e\")").ToDynamicArray();
+
+ // Assert
+ Check.That(oftypeDynamic.Length).Equals(oftype.Length);
+ }
+
+ [Fact]
+ public void Is_Dynamic_ActingOnIt()
+ {
+ // Assign
+ var qry = new BaseEmployee[]
+ {
+ new Worker { Name = "1" }, new Boss { Name = "b" }
+ }.AsQueryable();
+
+ // Act
+ int countOfType = qry.Count(c => c is Worker);
+ int countOfTypeDynamic = qry.Count("is(\"System.Linq.Dynamic.Core.Tests.Entities.Worker\")");
+
+ // Assert
+ Check.That(countOfTypeDynamic).Equals(countOfType);
+ }
+
+ [Fact]
+ public void Is_Dynamic_ActingOnIt_WithSimpleName()
+ {
+ // Assign
+ var config = new ParsingConfig
+ {
+ ResolveTypesBySimpleName = true
+ };
+
+ var qry = new BaseEmployee[]
+ {
+ new Worker { Name = "1" }, new Boss { Name = "b" }
+ }.AsQueryable();
+
+ // Act
+ int countOfType = qry.Count(c => c is Worker);
+ int countOfTypeDynamic = qry.Count(config, "is(\"Worker\")");
+
+ // Assert
+ Check.That(countOfTypeDynamic).Equals(countOfType);
+ }
+
+ [Fact]
+ public void As_Dynamic_ActingOnIt()
+ {
+ // Assign
+ var qry = new BaseEmployee[]
+ {
+ new Worker { Name = "1" }, new Boss { Name = "b" }
+ }.AsQueryable();
+
+ // Act
+ int countAsDynamic = qry.Count("As(\"System.Linq.Dynamic.Core.Tests.Entities.Worker\") != null");
+
+ // Assert
+ Check.That(countAsDynamic).Equals(1);
+ }
+
+ [Fact]
+ public void CastToType_WithType()
+ {
+ // Assign
+ var qry = new BaseEmployee[]
+ {
+ new Worker { Name = "1" }, new Worker { Name = "2" }
+ }.AsQueryable();
+
+ // Act
+ var cast = qry.Cast().ToArray();
+ var castDynamic = qry.Cast(typeof(Worker)).ToDynamicArray();
+
+ // Assert
+ Check.That(castDynamic.Length).Equals(cast.Length);
+ }
+
+ [Fact]
+ public void CastToType_WithString()
+ {
+ // Assign
+ var qry = new BaseEmployee[]
+ {
+ new Worker { Name = "1" }, new Worker { Name = "2" }
+ }.AsQueryable();
+
+ // Act
+ var cast = qry.Cast().ToArray();
+ var castDynamic = qry.Cast(typeof(Worker).FullName).ToDynamicArray();
+
+ // Assert
+ Check.That(castDynamic.Length).Equals(cast.Length);
+ }
+
+ [Fact]
+ public void CastToType_Dynamic()
+ {
+ // Assign
+ var qry = new[]
+ {
+ new CompanyWithBaseEmployees
+ {
+ Employees = new BaseEmployee[]
+ {
+ new Worker { Name = "e" }
+ }
+ }
+ }.AsQueryable();
+
+ // Act
+ var cast = qry.Select(c => c.Employees.Cast().Where(e => e.Name == "e")).ToArray();
+ var castDynamic = qry.Select("Employees.Cast(\"System.Linq.Dynamic.Core.Tests.Entities.Worker\").Where(Name == \"e\")").ToDynamicArray();
+
+ // Assert
+ Check.That(cast.Length).Equals(castDynamic.Length);
+ }
+
+ [Fact]
+ public void CastToType_Dynamic_ActingOnIt()
+ {
+ // Assign
+ var qry = new BaseEmployee[]
+ {
+ new Worker { Name = "1" }, new Worker { Name = "2" }
+ }.AsQueryable();
+
+ // Act
+ var cast = qry.Select(c => (Worker)c).ToArray();
+ var castDynamic = qry.Select("Cast(\"System.Linq.Dynamic.Core.Tests.Entities.Worker\")").ToDynamicArray();
+
+ // Assert
+ Check.That(cast.Length).Equals(castDynamic.Length);
+ }
+
+ [Fact]
+ public void CastToType_Dynamic_ActingOnIt_Throws()
+ {
+ // Assign
+ var qry = new BaseEmployee[]
+ {
+ new Worker { Name = "1" }, new Boss { Name = "b" }
+ }.AsQueryable();
+
+ // Act
+ Action castDynamic = () => qry.Select("Cast(\"System.Linq.Dynamic.Core.Tests.Entities.Worker\")").ToDynamicArray();
+
+ // Assert
+ Check.ThatCode(castDynamic).Throws();
+ }
+
+ [Fact]
+ public void OfType_Dynamic_Exceptions()
+ {
+ // Assign
+ var qry = new[]
+ {
+ new CompanyWithBaseEmployees
+ {
+ Employees = new BaseEmployee[]
+ {
+ new Worker { Name = "e" }, new Boss { Name = "e" }
+ }
+ }
+ }.AsQueryable();
+
+ // Act
+ Assert.Throws(() => qry.Select("Employees.OfType().Where(Name == \"e\")"));
+ Assert.Throws(() => qry.Select("Employees.OfType(true).Where(Name == \"e\")"));
+ Assert.Throws(() => qry.Select("Employees.OfType(\"not-found\").Where(Name == \"e\")"));
+ }
+
+ [Fact]
+ public void OfType_Dynamic_ActingOnIt_Exceptions()
+ {
+ // Assign
+ var qry = new BaseEmployee[]
+ {
+ new Worker { Name = "1" }, new Boss { Name = "b" }
+ }.AsQueryable();
+
+ // Act
+ Assert.Throws(() => qry.Count("OfType()"));
+ Assert.Throws(() => qry.Count("OfType(true)"));
+ Assert.Throws(() => qry.Count("OfType(\"not-found\")"));
+ }
+
+ [Fact]
+ public void CastToType_Dynamic_Exceptions()
+ {
+ // Assign
+ var qry = new[]
+ {
+ new CompanyWithBaseEmployees
+ {
+ Employees = new BaseEmployee[]
+ {
+ new Worker { Name = "1" }, new Worker { Name = "2" }
+ }
+ }
+ }.AsQueryable();
+
+ // Act
+ Assert.Throws(() => qry.Select("Employees.Cast().Where(Name == \"1\")"));
+ Assert.Throws(() => qry.Select("Employees.Cast(true).Where(Name == \"1\")"));
+ Assert.Throws(() => qry.Select("Employees.Cast(\"not-found\").Where(Name == \"1\")"));
+ }
+ }
+}
diff --git a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs
index 7785d83b..c18672dc 100644
--- a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs
+++ b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs
@@ -270,7 +270,7 @@ public void Select_Dynamic_IntoKnownNestedType()
{
var config = new ParsingConfig { AllowNewToEvaluateAnyType = true };
#if NETCOREAPP
- config.CustomTypeProvider = new NetStandardCustomTypeProvider();
+ // config.CustomTypeProvider = new NetStandardCustomTypeProvider();
#endif
// Assign
var queryable = new List() { "name1", "name2" }.AsQueryable();
@@ -288,7 +288,7 @@ public void Select_Dynamic_IntoKnownNestedTypeSecondLevel()
{
var config = new ParsingConfig { AllowNewToEvaluateAnyType = true };
#if NETCOREAPP
- config.CustomTypeProvider = new NetStandardCustomTypeProvider();
+ // config.CustomTypeProvider = new NetStandardCustomTypeProvider();
#endif
// Assign