From 1396110a61bf80f1e45788959697425e23becbed Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 27 Jun 2018 22:02:53 +0200 Subject: [PATCH 1/4] EvaluateGroupByAtDatabase (#165) --- README.md | 2 +- System.Linq.Dynamic.Core.sln | 19 +++ .../ConsoleApp_netcore2.0_EF2.1.csproj | 21 +++ src-console/ConsoleAppEF2.1/Database/Car.cs | 23 ++++ .../ConsoleAppEF2.1/Database/TestContext.cs | 29 ++++ src-console/ConsoleAppEF2.1/Program.cs | 126 ++++++++++++++++++ .../EntityFramework.DynamicLinq.csproj | 4 +- ...oft.EntityFrameworkCore.DynamicLinq.csproj | 4 +- .../DynamicQueryableExtensions.cs | 9 +- src/System.Linq.Dynamic.Core/ParsingConfig.cs | 9 +- .../System.Linq.Dynamic.Core.csproj | 4 +- 11 files changed, 235 insertions(+), 15 deletions(-) create mode 100644 src-console/ConsoleAppEF2.1/ConsoleApp_netcore2.0_EF2.1.csproj create mode 100644 src-console/ConsoleAppEF2.1/Database/Car.cs create mode 100644 src-console/ConsoleAppEF2.1/Database/TestContext.cs create mode 100644 src-console/ConsoleAppEF2.1/Program.cs diff --git a/README.md b/README.md index fa39b8e3..344ba8a3 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ The following frameworks are supported: - net45 and up - netstandard1.3 - netstandard2.0 -- uap10.0 +- uap10.0 (TODO...)s ## Fork details This fork takes the basic library to a new level. Contains XML Documentation and examples on how to use it. Also adds unit testing to help ensure that it works properly. diff --git a/System.Linq.Dynamic.Core.sln b/System.Linq.Dynamic.Core.sln index 87a54a0e..92c3faf9 100644 --- a/System.Linq.Dynamic.Core.sln +++ b/System.Linq.Dynamic.Core.sln @@ -53,6 +53,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp_netcore2.0_EF2.0 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MemoryLeakTest167", "src-console\MemoryLeakTest167\MemoryLeakTest167.csproj", "{AD4F83E5-4240-485D-BB5C-F43974F716E4}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleApp_netcore2.0_EF2.1", "src-console\ConsoleAppEF2.1\ConsoleApp_netcore2.0_EF2.1.csproj", "{EDF434F6-70C0-4005-B63E-0C365B3DA42A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -287,6 +289,22 @@ Global {AD4F83E5-4240-485D-BB5C-F43974F716E4}.Release|x64.Build.0 = Release|Any CPU {AD4F83E5-4240-485D-BB5C-F43974F716E4}.Release|x86.ActiveCfg = Release|Any CPU {AD4F83E5-4240-485D-BB5C-F43974F716E4}.Release|x86.Build.0 = Release|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Debug|ARM.ActiveCfg = Debug|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Debug|ARM.Build.0 = Debug|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Debug|x64.ActiveCfg = Debug|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Debug|x64.Build.0 = Debug|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Debug|x86.ActiveCfg = Debug|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Debug|x86.Build.0 = Debug|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Release|Any CPU.Build.0 = Release|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Release|ARM.ActiveCfg = Release|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Release|ARM.Build.0 = Release|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Release|x64.ActiveCfg = Release|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Release|x64.Build.0 = Release|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Release|x86.ActiveCfg = Release|Any CPU + {EDF434F6-70C0-4005-B63E-0C365B3DA42A}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -306,6 +324,7 @@ Global {5DC68E83-ABE0-4887-B17E-1ED4EEE89C2C} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} {437473EE-7FBB-4C28-96EC-41E1AEE161F3} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} {AD4F83E5-4240-485D-BB5C-F43974F716E4} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} + {EDF434F6-70C0-4005-B63E-0C365B3DA42A} = {7971CAEB-B9F2-416B-966D-2D697C4C1E62} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {94C56722-194E-4B8B-BC23-B3F754E89A20} diff --git a/src-console/ConsoleAppEF2.1/ConsoleApp_netcore2.0_EF2.1.csproj b/src-console/ConsoleAppEF2.1/ConsoleApp_netcore2.0_EF2.1.csproj new file mode 100644 index 00000000..4351e79f --- /dev/null +++ b/src-console/ConsoleAppEF2.1/ConsoleApp_netcore2.0_EF2.1.csproj @@ -0,0 +1,21 @@ + + + + Exe + netcoreapp2.0 + ConsoleAppEF21 + ConsoleAppEF21 + + + + + + + + + + + + + + diff --git a/src-console/ConsoleAppEF2.1/Database/Car.cs b/src-console/ConsoleAppEF2.1/Database/Car.cs new file mode 100644 index 00000000..3c16220e --- /dev/null +++ b/src-console/ConsoleAppEF2.1/Database/Car.cs @@ -0,0 +1,23 @@ +using System.ComponentModel.DataAnnotations; + +namespace ConsoleAppEF21.Database +{ + public class Car + { + [Key] + public int Key { get; set; } + + [Required] + [StringLength(8)] + public string Vin { get; set; } + + [Required] + public string Year { get; set; } + + [Required] + public string Brand { get; set; } + + [Required] + public string Color { get; set; } + } +} diff --git a/src-console/ConsoleAppEF2.1/Database/TestContext.cs b/src-console/ConsoleAppEF2.1/Database/TestContext.cs new file mode 100644 index 00000000..e15488fe --- /dev/null +++ b/src-console/ConsoleAppEF2.1/Database/TestContext.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Console; + +namespace ConsoleAppEF21.Database +{ + public class TestContext : DbContext + { + public static readonly LoggerFactory MyLoggerFactory = new LoggerFactory(new[] { new ConsoleLoggerProvider((filter, includeScopes) => true, true) }); + + public virtual DbSet Cars { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseLoggerFactory(MyLoggerFactory); // Warning: Do not create a new ILoggerFactory instance each time + optionsBuilder.EnableSensitiveDataLogging(); + + optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=CarsEF20;Trusted_Connection=True;"); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasKey(c => c.Key); + } + + // https://stackoverflow.com/questions/46212704/how-do-i-write-ef-functions-extension-method + public static bool Like(string matchExpression, string pattern) => EF.Functions.Like(matchExpression, pattern); + } +} diff --git a/src-console/ConsoleAppEF2.1/Program.cs b/src-console/ConsoleAppEF2.1/Program.cs new file mode 100644 index 00000000..cd2d8c19 --- /dev/null +++ b/src-console/ConsoleAppEF2.1/Program.cs @@ -0,0 +1,126 @@ +using System; +using System.Linq; +using System.Linq.Dynamic.Core; +using ConsoleAppEF21.Database; +using Newtonsoft.Json; + +namespace ConsoleAppEF21 +{ + class Program + { + //class C : AbstractDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider + //{ + // public HashSet GetCustomTypes() + // { + // var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + + // var set = new HashSet(FindTypesMarkedWithDynamicLinqTypeAttribute(assemblies)) + // { + // typeof(TestContext) + // }; + + // return set; + // } + //} + + //private static IQueryable GetQueryable() + //{ + // var random = new Random((int)DateTime.Now.Ticks); + + // var x = Enumerable.Range(0, 10).Select(i => new + // { + // Id = i, + // Value = random.Next(), + // }); + + // return x.AsQueryable().Select("new (it as Id, @0 as Value)", random.Next()); + // // return x.AsQueryable(); //x.AsQueryable().Select("new (Id, Value)"); + //} + + static void Main(string[] args) + { + //IQueryable qry = GetQueryable(); + + //var result = qry.Select("it").OrderBy("Value"); + //try + //{ + // Console.WriteLine("result {0}", JsonConvert.SerializeObject(result, Formatting.Indented)); + //} + //catch (Exception) + //{ + // // Console.WriteLine(e); + //} + + //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.None)); + + var config = new ParsingConfig + { + EvaluateGroupByAtDatabase = true + }; + + var context = new TestContext(); + if (!context.Cars.Any()) + { + context.Cars.Add(new Car { Brand = "Ford", Color = "Blue", Vin = "yes", Year = "2017" }); + context.Cars.Add(new Car { Brand = "Fiat", Color = "Red", Vin = "yes", Year = "2016" }); + context.Cars.Add(new Car { Brand = "Alfa", Color = "Black", Vin = "no", Year = "1979" }); + context.Cars.Add(new Car { Brand = "Alfa", Color = "Black", Vin = "a%bc", Year = "1979" }); + context.SaveChanges(); + } + + //var g1 = context.Cars.GroupBy("new(Brand)").Select("new(Key.Brand as KeyValue1, it.Count() as CountValue1)").ToDynamicList(); + //Console.WriteLine("GroupBy @ local {0}", JsonConvert.SerializeObject(g1, Formatting.Indented)); + + //Console.WriteLine(new string('_', 80)); + + var g2 = context.Cars.GroupBy("new(Brand)", config).Select("new(Key.Brand as KeyValue2, it.Count() as CountValue2)").ToDynamicList(); + Console.WriteLine("GroupBy @ database {0}", JsonConvert.SerializeObject(g2, Formatting.Indented)); + + //var carFirstOrDefault = context.Cars.Where(config, "Brand == \"Ford\""); + //Console.WriteLine("carFirstOrDefault {0}", JsonConvert.SerializeObject(carFirstOrDefault, Formatting.Indented)); + + //var carsLike1 = + // from c in context.Cars + // where EF.Functions.Like(c.Brand, "%a%") + // select c; + //Console.WriteLine("carsLike1 {0}", JsonConvert.SerializeObject(carsLike1, Formatting.Indented)); + + //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 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)); + + //var dynamicFunctionsLike2 = context.Cars.Where(config, "DynamicFunctions.Like(Vin, \"%a.%b%\", \".\")"); + //Console.WriteLine("dynamicFunctionsLike2 {0}", JsonConvert.SerializeObject(dynamicFunctionsLike2, Formatting.Indented)); + + //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); + //} + } + } +} diff --git a/src/EntityFramework.DynamicLinq/EntityFramework.DynamicLinq.csproj b/src/EntityFramework.DynamicLinq/EntityFramework.DynamicLinq.csproj index a5805e1b..4fd58aa1 100644 --- a/src/EntityFramework.DynamicLinq/EntityFramework.DynamicLinq.csproj +++ b/src/EntityFramework.DynamicLinq/EntityFramework.DynamicLinq.csproj @@ -12,7 +12,7 @@ true true system;linq;dynamic;entityframework;core;async - Bugfixes and new Features. For details see the release notes. + Bugfixes and new Features. For details see CHANGELOG.md https://github.com/StefH/System.Linq.Dynamic.Core https://github.com/StefH/System.Linq.Dynamic.Core/blob/master/licence.txt git @@ -58,4 +58,4 @@ - \ No newline at end of file + diff --git a/src/Microsoft.EntityFrameworkCore.DynamicLinq/Microsoft.EntityFrameworkCore.DynamicLinq.csproj b/src/Microsoft.EntityFrameworkCore.DynamicLinq/Microsoft.EntityFrameworkCore.DynamicLinq.csproj index e817cc9c..085c3db5 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 1.0.8.11 Stef Heyenrath - net451;net46;netstandard1.3;netstandard2.0;uap10.0 + net451;net46;netstandard1.3;netstandard2.0 $(DefineConstants);EFCORE true Microsoft.EntityFrameworkCore.DynamicLinq @@ -12,7 +12,7 @@ true true system;linq;dynamic;entityframework;core;async - Bugfixes and new Features. For details see the release notes. + Bugfixes and new Features. For details see CHANGELOG.md https://github.com/StefH/System.Linq.Dynamic.Core https://github.com/StefH/System.Linq.Dynamic.Core/blob/master/licence.txt git diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs index dcf0521d..f3c68807 100644 --- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs +++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs @@ -500,7 +500,7 @@ public static IQueryable GroupBy([NotNull] this IQueryable source, [CanBeNull] P Check.NotEmpty(keySelector, nameof(keySelector)); Check.NotEmpty(resultSelector, nameof(resultSelector)); - bool createParameterCtor = source.IsLinqToObjects(); + bool createParameterCtor = config?.EvaluateGroupByAtDatabase ?? source.IsLinqToObjects(); LambdaExpression keyLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, keySelector, args); LambdaExpression elementLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, resultSelector, args); @@ -570,7 +570,7 @@ public static IQueryable GroupBy([NotNull] this IQueryable source, [CanBeNull] P Check.NotNull(source, nameof(source)); Check.NotEmpty(keySelector, nameof(keySelector)); - bool createParameterCtor = source.IsLinqToObjects(); + bool createParameterCtor = config?.EvaluateGroupByAtDatabase ?? source.IsLinqToObjects(); LambdaExpression keyLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, keySelector, args); var optimized = OptimizeExpression(Expression.Call( @@ -586,7 +586,6 @@ public static IQueryable GroupBy([NotNull] this IQueryable source, [NotNull] str { return GroupBy(source, (ParsingConfig)null, keySelector, args); } - #endregion GroupBy #region GroupByMany @@ -897,7 +896,7 @@ public static dynamic LastOrDefault([NotNull] this IQueryable source) #if NET35 public static object LastOrDefault([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #else - public static dynamic LastOrDefault([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) + public static dynamic LastOrDefault([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #endif { Check.NotNull(source, nameof(source)); @@ -1580,7 +1579,7 @@ public static dynamic SingleOrDefault([NotNull] this IQueryable source) #if NET35 public static object SingleOrDefault([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #else - public static dynamic SingleOrDefault([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) + public static dynamic SingleOrDefault([NotNull] this IQueryable source, [CanBeNull] ParsingConfig config, [NotNull] string predicate, params object[] args) #endif { Check.NotNull(source, nameof(source)); diff --git a/src/System.Linq.Dynamic.Core/ParsingConfig.cs b/src/System.Linq.Dynamic.Core/ParsingConfig.cs index 01a13c88..dee8fa4c 100644 --- a/src/System.Linq.Dynamic.Core/ParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core/ParsingConfig.cs @@ -44,9 +44,12 @@ public IDynamicLinkCustomTypeProvider CustomTypeProvider /// /// Gets or sets a value indicating whether to use dynamic object class for anonymous types. /// - /// - /// true if wether to use dynamic object class for anonymous types; otherwise, false. - /// public bool UseDynamicObjectClassForAnonymousTypes { get; set; } + + /// + /// Gets or sets a value indicating whether the EntityFramwwork version supports evaluating GroupBy at database level. + /// See https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.1#linq-groupby-translation + /// + public bool EvaluateGroupByAtDatabase { get; set; } } } 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 09e86275..007ee072 100644 --- a/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj +++ b/src/System.Linq.Dynamic.Core/System.Linq.Dynamic.Core.csproj @@ -4,14 +4,14 @@ System.Linq.Dynamic.Core 1.0.8.11 Microsoft;Scott Guthrie;King Wilder;Nathan Arnott;Stef Heyenrath - net35;net40;net45;net46;uap10.0;netstandard1.3;netstandard2.0 + net35;net40;net45;net46;netstandard1.3;netstandard2.0 true System.Linq.Dynamic.Core System.Linq.Dynamic.Core.snk true true system;linq;dynamic;core;dotnet;NETCoreApp;NETStandard - Bugfixes and new Features. For details see the release notes. + Bugfixes and new Features. For details see CHANGELOG.md https://github.com/StefH/System.Linq.Dynamic.Core https://github.com/StefH/System.Linq.Dynamic.Core/blob/master/licence.txt git From 6741a586e943a0e0a09f3ff32ce449b99a478b02 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Thu, 19 Jul 2018 09:29:12 +0200 Subject: [PATCH 2/4] wip --- src-console/ConsoleAppEF2.1/Program.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src-console/ConsoleAppEF2.1/Program.cs b/src-console/ConsoleAppEF2.1/Program.cs index cd2d8c19..9b362231 100644 --- a/src-console/ConsoleAppEF2.1/Program.cs +++ b/src-console/ConsoleAppEF2.1/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Linq.Dynamic.Core; using ConsoleAppEF21.Database; @@ -39,6 +40,10 @@ class Program static void Main(string[] args) { + var list = new List { new Car { Key = 1 }, new Car { Key = 2 } }; + + var carsTest = list.AsQueryable().Where("Key = @0", "1").ToList(); + //IQueryable qry = GetQueryable(); //var result = qry.Select("it").OrderBy("Value"); From b29133d7d1c65c36ae3fb163bea16eb0a4170ff9 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 22 Jul 2018 12:25:58 +0200 Subject: [PATCH 3/4] Added ParsingConfig.DefaultEFCore21 --- src-console/ConsoleAppEF2.1.1/Program.cs | 9 ++++++--- src-console/ConsoleAppEF2.1/Program.cs | 5 +---- src/System.Linq.Dynamic.Core/ParsingConfig.cs | 13 ++++++++++++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src-console/ConsoleAppEF2.1.1/Program.cs b/src-console/ConsoleAppEF2.1.1/Program.cs index 37803140..ff0e7adc 100644 --- a/src-console/ConsoleAppEF2.1.1/Program.cs +++ b/src-console/ConsoleAppEF2.1.1/Program.cs @@ -61,7 +61,7 @@ static void Main(string[] args) }; Console.WriteLine("all {0}", JsonConvert.SerializeObject(all, Formatting.Indented)); - var config = new ParsingConfig(); + var config = ParsingConfig.DefaultEFCore21; config.CustomTypeProvider = new C(); var context = new TestContext(); @@ -79,8 +79,11 @@ static void Main(string[] args) context.SaveChanges(); } - var carFirstOrDefault = context.Cars.Where(config, "Brand == \"Ford\""); - Console.WriteLine("carFirstOrDefault {0}", JsonConvert.SerializeObject(carFirstOrDefault, Formatting.Indented)); + var carFirstOrDefault1 = context.Cars.Where(config, "Brand == \"Ford\""); + Console.WriteLine("carFirstOrDefault1 {0}", JsonConvert.SerializeObject(carFirstOrDefault1, Formatting.Indented)); + + var carFirstOrDefault2 = context.Cars.Where(config, "Brand == @0", "Alfa"); + Console.WriteLine("carFirstOrDefault2 {0}", JsonConvert.SerializeObject(carFirstOrDefault2, Formatting.Indented)); var carsLike1 = from c in context.Cars diff --git a/src-console/ConsoleAppEF2.1/Program.cs b/src-console/ConsoleAppEF2.1/Program.cs index 9b362231..9c6290a5 100644 --- a/src-console/ConsoleAppEF2.1/Program.cs +++ b/src-console/ConsoleAppEF2.1/Program.cs @@ -64,10 +64,7 @@ static void Main(string[] args) //}; // Console.WriteLine("all {0}", JsonConvert.SerializeObject(all, Formatting.None)); - var config = new ParsingConfig - { - EvaluateGroupByAtDatabase = true - }; + var config = ParsingConfig.DefaultEFCore21; var context = new TestContext(); if (!context.Cars.Any()) diff --git a/src/System.Linq.Dynamic.Core/ParsingConfig.cs b/src/System.Linq.Dynamic.Core/ParsingConfig.cs index dee8fa4c..cc0414a5 100644 --- a/src/System.Linq.Dynamic.Core/ParsingConfig.cs +++ b/src/System.Linq.Dynamic.Core/ParsingConfig.cs @@ -7,7 +7,18 @@ namespace System.Linq.Dynamic.Core /// public class ParsingConfig { - internal static ParsingConfig Default { get; } = new ParsingConfig(); + /// + /// Default ParsingConfig + /// + public static ParsingConfig Default { get; } = new ParsingConfig(); + + /// + /// Default ParsingConfig for EntityFramework Core 2.1 and higher + /// + public static ParsingConfig DefaultEFCore21 { get; } = new ParsingConfig + { + EvaluateGroupByAtDatabase = true + }; private IDynamicLinkCustomTypeProvider _customTypeProvider; From c9640c008a23b2753b8a9d50fed6c42e4627a992 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Fri, 17 Aug 2018 18:30:27 +0200 Subject: [PATCH 4/4] Added EvaluateGroupByAtDatabase to Select and SelectMany --- .../DynamicQueryableExtensions.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs index f3c68807..ed001349 100644 --- a/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs +++ b/src/System.Linq.Dynamic.Core/DynamicQueryableExtensions.cs @@ -1133,7 +1133,7 @@ public static IQueryable Select([NotNull] this IQueryable source, [CanBeNull] Pa Check.NotNull(source, nameof(source)); Check.NotEmpty(selector, nameof(selector)); - bool createParameterCtor = source.IsLinqToObjects(); + bool createParameterCtor = config?.EvaluateGroupByAtDatabase ?? source.IsLinqToObjects(); LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, selector, args); var optimized = OptimizeExpression(Expression.Call( @@ -1173,7 +1173,7 @@ public static IQueryable Select([NotNull] this IQueryable sour Check.NotNull(source, nameof(source)); Check.NotEmpty(selector, nameof(selector)); - bool createParameterCtor = source.IsLinqToObjects(); + bool createParameterCtor = config?.EvaluateGroupByAtDatabase ?? source.IsLinqToObjects(); LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, typeof(TResult), selector, args); var optimized = OptimizeExpression(Expression.Call( @@ -1211,7 +1211,7 @@ public static IQueryable Select([NotNull] this IQueryable source, [CanBeNull] Pa Check.NotNull(resultType, nameof(resultType)); Check.NotEmpty(selector, nameof(selector)); - bool createParameterCtor = source.IsLinqToObjects(); + bool createParameterCtor = config?.EvaluateGroupByAtDatabase ?? source.IsLinqToObjects(); LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, resultType, selector, args); var optimized = OptimizeExpression(Expression.Call( @@ -1289,7 +1289,7 @@ public static IQueryable SelectMany([NotNull] this IQueryable source, [NotNull] private static IQueryable SelectManyInternal(IQueryable source, ParsingConfig config, Type resultType, string selector, params object[] args) { - bool createParameterCtor = source.IsLinqToObjects(); + bool createParameterCtor = config?.EvaluateGroupByAtDatabase ?? source.IsLinqToObjects(); LambdaExpression lambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, selector, args); //Extra help to get SelectMany to work from StackOverflow Answer @@ -1346,7 +1346,7 @@ public static IQueryable SelectMany([NotNull] this IQueryable Check.NotNull(source, nameof(source)); Check.NotEmpty(selector, nameof(selector)); - bool createParameterCtor = source.IsLinqToObjects(); + bool createParameterCtor = config?.EvaluateGroupByAtDatabase ?? source.IsLinqToObjects(); LambdaExpression lambda = DynamicExpressionParser.ParseLambda(createParameterCtor, source.ElementType, null, selector, args); //we have to adjust to lambda to return an IEnumerable instead of whatever the actual property is. @@ -1439,7 +1439,7 @@ public static IQueryable SelectMany([NotNull] this IQueryable source, [CanBeNull Check.NotEmpty(resultSelector, nameof(resultSelector)); Check.NotEmpty(resultParameterName, nameof(resultParameterName)); - bool createParameterCtor = source.IsLinqToObjects(); + bool createParameterCtor = config?.EvaluateGroupByAtDatabase ?? source.IsLinqToObjects(); LambdaExpression sourceSelectLambda = DynamicExpressionParser.ParseLambda(config, createParameterCtor, source.ElementType, null, collectionSelector, collectionSelectorArgs); //we have to adjust to lambda to return an IEnumerable instead of whatever the actual property is.