Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src-console/ConsoleAppEF2.0.2_InMemory/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
6 changes: 6 additions & 0 deletions src-console/ConsoleAppEF2.0/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
6 changes: 6 additions & 0 deletions src-console/ConsoleAppEF2.1.1/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
6 changes: 6 additions & 0 deletions src-console/ConsoleAppEF2.1.1_InMemory/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,12 @@ private static void OfTypeAndCastTests(TestContext context, ParsingConfig config
var oftypeDynamicWithType = context.BaseDtos.OfType(typeof(TestDto)).ToDynamicArray();
var oftypeDynamicWithString = context.BaseDtos.OfType(config, "ConsoleAppEF2.Database.TestDto").ToDynamicArray();

var configX = new ParsingConfig
{
ResolveTypesBySimpleName = true
};
var oftypeDynamicWithSimpleNameString = context.BaseDtos.OfType(configX, "TestDto").ToDynamicArray();

int isOfType = context.BaseDtos.Count(b => b is TestDto);
int isOfTypeDynamicTestDto = context.BaseDtos.Count(config, "OfType(\"ConsoleAppEF2.Database.TestDto\")");
int isOfTypeDynamicOtherTestDto = context.BaseDtos.Count(config, "OfType(\"ConsoleAppEF2.Database.OtherTestDto\")");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,35 @@ protected Type ResolveType([NotNull] IEnumerable<Assembly> assemblies, [NotNull]
return null;
}

/// <summary>
/// Resolve a type by the simple name which is registered in the current application domain.
/// </summary>
/// <param name="assemblies">The assemblies to inspect.</param>
/// <param name="simpleTypeName">The simple typename to resolve.</param>
/// <returns>A resolved <see cref="Type"/> or null when not found.</returns>
protected Type ResolveTypeBySimpleName([NotNull] IEnumerable<Assembly> 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)
/// <summary>
/// Gets the assembly types annotated with <see cref="DynamicLinqTypeAttribute"/> in an Exception friendly way.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ public Type ResolveType(string typeName)
return ResolveType(assemblies, typeName);
}

/// <inheritdoc cref="IDynamicLinkCustomTypeProvider.ResolveTypeBySimpleName"/>
public Type ResolveTypeBySimpleName(string simpleTypeName)
{
Check.NotEmpty(simpleTypeName, nameof(simpleTypeName));

IEnumerable<Assembly> assemblies = _assemblyHelper.GetAssemblies();
return ResolveTypeBySimpleName(assemblies, simpleTypeName);
}

private HashSet<Type> GetCustomTypesInternal()
{
IEnumerable<Assembly> assemblies = _assemblyHelper.GetAssemblies();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,17 @@ public interface IDynamicLinkCustomTypeProvider
HashSet<Type> GetCustomTypes();

/// <summary>
/// Resolve any type which is registered in the current application domain.
/// Resolve any type by fullname which is registered in the current application domain.
/// </summary>
/// <param name="typeName">The typename to resolve.</param>
/// <returns>A resolved <see cref="Type"/> or null when not found.</returns>
Type ResolveType([NotNull] string typeName);

/// <summary>
/// Resolve any type by the simple name which is registered in the current application domain.
/// </summary>
/// <param name="simpleTypeName">The typename to resolve.</param>
/// <returns>A resolved <see cref="Type"/> or null when not found.</returns>
Type ResolveTypeBySimpleName([NotNull] string simpleTypeName);
}
}
21 changes: 7 additions & 14 deletions src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,10 @@ public static IQueryable Cast([NotNull] this IQueryable source, [NotNull] Parsin
Check.NotNull(config, nameof(config));
Check.NotEmpty(typeName, nameof(typeName));

config.AllowNewToEvaluateAnyType = true;
var newExpression = DynamicExpressionParser.ParseLambda(config, null, $"new {typeName}()");
var finder = new TypeFinder(config, new KeywordsHelper(config));
Type type = finder.FindTypeByName(typeName, null, true);

return Cast(source, newExpression.Body.Type);
return Cast(source, type);
}

/// <summary>
Expand Down Expand Up @@ -1025,10 +1025,10 @@ public static IQueryable OfType([NotNull] this IQueryable source, [NotNull] Pars
Check.NotNull(config, nameof(config));
Check.NotEmpty(typeName, nameof(typeName));

config.AllowNewToEvaluateAnyType = true;
var newExpression = DynamicExpressionParser.ParseLambda(config, null, $"new {typeName}()");
var finder = new TypeFinder(config, new KeywordsHelper(config));
Type type = finder.FindTypeByName(typeName, null, true);

return OfType(source, newExpression.Body.Type);
return OfType(source, type);
}

/// <summary>
Expand Down Expand Up @@ -2156,14 +2156,7 @@ private static TResult Execute<TResult>(MethodInfo operatorMethodInfo, IQueryabl

private static MethodInfo GetGenericMethod(string name)
{
try
{
return typeof(Queryable).GetTypeInfo().GetDeclaredMethods(name).Single(mi => mi.IsGenericMethod);
}
catch (Exception ex)
{
throw new Exception("Method not found: " + name, ex);
}
return typeof(Queryable).GetTypeInfo().GetDeclaredMethods(name).Single(mi => mi.IsGenericMethod);
}

private static MethodInfo GetMethod(string name, int parameterCount = 0, Func<MethodInfo, bool> predicate = null)
Expand Down
56 changes: 5 additions & 51 deletions src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, object> _internals;
private readonly Dictionary<string, object> _symbols;

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1242,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);
Expand Down Expand Up @@ -1654,54 +1656,6 @@ Expression ParseMemberAccess(Type type, Expression instance)
throw ParseError(errorPos, Res.UnknownPropertyOrField, id, TypeHelper.GetTypeName(type));
}

Type FindType(string name)
{
_keywordsHelper.TryGetValue(name, out object type);

Type result = type as Type;
if (result != null)
{
return result;
}

if (_it != null && _it.Type.Name == name)
{
return _it.Type;
}

if (_parent != null && _parent.Type.Name == name)
{
return _parent.Type;
}

if (_root != null && _root.Type.Name == name)
{
return _root.Type;
}

if (_it != null && _it.Type.Namespace + "." + _it.Type.Name == name)
{
return _it.Type;
}

if (_parent != null && _parent.Type.Namespace + "." + _parent.Type.Name == name)
{
return _parent.Type;
}

if (_root != null && _root.Type.Namespace + "." + _root.Type.Name == name)
{
return _root.Type;
}

if (_parsingConfig.AllowNewToEvaluateAnyType && _parsingConfig.CustomTypeProvider != null)
{
return _parsingConfig.CustomTypeProvider.ResolveType(name);
}

return null;
}

Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos, bool isQueryable)
{
var oldParent = _parent;
Expand Down Expand Up @@ -1806,7 +1760,7 @@ private Type ResolveTypeFromArgumentExpression(string functionName, Expression a
throw ParseError(_textParser.CurrentToken.Pos, Res.FunctionRequiresOneNotNullArg, functionName, typeName);
}

Type resultType = FindType(typeName);
Type resultType = _typeFinder.FindTypeByName(typeName, new[] { _it, _parent, _root }, true);
if (resultType == null)
{
throw ParseError(_textParser.CurrentToken.Pos, Res.TypeNotFound, typeName);
Expand Down
7 changes: 7 additions & 0 deletions src/System.Linq.Dynamic.Core/Parser/IKeywordsHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace System.Linq.Dynamic.Core.Parser
{
interface IKeywordsHelper
{
bool TryGetValue(string name, out object type);
}
}
10 changes: 10 additions & 0 deletions src/System.Linq.Dynamic.Core/Parser/ITypeFinder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Linq.Expressions;
using JetBrains.Annotations;

namespace System.Linq.Dynamic.Core.Parser
{
interface ITypeFinder
{
Type FindTypeByName([NotNull] string name, [CanBeNull] ParameterExpression[] expressions, bool forceUseCustomTypeProvider);
}
}
2 changes: 1 addition & 1 deletion src/System.Linq.Dynamic.Core/Parser/KeywordsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace System.Linq.Dynamic.Core.Parser
{
internal class KeywordsHelper
internal class KeywordsHelper : IKeywordsHelper
{
public const string SYMBOL_IT = "$";
public const string SYMBOL_PARENT = "^";
Expand Down
93 changes: 93 additions & 0 deletions src/System.Linq.Dynamic.Core/Parser/TypeFinder.cs
Original file line number Diff line number Diff line change
@@ -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 (expression.Type.Name == name)
{
result = expression.Type;
return true;
}

if ($"{expression.Type.Namespace}.{expression.Type.Name}" == 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;
}
}
}
8 changes: 8 additions & 0 deletions src/System.Linq.Dynamic.Core/ParsingConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,13 @@ public IQueryableAnalyzer QueryableAnalyzer
/// where a member access on a non existing member happens. Default value is false.
/// </summary>
public bool DisableMemberAccessToIndexAccessorFallback { get; set; } = false;

/// <summary>
/// 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.
/// </summary>
public bool ResolveTypesBySimpleName { get; set; } = false;
}
}
Loading