diff --git a/csharp-advanced-topics/ExpressionTreesInCSharp/Benchmarks/Benchmarks.csproj b/csharp-advanced-topics/ExpressionTreesInCSharp/Benchmarks/Benchmarks.csproj new file mode 100644 index 0000000000..d3c17d10a2 --- /dev/null +++ b/csharp-advanced-topics/ExpressionTreesInCSharp/Benchmarks/Benchmarks.csproj @@ -0,0 +1,18 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + + + + + diff --git a/csharp-advanced-topics/ExpressionTreesInCSharp/Benchmarks/CompileBenchmarks.cs b/csharp-advanced-topics/ExpressionTreesInCSharp/Benchmarks/CompileBenchmarks.cs new file mode 100644 index 0000000000..c7112007ff --- /dev/null +++ b/csharp-advanced-topics/ExpressionTreesInCSharp/Benchmarks/CompileBenchmarks.cs @@ -0,0 +1,38 @@ +using BenchmarkDotNet.Attributes; +using ExpressionTreesInCSharp; +using System.Linq.Expressions; + +namespace Benchmarks; + +[MemoryDiagnoser] +public class CompileBenchmarks +{ + private readonly Expression> _sumExpressionTree = + ExpressionTrees.CreateExpressionTreeFromLambdaExpression(); + + private readonly Func _lambda = ExpressionTrees.CreateLambdaExpression(); + private Func _compiled = default!; + private Func _interpreted = default!; + + [GlobalSetup] + public void Setup() + { + _compiled = _sumExpressionTree.Compile(); + _interpreted = _sumExpressionTree.Compile(preferInterpretation: true); + } + + [Benchmark] + public int InvokeLambda() => _lambda(1, 2); + + [Benchmark] + public int InvokeCachedCompiledDelegate() => _compiled(1, 2); + + [Benchmark] + public int InvokeCachedInterpretedDelegate() => _interpreted(1, 2); + + [Benchmark] + public int CompileAndInvoke() => _sumExpressionTree.Compile()(1, 2); + + [Benchmark] + public int InterpretAndInvoke() => _sumExpressionTree.Compile(preferInterpretation: true)(1, 2); +} diff --git a/csharp-advanced-topics/ExpressionTreesInCSharp/Benchmarks/Program.cs b/csharp-advanced-topics/ExpressionTreesInCSharp/Benchmarks/Program.cs new file mode 100644 index 0000000000..c9a046727c --- /dev/null +++ b/csharp-advanced-topics/ExpressionTreesInCSharp/Benchmarks/Program.cs @@ -0,0 +1,3 @@ +using BenchmarkDotNet.Running; + +BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); diff --git a/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp.sln b/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp.sln index e59a24efd4..e1a50157f9 100644 --- a/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp.sln +++ b/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp.sln @@ -7,20 +7,54 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExpressionTreesInCSharp", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{1421730A-00C5-44F5-98F4-8E6623F7C3C5}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{17A874D8-A920-4912-8E62-1B43E8176375}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {DD0C1186-0518-4FA7-89A9-5101A346045C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DD0C1186-0518-4FA7-89A9-5101A346045C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD0C1186-0518-4FA7-89A9-5101A346045C}.Debug|x64.ActiveCfg = Debug|Any CPU + {DD0C1186-0518-4FA7-89A9-5101A346045C}.Debug|x64.Build.0 = Debug|Any CPU + {DD0C1186-0518-4FA7-89A9-5101A346045C}.Debug|x86.ActiveCfg = Debug|Any CPU + {DD0C1186-0518-4FA7-89A9-5101A346045C}.Debug|x86.Build.0 = Debug|Any CPU {DD0C1186-0518-4FA7-89A9-5101A346045C}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD0C1186-0518-4FA7-89A9-5101A346045C}.Release|Any CPU.Build.0 = Release|Any CPU + {DD0C1186-0518-4FA7-89A9-5101A346045C}.Release|x64.ActiveCfg = Release|Any CPU + {DD0C1186-0518-4FA7-89A9-5101A346045C}.Release|x64.Build.0 = Release|Any CPU + {DD0C1186-0518-4FA7-89A9-5101A346045C}.Release|x86.ActiveCfg = Release|Any CPU + {DD0C1186-0518-4FA7-89A9-5101A346045C}.Release|x86.Build.0 = Release|Any CPU {1421730A-00C5-44F5-98F4-8E6623F7C3C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1421730A-00C5-44F5-98F4-8E6623F7C3C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1421730A-00C5-44F5-98F4-8E6623F7C3C5}.Debug|x64.ActiveCfg = Debug|Any CPU + {1421730A-00C5-44F5-98F4-8E6623F7C3C5}.Debug|x64.Build.0 = Debug|Any CPU + {1421730A-00C5-44F5-98F4-8E6623F7C3C5}.Debug|x86.ActiveCfg = Debug|Any CPU + {1421730A-00C5-44F5-98F4-8E6623F7C3C5}.Debug|x86.Build.0 = Debug|Any CPU {1421730A-00C5-44F5-98F4-8E6623F7C3C5}.Release|Any CPU.ActiveCfg = Release|Any CPU {1421730A-00C5-44F5-98F4-8E6623F7C3C5}.Release|Any CPU.Build.0 = Release|Any CPU + {1421730A-00C5-44F5-98F4-8E6623F7C3C5}.Release|x64.ActiveCfg = Release|Any CPU + {1421730A-00C5-44F5-98F4-8E6623F7C3C5}.Release|x64.Build.0 = Release|Any CPU + {1421730A-00C5-44F5-98F4-8E6623F7C3C5}.Release|x86.ActiveCfg = Release|Any CPU + {1421730A-00C5-44F5-98F4-8E6623F7C3C5}.Release|x86.Build.0 = Release|Any CPU + {17A874D8-A920-4912-8E62-1B43E8176375}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {17A874D8-A920-4912-8E62-1B43E8176375}.Debug|Any CPU.Build.0 = Debug|Any CPU + {17A874D8-A920-4912-8E62-1B43E8176375}.Debug|x64.ActiveCfg = Debug|Any CPU + {17A874D8-A920-4912-8E62-1B43E8176375}.Debug|x64.Build.0 = Debug|Any CPU + {17A874D8-A920-4912-8E62-1B43E8176375}.Debug|x86.ActiveCfg = Debug|Any CPU + {17A874D8-A920-4912-8E62-1B43E8176375}.Debug|x86.Build.0 = Debug|Any CPU + {17A874D8-A920-4912-8E62-1B43E8176375}.Release|Any CPU.ActiveCfg = Release|Any CPU + {17A874D8-A920-4912-8E62-1B43E8176375}.Release|Any CPU.Build.0 = Release|Any CPU + {17A874D8-A920-4912-8E62-1B43E8176375}.Release|x64.ActiveCfg = Release|Any CPU + {17A874D8-A920-4912-8E62-1B43E8176375}.Release|x64.Build.0 = Release|Any CPU + {17A874D8-A920-4912-8E62-1B43E8176375}.Release|x86.ActiveCfg = Release|Any CPU + {17A874D8-A920-4912-8E62-1B43E8176375}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/DynamicMethods/DynamicMethodGenerator.cs b/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/DynamicMethods/DynamicMethodGenerator.cs index c32e1d7a27..0bb966a1e6 100644 --- a/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/DynamicMethods/DynamicMethodGenerator.cs +++ b/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/DynamicMethods/DynamicMethodGenerator.cs @@ -13,8 +13,8 @@ public static Func GenerateExecutableFunctionFromMathStatement(st var right = Expression.Parameter(typeof(int), variables[1]); // Create the expression tree - var add = Expression.Multiply(left, right); - var lambda = Expression.Lambda(add, left, right); + var product = Expression.Multiply(left, right); + var lambda = Expression.Lambda(product, left, right); // Return the compiled method return (Func)lambda.Compile(); diff --git a/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/EntityFramework/EntityFrameworkSqlite.cs b/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/EntityFramework/EntityFrameworkSqlite.cs index 29700f2ed5..62e9310e1f 100644 --- a/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/EntityFramework/EntityFrameworkSqlite.cs +++ b/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/EntityFramework/EntityFrameworkSqlite.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using System.Linq.Expressions; namespace ExpressionTreesInCSharp.EntityFramework; public class EntityFrameworkSqlite @@ -26,4 +27,14 @@ public string GetWhereQuery() return whereQuery; } -} \ No newline at end of file + + public IEnumerable FilterWithFunc(Func predicate) + { + return _dbContext.Users.Where(predicate); + } + + public IQueryable FilterWithExpression(Expression> predicate) + { + return _dbContext.Users.Where(predicate); + } +} diff --git a/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/ExpressionTrees.cs b/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/ExpressionTrees.cs index c3c1ec5dad..4ecc9c1469 100644 --- a/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/ExpressionTrees.cs +++ b/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/ExpressionTrees.cs @@ -33,4 +33,9 @@ public static Func CompileExpressionTreeToLambdaExpression(Expres return delegateRepresentingLambdaExpression; } + + public static Func InterpretExpressionTree(Expression> expressionTree) + { + return expressionTree.Compile(preferInterpretation: true); + } } diff --git a/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/ExpressionTreesInCSharp.csproj b/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/ExpressionTreesInCSharp.csproj index 554375c136..19b1557167 100644 --- a/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/ExpressionTreesInCSharp.csproj +++ b/csharp-advanced-topics/ExpressionTreesInCSharp/ExpressionTreesInCSharp/ExpressionTreesInCSharp.csproj @@ -2,13 +2,13 @@ Exe - net7.0 + net10.0 enable enable - + diff --git a/csharp-advanced-topics/ExpressionTreesInCSharp/Tests/EntityFrameworkTests.cs b/csharp-advanced-topics/ExpressionTreesInCSharp/Tests/EntityFrameworkTests.cs index 9d62baf5de..2a45c4666c 100644 --- a/csharp-advanced-topics/ExpressionTreesInCSharp/Tests/EntityFrameworkTests.cs +++ b/csharp-advanced-topics/ExpressionTreesInCSharp/Tests/EntityFrameworkTests.cs @@ -33,4 +33,16 @@ public void GivenAUserDbContext_WhenListedUsersWithFilters_ThenShouldUseCorrectQ Assert.Equal(expectedQuery, query); } + + [Fact] + public void GivenAFuncPredicate_WhenFiltering_ThenTheQueryLeavesEntityFramework() + { + var efSqlite = new EntityFrameworkSqlite(); + + var withFunc = efSqlite.FilterWithFunc(x => x.Age > 20); + var withExpression = efSqlite.FilterWithExpression(x => x.Age > 20); + + Assert.False(withFunc is IQueryable); + Assert.True(withExpression is IQueryable); + } } diff --git a/csharp-advanced-topics/ExpressionTreesInCSharp/Tests/ExpressionTreeTests.cs b/csharp-advanced-topics/ExpressionTreesInCSharp/Tests/ExpressionTreeTests.cs index fb7da83460..f666f39b31 100644 --- a/csharp-advanced-topics/ExpressionTreesInCSharp/Tests/ExpressionTreeTests.cs +++ b/csharp-advanced-topics/ExpressionTreesInCSharp/Tests/ExpressionTreeTests.cs @@ -70,4 +70,14 @@ public void GivenAnExpressionTree_WhenCompiled_ThenShouldExecuteAndReturnResult( Assert.Equal(2, sum); } -} \ No newline at end of file + + [Fact] + public void GivenAnExpressionTree_WhenInterpreted_ThenShouldReturnTheSameResult() + { + var expression = ExpressionTrees.CreateExpressionTreeFromLambdaExpression(); + + var interpretedSum = ExpressionTrees.InterpretExpressionTree(expression); + + Assert.Equal(3, interpretedSum(1, 2)); + } +} diff --git a/csharp-advanced-topics/ExpressionTreesInCSharp/Tests/Tests.csproj b/csharp-advanced-topics/ExpressionTreesInCSharp/Tests/Tests.csproj index f7ba662f76..6d962eb27b 100644 --- a/csharp-advanced-topics/ExpressionTreesInCSharp/Tests/Tests.csproj +++ b/csharp-advanced-topics/ExpressionTreesInCSharp/Tests/Tests.csproj @@ -1,7 +1,7 @@ - net7.0 + net10.0 enable enable @@ -10,14 +10,14 @@ - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all