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 { 7, 8, 9 }.ToDynamicList(typeof(int)) + CustomTypeProvider = new TestCustomTypeProvider() }; - Console.WriteLine("all {0}", JsonConvert.SerializeObject(all, Formatting.Indented)); - var anyTest = new[] - { - new { id = "1", values =new [] { 1, 2, 3 } }, - new { id = "2", values =new [] { 1, 4 } }, - new { id = "3", values =new [] { 9, 5 } } - }.AsQueryable(); + //// 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 any1 = anyTest.Where(x => x.values.Contains(1)); - Console.WriteLine("any1 {0}", JsonConvert.SerializeObject(any1, Formatting.Indented)); + //var all = new + //{ + // test1 = new List { 1, 2, 3 }.ToDynamicList(typeof(int)), + // test2 = new List { 4, 5, 6 }.ToDynamicList(typeof(int)), + // test3 = new List { 7, 8, 9 }.ToDynamicList(typeof(int)) + //}; + //Console.WriteLine("all {0}", JsonConvert.SerializeObject(all, Formatting.Indented)); - var any2 = anyTest.Where("values.Contains(1)"); - Console.WriteLine("any2 {0}", JsonConvert.SerializeObject(any2, Formatting.Indented)); + //var anyTest = new[] + //{ + // new { id = "1", values =new [] { 1, 2, 3 } }, + // new { id = "2", values =new [] { 1, 4 } }, + // new { id = "3", values =new [] { 9, 5 } } + //}.AsQueryable(); + //var any1 = anyTest.Where(x => x.values.Contains(1)); + //Console.WriteLine("any1 {0}", JsonConvert.SerializeObject(any1, Formatting.Indented)); + //var any2 = anyTest.Where("values.Contains(1)"); + //Console.WriteLine("any2 {0}", JsonConvert.SerializeObject(any2, Formatting.Indented)); - var dateLastModified = new DateTime(2018, 1, 15); + DateTime dateLastModified = new DateTime(2018, 1, 15); var context = new TestContext(); context.Cars.Add(new Car { Brand = "Ford", Color = "Blue", Vin = "yes", Year = "2017", DateLastModified = dateLastModified }); @@ -120,19 +112,48 @@ static void Main(string[] args) context.Brands.Add(new Brand { BrandType = "Alfa", BrandName = "Romeo" }); context.SaveChanges(); - context.BaseDtos.Add(new TestDto { BaseName = "b", Name = "t" }); - context.BaseDtos.Add(new OtherTestDto { BaseName = "b", Name = "t" }); + var testDto1 = new TestDto { BaseName = "a", Name = "t" }; + context.BaseDtos.Add(testDto1); + var testDto2 = new TestDto { BaseName = "b", Name = "t" }; + context.BaseDtos.Add(testDto2); + + var otherTestDto = new OtherTestDto { BaseName = "c", Name = "t" }; + context.BaseDtos.Add(otherTestDto); + context.SaveChanges(); + + context.ComplexDtos.Add(new ComplexDto { X = "both", ListOfBaseDtos = new BaseDto[] { testDto1, otherTestDto } }); + context.ComplexDtos.Add(new ComplexDto { X = "testDto", ListOfBaseDtos = new BaseDto[] { testDto2 } }); context.SaveChanges(); - var oftypeTestDto1 = context.BaseDtos.OfType().Where(x => x.Name == "t").ToArray(); - var oftypeTestDto2 = context.BaseDtos.OfType().Where("Name == \"t\"").ToArray(); + OfTypeAndCastTests(context, config); var carDateLastModified = context.Cars.Where(config, "DateLastModified > \"2018-01-16\""); Console.WriteLine("carDateLastModified {0}", JsonConvert.SerializeObject(carDateLastModified, Formatting.Indented)); - //var carFirstOrDefault = context.Cars.Where(config, "Brand == \"Ford\""); - //Console.WriteLine("carFirstOrDefault {0}", JsonConvert.SerializeObject(carFirstOrDefault, Formatting.Indented)); + var carFirstOrDefault = context.Cars.Where(config, "Brand == \"Ford\""); + Console.WriteLine("carFirstOrDefault {0}", JsonConvert.SerializeObject(carFirstOrDefault, Formatting.Indented)); + + LikeTests(context, config); + + var testDynamic = context.Cars.Select(c => new + { + K = c.Key, + C = c.Color + }); + + var testDynamicResult = testDynamic.Select("it").OrderBy("C"); + try + { + Console.WriteLine("resultX {0}", JsonConvert.SerializeObject(testDynamicResult, Formatting.Indented)); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + private static void LikeTests(TestContext context, ParsingConfig config) + { //var carsLike1 = // from c in context.Cars // where EF.Functions.Like(c.Brand, "%a%") @@ -142,33 +163,66 @@ static void Main(string[] args) //var cars2Like = context.Cars.Where(c => EF.Functions.Like(c.Brand, "%a%")); //Console.WriteLine("cars2Like {0}", JsonConvert.SerializeObject(cars2Like, Formatting.Indented)); - //var dynamicCarsLike1 = context.Cars.Where(config, "TestContext.Like(Brand, \"%a%\")"); - //Console.WriteLine("dynamicCarsLike1 {0}", JsonConvert.SerializeObject(dynamicCarsLike1, Formatting.Indented)); + var dynamicCarsLike1 = context.Cars.Where(config, "TestContext.Like(Brand, \"%a%\")"); + Console.WriteLine("dynamicCarsLike1 {0}", JsonConvert.SerializeObject(dynamicCarsLike1, Formatting.Indented)); - //var dynamicCarsLike2 = context.Cars.Where(config, "TestContext.Like(Brand, \"%d%\")"); - //Console.WriteLine("dynamicCarsLike2 {0}", JsonConvert.SerializeObject(dynamicCarsLike2, Formatting.Indented)); + var dynamicCarsLike2 = context.Cars.Where(config, "TestContext.Like(Brand, \"%d%\")"); + Console.WriteLine("dynamicCarsLike2 {0}", JsonConvert.SerializeObject(dynamicCarsLike2, Formatting.Indented)); //var dynamicFunctionsLike1 = context.Cars.Where(config, "DynamicFunctions.Like(Brand, \"%a%\")"); - //Console.WriteLine("dynamicFunctionsLike1 {0}", JsonConvert.SerializeObject(dynamicFunctionsLike1, Formatting.Indented)); + //Console.WriteLine("dynamicFunctionsLike1 {0}", + //JsonConvert.SerializeObject(dynamicFunctionsLike1, Formatting.Indented)); //var dynamicFunctionsLike2 = context.Cars.Where(config, "DynamicFunctions.Like(Vin, \"%a.%b%\", \".\")"); - //Console.WriteLine("dynamicFunctionsLike2 {0}", JsonConvert.SerializeObject(dynamicFunctionsLike2, Formatting.Indented)); + //Console.WriteLine("dynamicFunctionsLike2 {0}", + //JsonConvert.SerializeObject(dynamicFunctionsLike2, Formatting.Indented)); + } - //var testDynamic = context.Cars.Select(c => new - //{ - // K = c.Key, - // C = c.Color - //}); + private static void OfTypeAndCastTests(TestContext context, ParsingConfig config) + { + var cast = context.BaseDtos.Where(b => b is TestDto).Cast().ToArray(); + var castDynamicWithType = context.BaseDtos.Where(b => b is TestDto).Cast(typeof(TestDto)).ToDynamicArray(); + var castDynamicWithString = context.BaseDtos.Where(b => b is TestDto).Cast(config, "ConsoleAppEF2.Database.TestDto").ToDynamicArray(); - //var testDynamicResult = testDynamic.Select("it").OrderBy("C"); - //try - //{ - // Console.WriteLine("resultX {0}", JsonConvert.SerializeObject(testDynamicResult, Formatting.Indented)); - //} - //catch (Exception e) - //{ - // Console.WriteLine(e); - //} + var oftype = context.BaseDtos.OfType().ToArray(); + bool ofTypeAny = context.BaseDtos.OfType().Any(); + 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, "is(\"ConsoleAppEF2.Database.TestDto\")"); + int isOfTypeDynamicOtherTestDto = context.BaseDtos.Count(config, "is(\"ConsoleAppEF2.Database.OtherTestDto\")"); + int isOfTypeDynamicComplexDto = context.BaseDtos.Count(config, "is(\"ConsoleAppEF2.Database.ComplexDto\")"); + + var asOfType = context.BaseDtos.Where(b => b as TestDto != null).ToArray(); + var asOfTypeDynamicTestDto = context.BaseDtos.Where(config, "As(\"ConsoleAppEF2.Database.TestDto\") != null").ToDynamicArray(); + var asOfTypeDynamicOtherTestDto = context.BaseDtos.Where(config, "As(\"ConsoleAppEF2.Database.OtherTestDto\") != null").ToDynamicArray(); + var asOfTypeDynamicComplexDto = context.BaseDtos.Where(config, "As(\"ConsoleAppEF2.Database.ComplexDto\") != null").ToDynamicArray(); + + var castOnX = context.BaseDtos.Where(b => b as TestDto != null).Where(b => ((TestDto)b).Name != null).ToArray(); + var castOnXDynamic = context.BaseDtos.Where(b => b as TestDto != null).Where(config, "Cast(\"ConsoleAppEF2.Database.TestDto\").Name != null").ToArray(); + + var oftypeTestDto = context.BaseDtos.OfType().Where(x => x.Name == "t").ToArray(); + var oftypeTestDtoDynamic = context.BaseDtos.OfType().Where("Name == \"t\"").ToArray(); + + var complexOfType = context.ComplexDtos.Select(c => c.ListOfBaseDtos.OfType().Where(x => x.Name == "t")) + .ToArray(); + var complexOfTypeDynamic = context.ComplexDtos + .Select(config, "ListOfBaseDtos.OfType(\"ConsoleAppEF2.Database.TestDto\").Where(Name == \"t\")") + .ToDynamicArray(); + + var complexCast = context.ComplexDtos.Where(c => c.X == "testDto").ToList() + .Select(c => c.ListOfBaseDtos.Cast().Where(x => x.Name == "t")) + .ToArray(); + var complexCastDynamic = context.ComplexDtos.Where(c => c.X == "testDto").ToList().AsQueryable() + .Select(config, "ListOfBaseDtos.Cast(\"ConsoleAppEF2.Database.TestDto\").Where(Name == \"t\")") + .ToDynamicArray(); } } } diff --git a/src/Microsoft.EntityFrameworkCore.DynamicLinq/Microsoft.EntityFrameworkCore.DynamicLinq.csproj b/src/Microsoft.EntityFrameworkCore.DynamicLinq/Microsoft.EntityFrameworkCore.DynamicLinq.csproj index ff946bbe..64a47868 100644 --- a/src/Microsoft.EntityFrameworkCore.DynamicLinq/Microsoft.EntityFrameworkCore.DynamicLinq.csproj +++ b/src/Microsoft.EntityFrameworkCore.DynamicLinq/Microsoft.EntityFrameworkCore.DynamicLinq.csproj @@ -4,7 +4,7 @@ Microsoft.EntityFrameworkCore.DynamicLinq Stef Heyenrath - net451;net46;netstandard1.3;netstandard2.0;uap10.0 + net451;net46;netstandard1.3;netstandard2.0;uap10.0;netcoreapp2.1 $(DefineConstants);EFCORE true Microsoft.EntityFrameworkCore.DynamicLinq @@ -17,7 +17,6 @@ https://raw.githubusercontent.com/StefH/System.Linq.Dynamic.Core/master/LICENSE git https://github.com/StefH/System.Linq.Dynamic.Core - $(PackageTargetFallback);dotnet5.4;portable-win81+wp81;portable-net45+wp8;portable-net45+win8+wp8;portable-wp81+wpa81;portable-win81+wp81+wpa81;portable-net45+win8+wpa81+wp8;portable-net45+win8;portable-net45+win8+wpa81;portable-win81+wpa81;portable-net451+win81;portable-net451+win81+wpa81 false false false @@ -33,7 +32,7 @@ - net451;net46;netstandard1.3;netstandard2.0 + net451;net46;netstandard1.3;netstandard2.0;netcoreapp2.1 @@ -75,32 +74,22 @@ - - - - - @@ -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