diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs index 0a32c0da..7a487fe8 100644 --- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs +++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs @@ -697,7 +697,7 @@ public static IQueryable GroupJoin([NotNull] this IQueryable outer, [CanBeNull] typeof(Queryable), nameof(Queryable.GroupJoin), new[] { outer.ElementType, innerType, outerSelectorLambda.Body.Type, resultSelectorLambda.Body.Type }, outer.Expression, - Expression.Constant(inner), + inner.AsQueryable().Expression, Expression.Quote(outerSelectorLambda), Expression.Quote(innerSelectorLambda), Expression.Quote(resultSelectorLambda))); diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index 591fac4f..2b22e203 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -1549,6 +1549,10 @@ Type FindType(string name) { return _root.Type; } + if (this._parsingConfig.AllowNewToEvaluateAnyType && Type.GetType(name) != null) + { + return Type.GetType(name); + } return null; } diff --git a/src/System.Linq.Dynamic.Core/ParsingConfig.cs b/src/System.Linq.Dynamic.Core/ParsingConfig.cs index cc0414a5..59bae863 100644 --- a/src/System.Linq.Dynamic.Core/ParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core/ParsingConfig.cs @@ -1,4 +1,5 @@ using System.Linq.Dynamic.Core.CustomTypeProviders; +using System.Reflection; namespace System.Linq.Dynamic.Core { @@ -7,6 +8,26 @@ namespace System.Linq.Dynamic.Core /// public class ParsingConfig { + /// + /// Get an instance of ParsingConfig + /// + public ParsingConfig() + { +#if !(DOTNET5_1 || WINDOWS_APP || UAP10_0 || NETSTANDARD) + AppDomain.CurrentDomain.TypeResolve += this.CurrentDomain_TypeResolve; +#endif + } + + /// + /// Cleanup + /// + ~ParsingConfig() + { +#if !(DOTNET5_1 || WINDOWS_APP || UAP10_0 || NETSTANDARD) + AppDomain.CurrentDomain.TypeResolve -= this.CurrentDomain_TypeResolve; +#endif + } + /// /// Default ParsingConfig /// @@ -62,5 +83,35 @@ public IDynamicLinkCustomTypeProvider CustomTypeProvider /// See https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.1#linq-groupby-translation /// public bool EvaluateGroupByAtDatabase { get; set; } + + /// + /// Allows the New() keyword to evaluate any available Type + /// + public bool AllowNewToEvaluateAnyType { get; set; } = false; + +#if !(DOTNET5_1 || WINDOWS_APP || UAP10_0 || NETSTANDARD) + /// + /// Custom type resolver to allow ExpressionParser to find any type + /// registered in the current application domain. You can add additional + /// type resolvers in your application. + /// + /// + /// + /// + protected Assembly CurrentDomain_TypeResolve(object sender, ResolveEventArgs args) + { + foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + Type t = assembly.GetType(args.Name, false, true); + + if (t != null) + { + return assembly; + } + } + + return null; + } +#endif } } diff --git a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs index 41210139..32163f8a 100644 --- a/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs +++ b/test/System.Linq.Dynamic.Core.Tests/QueryableTests.Select.cs @@ -169,6 +169,16 @@ public class Example public DayOfWeek DOW { get; set; } public int Sec { get; set; } public int? SecNull { get; set; } + + public class NestedDto + { + public string Name { get; set; } + + public class NestedDto2 + { + public string Name2 { get; set; } + } + } } public class ExampleWithConstructor @@ -227,6 +237,47 @@ public void Select_Dynamic_IntoTypeWithNullableProperties2() Check.That(resultDynamic.Last()).Equals(result.Last()); } + [Fact] + public void Select_Dynamic_IntoKnownNestedType() + { + +#if (DOTNET5_1 || WINDOWS_APP || UAP10_0 || NETSTANDARD) + return; +#endif + var config = new ParsingConfig(); + config.AllowNewToEvaluateAnyType = true; + + // Act + IQueryable data = new List() { "name1", "name2" }.AsQueryable(); + IQueryable projectedData = + (IQueryable) data.Select(config, $"new {typeof(Example.NestedDto).FullName}(~ as Name)"); + + // Assert + Check.That(projectedData.First().Name).Equals("name1"); + Check.That(projectedData.Last().Name).Equals("name2"); + } + + [Fact] + public void Select_Dynamic_IntoKnownNestedTypeSecondLevel() + { + +#if (DOTNET5_1 || WINDOWS_APP || UAP10_0 || NETSTANDARD) + return; +#endif + + var config = new ParsingConfig(); + config.AllowNewToEvaluateAnyType = true; + + // Act + IQueryable data = new List() { "name1", "name2" }.AsQueryable(); + IQueryable projectedData = + (IQueryable)data.Select(config, $"new {typeof(Example.NestedDto.NestedDto2).FullName}(~ as Name2)"); + + // Assert + Check.That(projectedData.First().Name2).Equals("name1"); + Check.That(projectedData.Last().Name2).Equals("name2"); + } + [Fact] public void Select_Dynamic_Exceptions() {