├── jetbrains.png ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE.md ├── CONTRIBUTING.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── build.yml ├── src └── DynamicQueryable │ ├── icon.png │ ├── DynamicQueryable.Compare.cs │ ├── DynamicQueryable.Alter.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── DynamicQueryable.csproj │ ├── DynamicQueryable.Count.cs │ ├── DynamicQueryable.Look.cs │ ├── DynamicQueryable.Select.cs │ ├── DynamicQueryable.cs │ ├── DynamicQueryable.Filter.cs │ ├── DynamicQueryable.Group.cs │ ├── DynamicQueryable.Order.cs │ ├── DynamicQueryable.Join.cs │ ├── DynamicQueryable.Aggregate.cs │ └── DynamicQueryable.Get.cs ├── test └── DynamicQueryable.Tests │ ├── Fixture │ ├── Entity.cs │ ├── Address.cs │ ├── Company.cs │ ├── Product.cs │ ├── Person.cs │ ├── Order.cs │ └── OrderLine.cs │ ├── DynamicQueryable.Tests.csproj │ └── ExpressionTests.cs ├── .idea └── .idea.DynamicQueryable │ └── .idea │ ├── encodings.xml │ ├── vcs.xml │ ├── indexLayout.xml │ └── .gitignore ├── .gitignore ├── omnisharp.json ├── LICENSE ├── Dockerfile ├── README.md ├── DynamicQueryable.sln └── notebooks └── README.ipynb /jetbrains.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umutozel/DynamicQueryable/HEAD/jetbrains.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [umutozel] 4 | patreon: umutozel 5 | -------------------------------------------------------------------------------- /src/DynamicQueryable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/umutozel/DynamicQueryable/HEAD/src/DynamicQueryable/icon.png -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## DynamicQueryable version 2 | 3 | 4 | ## Steps to reproduce 5 | 6 | 7 | ## Expected behavior 8 | 9 | 10 | ## Actual behavior 11 | -------------------------------------------------------------------------------- /test/DynamicQueryable.Tests/Fixture/Entity.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicQueryable.Tests.Fixture; 2 | 3 | public class Entity { 4 | public bool IsActive { get; set; } 5 | public int IsSomething { get; set; } 6 | } 7 | -------------------------------------------------------------------------------- /.idea/.idea.DynamicQueryable/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/.idea.DynamicQueryable/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/DynamicQueryable.Tests/Fixture/Address.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicQueryable.Tests.Fixture; 2 | 3 | public class Address { 4 | public string City { get; set; } = ""; 5 | public int Zip { get; set; } 6 | public int? Number { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /test/DynamicQueryable.Tests/Fixture/Company.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicQueryable.Tests.Fixture; 2 | 3 | public class Company { 4 | public int Id { get; set; } 5 | public string? CompanyName { get; set; } 6 | public string? Phone { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /test/DynamicQueryable.Tests/Fixture/Product.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicQueryable.Tests.Fixture; 2 | 3 | public class Product { 4 | public int Id { get; set; } 5 | public string? Name { get; set; } 6 | public Company? Supplier { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /.idea/.idea.DynamicQueryable/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/DynamicQueryable.Tests/Fixture/Person.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicQueryable.Tests.Fixture; 2 | 3 | public class Person { 4 | public string Name { get; set; } = ""; 5 | public int Age { get; set; } 6 | public int? Number { get; set; } 7 | public Address Address { get; set; } = new(); 8 | } 9 | -------------------------------------------------------------------------------- /.idea/.idea.DynamicQueryable/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /modules.xml 6 | /projectSettingsUpdater.xml 7 | /.idea.DynamicQueryable.iml 8 | /contentModel.xml 9 | # Editor-based HTTP Client requests 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | -------------------------------------------------------------------------------- /test/DynamicQueryable.Tests/Fixture/Order.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DynamicQueryable.Tests.Fixture; 5 | 6 | public class Order { 7 | public int Id { get; set; } 8 | public string? OrderNo { get; set; } 9 | public DateTime? OrderDate { get; set; } 10 | public double? Price; 11 | public IList Lines { get; set; } = []; 12 | } 13 | -------------------------------------------------------------------------------- /test/DynamicQueryable.Tests/Fixture/OrderLine.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicQueryable.Tests.Fixture; 2 | 3 | public class OrderLine { 4 | public int Id { get; set; } 5 | public Product? Product; 6 | public int ProductId { get; set; } 7 | public Order? Order { get; set; } 8 | public int OrderId { get; set; } 9 | public int? Count { get; set; } 10 | public double? UnitPrice { get; set; } 11 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.*~ 3 | project.lock.json 4 | .DS_Store 5 | *.pyc 6 | nupkg/ 7 | 8 | # Visual Studio Code 9 | .vscode 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.userosscache 15 | *.sln.docstates 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Dd]ebugPublic/ 20 | [Rr]elease/ 21 | [Rr]eleases/ 22 | x64/ 23 | x86/ 24 | build/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Oo]ut/ 29 | msbuild.log 30 | msbuild.err 31 | msbuild.wrn 32 | 33 | # Visual Studio 2015 34 | .vs/ 35 | 36 | # Coverage 37 | coverage.json 38 | coverage.xml 39 | lcov.info 40 | .ipynb_checkpoints 41 | -------------------------------------------------------------------------------- /omnisharp.json: -------------------------------------------------------------------------------- 1 | { 2 | "FormattingOptions": { 3 | "NewLinesForBracesInLambdaExpressionBody": false, 4 | "NewLinesForBracesInAnonymousMethods": false, 5 | "NewLinesForBracesInAnonymousTypes": false, 6 | "NewLinesForBracesInControlBlocks": false, 7 | "NewLinesForBracesInTypes": false, 8 | "NewLinesForBracesInMethods": false, 9 | "NewLinesForBracesInProperties": false, 10 | "NewLinesForBracesInAccessors": false, 11 | "NewLineForElse": false, 12 | "NewLineForCatch": false, 13 | "NewLineForFinally": false 14 | } 15 | } -------------------------------------------------------------------------------- /src/DynamicQueryable/DynamicQueryable.Compare.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | // ReSharper disable once CheckNamespace 4 | namespace System.Linq.Dynamic; 5 | 6 | public static partial class DynamicQueryable { 7 | 8 | public static IQueryable Except(this IQueryable source, IEnumerable items) 9 | => HandleConstant(source, "Except", items); 10 | 11 | public static IQueryable Intersect(this IQueryable source, IEnumerable items) 12 | => HandleConstant(source, "Intersect", items); 13 | 14 | public static IQueryable Union(this IQueryable source, IEnumerable items) 15 | => HandleConstant(source, "Union", items); 16 | 17 | public static IQueryable Concat(this IQueryable source, IEnumerable items) 18 | => HandleConstant(source, "Concat", items); 19 | } 20 | -------------------------------------------------------------------------------- /src/DynamicQueryable/DynamicQueryable.Alter.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable once CheckNamespace 2 | namespace System.Linq.Dynamic; 3 | 4 | public static partial class DynamicQueryable { 5 | 6 | public static IQueryable Take(this IQueryable source, int count) 7 | => HandleConstant(source, "Take", count); 8 | 9 | public static IQueryable Skip(this IQueryable source, int count) 10 | => HandleConstant(source, "Skip", count); 11 | 12 | public static IQueryable Distinct(this IQueryable source) 13 | => Handle(source, "Distinct"); 14 | 15 | public static IQueryable Reverse(this IQueryable source) 16 | => Handle(source, "Reverse"); 17 | 18 | public static IQueryable DefaultIfEmpty(this IQueryable source) 19 | => Handle(source, "DefaultIfEmpty"); 20 | 21 | public static IQueryable DefaultIfEmpty(this IQueryable source, object defaultValue) 22 | => HandleConstant(source, "DefaultIfEmpty", defaultValue); 23 | } 24 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How to contribute to DynamicQueryable 2 | 3 | #### **Did you find a bug?** 4 | * Go on, open an [Issue](https://github.com/umutozel/DynamicQueryable/issues/new). 5 | 6 | * *Ensure the bug was not already reported* by searching on GitHub under [Issues](https://github.com/umutozel/DynamicQueryable/issues). 7 | 8 | #### **Did you write a patch that fixes a bug?** 9 | 10 | * Open a new GitHub pull request with the patch. 11 | 12 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. 13 | 14 | #### **Did you fix whitespace, format code, or make a purely cosmetic patch?** 15 | 16 | Unless we really loved the style, or we missed something out of the norm and you catched it, we won't accept the pull request, sorry! 17 | 18 | #### **Do you have questions about the source code?** 19 | 20 | [Be a good open source citizen!](https://hackernoon.com/being-a-good-open-source-citizen-9060d0ab9732) 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Change list 2 | 3 | *Please provide briefly described change list which are you going to propose.* 4 | 5 | ## Types of changes 6 | 7 | What types of changes are you proposing/introducing? 8 | _Put an `x` in the boxes that apply_ 9 | 10 | - [ ] Bugfix (non-breaking change which fixes an issue) 11 | - [ ] New feature (non-breaking change which adds functionality) 12 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 13 | 14 | ## Details 15 | 16 | Please provide more details about changes if it is necessary. If there are new features you can provide code samples which show the way they 17 | work and possible use cases. Also you can create [gists](https://gist.github.com) with pasted C# code samples or put them here using markdown. 18 | About markdown please read [Mastering markdown](https://guides.github.com/features/mastering-markdown/) and [Writing on GitHub](https://help.github.com/categories/writing-on-github/) 19 | -------------------------------------------------------------------------------- /src/DynamicQueryable/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("DynamicQueryable - Construct Linq queries using strings.")] 8 | [assembly: AssemblyDescription("Construct Linq queries using strings.")] 9 | #if DEBUG 10 | [assembly: AssemblyConfiguration("Debug")] 11 | #else 12 | [assembly: AssemblyConfiguration("Release")] 13 | #endif 14 | [assembly: AssemblyCompany("Umut Özel")] 15 | [assembly: AssemblyProduct("DynamicQueryable")] 16 | [assembly: AssemblyCopyright("Copyright © 2018")] 17 | [assembly: AssemblyTrademark("")] 18 | [assembly: AssemblyCulture("")] 19 | 20 | // Setting ComVisible to false makes the types in this assembly not visible 21 | // to COM components. If you need to access a type in this assembly from 22 | // COM, set the ComVisible attribute to true on that type. 23 | [assembly: ComVisible(false)] 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Umut Ozel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/DynamicQueryable/DynamicQueryable.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 2.1.0 6 | false 7 | Umut Özel 8 | Construct Linq queries using strings. 9 | Copyright (c) 2018 10 | MIT 11 | https://github.com/umutozel/DynamicQueryable 12 | icon.png 13 | csharp expression linq string dynamic iqueryable 14 | https://github.com/umutozel/DynamicQueryable 15 | git 16 | 13 17 | enable 18 | full 19 | True 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /test/DynamicQueryable.Tests/DynamicQueryable.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | false 6 | 13 7 | enable 8 | 9 | 10 | 11 | 12 | all 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | 15 | 16 | 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/DynamicQueryable/DynamicQueryable.Count.cs: -------------------------------------------------------------------------------- 1 | using Jokenizer.Net; 2 | using VarType = System.Collections.Generic.IDictionary; 3 | 4 | // ReSharper disable once CheckNamespace 5 | namespace System.Linq.Dynamic; 6 | 7 | public static partial class DynamicQueryable { 8 | 9 | public static int Count(this IQueryable source, string? predicate = null, params object[] values) 10 | => Count(source, predicate, null, null, values); 11 | 12 | public static int Count(this IQueryable source, string predicate, Settings settings, params object[] values) 13 | => Count(source, predicate, null, settings, values); 14 | 15 | public static int Count(this IQueryable source, string predicate, VarType variables, params object[] values) 16 | => Count(source, predicate, variables, null, values); 17 | 18 | public static int Count(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 19 | => (int)ExecuteOptionalExpression(source, "Count", predicate, string.IsNullOrEmpty(predicate), variables, values, settings)!; 20 | 21 | public static long LongCount(this IQueryable source, string? predicate = null, params object[] values) 22 | => LongCount(source, predicate, null, null, values); 23 | 24 | public static long LongCount(this IQueryable source, string predicate, Settings settings, params object[] values) 25 | => LongCount(source, predicate, null, settings, values); 26 | 27 | public static long LongCount(this IQueryable source, string predicate, VarType variables, params object[] values) 28 | => LongCount(source, predicate, variables, null, values); 29 | 30 | public static long LongCount(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 31 | => (long)ExecuteOptionalExpression(source, "LongCount", predicate, string.IsNullOrEmpty(predicate), variables, values, settings)!; 32 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM jupyter/scipy-notebook:7a0c7325e470 2 | 3 | # .NET SDK versiyonunu ayarla 4 | ENV DOTNET_SDK_VERSION 9.0.200 5 | 6 | # Kullanıcı ve UID ayarları 7 | ARG NB_USER=jovyan 8 | ARG NB_UID=1000 9 | ENV USER ${NB_USER} 10 | ENV NB_UID ${NB_UID} 11 | ENV HOME /home/${NB_USER} 12 | 13 | WORKDIR ${HOME} 14 | 15 | USER root 16 | RUN apt-get update && apt-get install -y curl 17 | 18 | # Install .NET CLI dependencies 19 | RUN apt-get install -y --no-install-recommends \ 20 | libc6 \ 21 | libgcc1 \ 22 | libgssapi-krb5-2 \ 23 | libicu60 \ 24 | libssl1.1 \ 25 | libstdc++6 \ 26 | zlib1g 27 | 28 | RUN rm -rf /var/lib/apt/lists/* 29 | 30 | # Install .NET Core SDK 31 | RUN curl -SL --output dotnet.tar.gz https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$DOTNET_SDK_VERSION/dotnet-sdk-$DOTNET_SDK_VERSION-linux-x64.tar.gz \ 32 | && mkdir -p /usr/share/dotnet \ 33 | && tar -zxf dotnet.tar.gz -C /usr/share/dotnet \ 34 | && rm dotnet.tar.gz \ 35 | && ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet 36 | 37 | # PATH güncellemesi 38 | ENV PATH="${PATH}:/root/.dotnet/tools" 39 | 40 | # .NET Interactive'i yükle 41 | RUN dotnet tool install -g Microsoft.dotnet-interactive 42 | RUN dotnet tool install -g Microsoft.dotnet-try 43 | 44 | # Jupyter ile etkileşimi sağlamak için kernel kurulumunu yap 45 | RUN dotnet interactive jupyter install 46 | 47 | # Global araçlar için PATH güncellemesi 48 | ENV PATH="${PATH}:/root/.dotnet/tools" 49 | 50 | # Notebooks dosyalarını kopyala 51 | COPY ./notebooks/ ${HOME}/notebooks/ 52 | 53 | # NuGet kaynaklarını kopyala (eğer varsa) 54 | COPY ./NuGet.config ${HOME}/nuget.config 55 | 56 | # Kullanıcı sahipliğini ayarla 57 | RUN chown -R ${NB_UID} ${HOME} 58 | 59 | # Jupyter'ı kullanıcı olarak çalıştır 60 | USER ${USER} 61 | 62 | # Notebook dizinine geç 63 | WORKDIR ${HOME}/notebooks/ 64 | 65 | # Varsayılan Jupyter portunu aç 66 | EXPOSE 8888 67 | 68 | # Jupyter'i başlat 69 | CMD ["start-notebook.sh"] 70 | -------------------------------------------------------------------------------- /src/DynamicQueryable/DynamicQueryable.Look.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Jokenizer.Net; 3 | using VarType = System.Collections.Generic.IDictionary; 4 | 5 | // ReSharper disable once CheckNamespace 6 | namespace System.Linq.Dynamic; 7 | 8 | public static partial class DynamicQueryable { 9 | 10 | public static bool All(this IQueryable source, string? predicate = null, params object[] values) 11 | => All(source, predicate, null, null, values); 12 | 13 | public static bool All(this IQueryable source, string predicate, Settings settings, params object[] values) 14 | => All(source, predicate, null, settings, values); 15 | 16 | public static bool All(this IQueryable source, string predicate, VarType variables, params object[] values) 17 | => All(source, predicate, variables, null, values); 18 | 19 | public static bool All(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 20 | => (bool)ExecuteLambda(source, "All", predicate, false, variables, values, settings)!; 21 | 22 | public static bool Any(this IQueryable source, string? predicate = null, params object[] values) 23 | => Any(source, predicate, null, null, values); 24 | 25 | public static bool Any(this IQueryable source, string predicate, Settings settings, params object[] values) 26 | => Any(source, predicate, null, settings, values); 27 | 28 | public static bool Any(this IQueryable source, string predicate, VarType variables, params object[] values) 29 | => Any(source, predicate, variables, null, values); 30 | 31 | public static bool Any(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 32 | => (bool)ExecuteOptionalExpression(source, "Any", predicate, string.IsNullOrEmpty(predicate), variables, values, settings)!; 33 | 34 | public static bool Contains(this IQueryable source, object item) 35 | => (bool)ExecuteConstant(source, "Contains", item)!; 36 | 37 | public static bool SequenceEqual(this IQueryable source, IEnumerable items) 38 | => (bool)ExecuteConstant(source, "SequenceEqual", items)!; 39 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DynamicQueryable 2 | Construct Linq queries using strings. 3 | 4 | [![Build and Test](https://github.com/umutozel/DynamicQueryable/actions/workflows/build.yml/badge.svg)](https://github.com/umutozel/DynamicQueryable/actions/workflows/build.yml) 5 | [![codecov](https://codecov.io/gh/umutozel/DynamicQueryable/graph/badge.svg?token=5A9hHTDVFc)](https://codecov.io/gh/umutozel/DynamicQueryable) 6 | [![NuGet Badge](https://img.shields.io/nuget/v/DynamicQueryable.svg)](https://www.nuget.org/packages/DynamicQueryable/) 7 | ![NuGet Downloads](https://img.shields.io/nuget/dt/DynamicQueryable.svg) 8 | [![GitHub issues](https://img.shields.io/github/issues/umutozel/DynamicQueryable.svg)](https://github.com/umutozel/DynamicQueryable/issues) 9 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/umutozel/DynamicQueryable/master/LICENSE) 10 | 11 | [![GitHub stars](https://img.shields.io/github/stars/umutozel/DynamicQueryable.svg?style=social&label=Star)](https://github.com/umutozel/DynamicQueryable) 12 | [![GitHub forks](https://img.shields.io/github/forks/umutozel/DynamicQueryable.svg?style=social&label=Fork)](https://github.com/umutozel/DynamicQueryable) 13 | 14 | Play with **jupyter notebook readme** on: 15 | 16 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/umutozel/DynamicQueryable/master) 17 | 18 | # Installation 19 | 20 | #### Package Manager 21 | ``` 22 | Install-Package DynamicQueryable 23 | ``` 24 | #### .Net CLI 25 | ``` 26 | dotnet add package DynamicQueryable 27 | ``` 28 | 29 | # Getting Started 30 | 31 | Start with adding System.Linq.Dynamic namespace to usings. 32 | 33 | ```csharp 34 | // you can use inline values 35 | query.Where("o => o.Id > 5").ToList(); 36 | // or you can pass ordered values, @0 will be replaced with first argument 37 | query.Where("o => o.Id > @0", 5).ToList(); 38 | // or you can use named variables, AvgId will be replaced with value from given dictionary 39 | query.Where("o => o.Id > AvgId", new Dictionary { { "AvgId", AvgId } }).ToList(); 40 | ``` 41 | 42 | # Supported Methods 43 | Aggregate, All, Any, Average, Concat, Contains, Count, 44 | DefaultIfEmpty, Distinct, Except, ElementAt, ElementAtOrDefault, 45 | First, FirstOrDefault, GroupBy, GroupJoin, Intersect, Join, 46 | Last, LastOrDefault, LongCount, Max, Min, OrderBy, OrderByDescending, 47 | Reverse, Select, SelectMany, SequenceEqual, Single, SingleOrDefault, 48 | Skip, SkipWhile, Sum, Take, TakeWhile, ThenBy, ThenByDescending, Union, Where, Zip 49 | 50 | # License 51 | DynamicQueryable is under the [MIT License](LICENSE). 52 | 53 | drawing Thanks to [JetBrains](https://www.jetbrains.com/) for providing me with free licenses to their great tools. 54 | -------------------------------------------------------------------------------- /src/DynamicQueryable/DynamicQueryable.Select.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | using Jokenizer.Net; 4 | using VarType = System.Collections.Generic.IDictionary; 5 | 6 | // ReSharper disable once CheckNamespace 7 | namespace System.Linq.Dynamic; 8 | 9 | public static partial class DynamicQueryable { 10 | 11 | public static IQueryable Select(this IQueryable source, string selector, params object[] values) 12 | => Select(source, selector, null, null, values); 13 | 14 | public static IQueryable Select(this IQueryable source, string selector, Settings settings, params object[] values) 15 | => Select(source, selector, null, settings, values); 16 | 17 | public static IQueryable Select(this IQueryable source, string selector, VarType variables, params object[] values) 18 | => Select(source, selector, variables, null, values); 19 | 20 | public static IQueryable Select(this IQueryable source, string selector, VarType? variables, Settings? settings, params object[] values) 21 | => HandleLambda(source, "Select", selector, true, variables, values, settings); 22 | 23 | public static IQueryable SelectMany(this IQueryable source, string selector, params object[] values) 24 | => SelectMany(source, selector, null, null, values); 25 | 26 | public static IQueryable SelectMany(this IQueryable source, string selector, Settings settings, params object[] values) 27 | => SelectMany(source, selector, null, settings, values); 28 | 29 | public static IQueryable SelectMany(this IQueryable source, string selector, VarType variables, params object[] values) 30 | => SelectMany(source, selector, variables, null, values); 31 | 32 | public static IQueryable SelectMany(this IQueryable source, string selector, VarType? variables, Settings? settings, params object[] values) { 33 | if (source == null) throw new ArgumentNullException(nameof(source)); 34 | if (string.IsNullOrWhiteSpace(selector)) throw new ArgumentNullException(nameof(selector)); 35 | 36 | var lambda = Evaluator.ToLambda(selector, [source.ElementType], variables, settings, values); 37 | 38 | // Fix lambda by recreating to be of correct Func<> type in case 39 | // the expression parsed to something other than IEnumerable. 40 | // For instance, an expression evaluating to List would result 41 | // in a lambda of type Func> when we need one of type 42 | // a Func in order to call SelectMany(). 43 | var inputType = source.Expression.Type.GetGenericArguments()[0]; 44 | var resultType = lambda.Body.Type.GetGenericArguments()[0]; 45 | var enumerableType = typeof(IEnumerable<>).MakeGenericType(resultType); 46 | var delegateType = typeof(Func<,>).MakeGenericType(inputType, enumerableType); 47 | lambda = Expression.Lambda(delegateType, lambda.Body, lambda.Parameters); 48 | 49 | return source.Provider.CreateQuery( 50 | Expression.Call( 51 | typeof(Queryable), 52 | "SelectMany", 53 | [source.ElementType, resultType], 54 | source.Expression, 55 | Expression.Quote(lambda) 56 | ) 57 | ); 58 | } 59 | } -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'src/**' 9 | - 'test/**' 10 | tags: 11 | - 'v*' 12 | 13 | pull_request: 14 | branches: 15 | - main 16 | workflow_dispatch: 17 | 18 | jobs: 19 | build: 20 | runs-on: windows-latest 21 | env: 22 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 23 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 24 | NUGET_XMLDOC_MODE: skip 25 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 26 | 27 | steps: 28 | - name: Checkout repository 29 | uses: actions/checkout@v4 30 | with: 31 | fetch-depth: 0 32 | 33 | - name: Setup .NET 9.0 34 | uses: actions/setup-dotnet@v4 35 | with: 36 | dotnet-version: '9.0' 37 | 38 | - name: Update .csproj version 39 | if: startsWith(github.ref, 'refs/tags/v') 40 | run: | 41 | $VERSION = $env:GITHUB_REF -replace 'refs/tags/v', '' 42 | echo "VERSION=$VERSION" >> $GITHUB_ENV 43 | echo "Updating .csproj version to: $VERSION" 44 | 45 | [xml]$xml = Get-Content src\DynamicQueryable\DynamicQueryable.csproj 46 | $propertyGroup = $xml.Project.PropertyGroup | Where-Object { $_.Version } 47 | 48 | if ($propertyGroup) { 49 | echo "Existing found. Updating..." 50 | $propertyGroup.Version = $VERSION 51 | } else { 52 | echo "No existing tag. Creating new one..." 53 | $newPropertyGroup = $xml.CreateElement("PropertyGroup") 54 | $versionElement = $xml.CreateElement("Version") 55 | $versionElement.InnerText = $VERSION 56 | $newPropertyGroup.AppendChild($versionElement) | Out-Null 57 | $xml.Project.AppendChild($newPropertyGroup) | Out-Null 58 | } 59 | 60 | $xml.Save("src/DynamicQueryable/DynamicQueryable.csproj") 61 | 62 | echo "Updated .csproj version:" 63 | Get-Content src/DynamicQueryable/DynamicQueryable.csproj 64 | shell: pwsh 65 | 66 | - name: Restore dependencies 67 | run: dotnet restore 68 | 69 | - name: Build project 70 | run: dotnet build --configuration Release --no-restore 71 | 72 | - name: Run tests with coverage 73 | run: | 74 | dotnet tool install --global coverlet.console 75 | coverlet test/DynamicQueryable.Tests/bin/Release/net9.0/DynamicQueryable.Tests.dll --target "dotnet" --targetargs "test test/DynamicQueryable.Tests/DynamicQueryable.Tests.csproj --no-build -c Release" --format opencover --output opencoverCoverage.xml 76 | 77 | - name: Upload coverage to Codecov 78 | run: | 79 | curl -s https://codecov.io/bash > codecov 80 | bash codecov -f opencoverCoverage.xml -t ${{ secrets.CODECOV_TOKEN }} 81 | 82 | - name: Pack NuGet package 83 | if: startsWith(github.ref, 'refs/tags/v') 84 | run: dotnet pack src\DynamicQueryable\DynamicQueryable.csproj --configuration Release --output "${{ github.workspace }}\packages" 85 | 86 | - name: Publish NuGet package (on tag push) 87 | if: startsWith(github.ref, 'refs/tags/v') 88 | run: dotnet nuget push "${{ github.workspace }}\packages\DynamicQueryable.*.nupkg" --api-key "${{ secrets.NUGET_API_KEY }}" --source "https://api.nuget.org/v3/index.json" --skip-duplicate 89 | -------------------------------------------------------------------------------- /src/DynamicQueryable/DynamicQueryable.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | using Jokenizer.Net; 3 | using VarType = System.Collections.Generic.IDictionary; 4 | 5 | // ReSharper disable once CheckNamespace 6 | namespace System.Linq.Dynamic; 7 | 8 | public static partial class DynamicQueryable { 9 | 10 | private static Expression CreateLambda(IQueryable source, string method, string? expression, bool generic, VarType? variables, object[] values, Settings? settings) { 11 | if (source == null) throw new ArgumentNullException(nameof(source)); 12 | if (string.IsNullOrWhiteSpace(expression)) throw new ArgumentNullException(nameof(expression)); 13 | 14 | var types = new[] { source.ElementType }; 15 | var lambda = Evaluator.ToLambda(expression!, types, variables, settings, values); 16 | 17 | return Expression.Call( 18 | typeof(Queryable), 19 | method, 20 | generic ? [source.ElementType, lambda.Body.Type] : types, 21 | source.Expression, 22 | Expression.Quote(lambda) 23 | ); 24 | } 25 | 26 | private static Expression CreateExpression(IQueryable source, string method, params Expression[] expressions) { 27 | if (source == null) throw new ArgumentNullException(nameof(source)); 28 | 29 | return Expression.Call( 30 | typeof(Queryable), 31 | method, 32 | [source.ElementType], 33 | new[] { source.Expression }.Concat(expressions).ToArray() 34 | ); 35 | } 36 | 37 | private static IQueryable Handle(IQueryable source, string method) { 38 | var expression = CreateExpression(source, method); 39 | return source.Provider.CreateQuery(expression); 40 | } 41 | 42 | private static IQueryable HandleConstant(IQueryable source, string method, object value) { 43 | var expression = CreateExpression(source, method, Expression.Constant(value)); 44 | return source.Provider.CreateQuery(expression); 45 | } 46 | 47 | private static IQueryable HandleLambda(IQueryable source, string method, string? expression, bool generic, VarType? variables, object[] values, Settings? settings) { 48 | var lambda = CreateLambda(source, method, expression, generic, variables, values, settings); 49 | return source.Provider.CreateQuery(lambda); 50 | } 51 | 52 | private static object? Execute(IQueryable source, string method) { 53 | var expression = CreateExpression(source, method); 54 | return source.Provider.Execute(expression); 55 | } 56 | 57 | private static object? ExecuteLambda(IQueryable source, string method, string? expression, bool generic, VarType? variables, object[] values, Settings? settings) { 58 | var lambda = CreateLambda(source, method, expression, generic, variables, values, settings); 59 | return source.Provider.Execute(lambda); 60 | } 61 | 62 | private static object? ExecuteOptionalExpression(IQueryable source, string method, string? expression, bool generic, VarType? variables, object[] values, Settings? settings) 63 | => string.IsNullOrEmpty(expression) 64 | ? Execute(source, method) 65 | : ExecuteLambda(source, method, expression, generic, variables, values, settings); 66 | 67 | private static object? ExecuteConstant(IQueryable source, string method, object value) { 68 | var expression = CreateExpression(source, method, Expression.Constant(value)); 69 | return source.Provider.Execute(expression); 70 | } 71 | } -------------------------------------------------------------------------------- /DynamicQueryable.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26124.0 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{182DC45A-4067-4F0F-BCE3-EC825ECA8882}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicQueryable", "src\DynamicQueryable\DynamicQueryable.csproj", "{6AB5F252-361B-4072-B35A-907F89B91B18}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{EDD569A8-8291-4C1B-BC41-3B745A24511C}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicQueryable.Tests", "test\DynamicQueryable.Tests\DynamicQueryable.Tests.csproj", "{E582F1CC-D746-418F-BEA5-399DBBE92F97}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Debug|x64 = Debug|x64 18 | Debug|x86 = Debug|x86 19 | Release|Any CPU = Release|Any CPU 20 | Release|x64 = Release|x64 21 | Release|x86 = Release|x86 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 27 | {6AB5F252-361B-4072-B35A-907F89B91B18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {6AB5F252-361B-4072-B35A-907F89B91B18}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {6AB5F252-361B-4072-B35A-907F89B91B18}.Debug|x64.ActiveCfg = Debug|Any CPU 30 | {6AB5F252-361B-4072-B35A-907F89B91B18}.Debug|x64.Build.0 = Debug|Any CPU 31 | {6AB5F252-361B-4072-B35A-907F89B91B18}.Debug|x86.ActiveCfg = Debug|Any CPU 32 | {6AB5F252-361B-4072-B35A-907F89B91B18}.Debug|x86.Build.0 = Debug|Any CPU 33 | {6AB5F252-361B-4072-B35A-907F89B91B18}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {6AB5F252-361B-4072-B35A-907F89B91B18}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {6AB5F252-361B-4072-B35A-907F89B91B18}.Release|x64.ActiveCfg = Release|Any CPU 36 | {6AB5F252-361B-4072-B35A-907F89B91B18}.Release|x64.Build.0 = Release|Any CPU 37 | {6AB5F252-361B-4072-B35A-907F89B91B18}.Release|x86.ActiveCfg = Release|Any CPU 38 | {6AB5F252-361B-4072-B35A-907F89B91B18}.Release|x86.Build.0 = Release|Any CPU 39 | {E582F1CC-D746-418F-BEA5-399DBBE92F97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {E582F1CC-D746-418F-BEA5-399DBBE92F97}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {E582F1CC-D746-418F-BEA5-399DBBE92F97}.Debug|x64.ActiveCfg = Debug|Any CPU 42 | {E582F1CC-D746-418F-BEA5-399DBBE92F97}.Debug|x64.Build.0 = Debug|Any CPU 43 | {E582F1CC-D746-418F-BEA5-399DBBE92F97}.Debug|x86.ActiveCfg = Debug|Any CPU 44 | {E582F1CC-D746-418F-BEA5-399DBBE92F97}.Debug|x86.Build.0 = Debug|Any CPU 45 | {E582F1CC-D746-418F-BEA5-399DBBE92F97}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {E582F1CC-D746-418F-BEA5-399DBBE92F97}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {E582F1CC-D746-418F-BEA5-399DBBE92F97}.Release|x64.ActiveCfg = Release|Any CPU 48 | {E582F1CC-D746-418F-BEA5-399DBBE92F97}.Release|x64.Build.0 = Release|Any CPU 49 | {E582F1CC-D746-418F-BEA5-399DBBE92F97}.Release|x86.ActiveCfg = Release|Any CPU 50 | {E582F1CC-D746-418F-BEA5-399DBBE92F97}.Release|x86.Build.0 = Release|Any CPU 51 | EndGlobalSection 52 | GlobalSection(NestedProjects) = preSolution 53 | {6AB5F252-361B-4072-B35A-907F89B91B18} = {182DC45A-4067-4F0F-BCE3-EC825ECA8882} 54 | {E582F1CC-D746-418F-BEA5-399DBBE92F97} = {EDD569A8-8291-4C1B-BC41-3B745A24511C} 55 | EndGlobalSection 56 | EndGlobal 57 | -------------------------------------------------------------------------------- /notebooks/README.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "data": { 10 | "text/html": [ 11 | "Installing package DynamicQueryable..............done!" 12 | ] 13 | }, 14 | "metadata": {}, 15 | "output_type": "display_data" 16 | }, 17 | { 18 | "data": { 19 | "text/html": [ 20 | "Successfully added reference to package DynamicQueryable, version 2.0.25" 21 | ] 22 | }, 23 | "metadata": {}, 24 | "output_type": "display_data" 25 | } 26 | ], 27 | "source": [ 28 | "#r \"nuget:DynamicQueryable,2.*\"\n", 29 | "\n", 30 | "using System.Linq;\n", 31 | "using System.Linq.Dynamic;" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": null, 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "public class Order {\n", 41 | " public int Id { get; set; }\n", 42 | " public double Price;\n", 43 | "}\n", 44 | "\n", 45 | "var orders = new List {\n", 46 | " new Order { Id = 1, Price = 42 },\n", 47 | " new Order { Id = 2, Price = 81 },\n", 48 | " new Order { Id = 3, Price = 34 },\n", 49 | " new Order { Id = 4, Price = 117 },\n", 50 | " new Order { Id = 5, Price = 289 },\n", 51 | " new Order { Id = 6, Price = 3 }\n", 52 | "};\n", 53 | "\n", 54 | "var query = orders.AsQueryable();" 55 | ] 56 | }, 57 | { 58 | "cell_type": "markdown", 59 | "metadata": {}, 60 | "source": [ 61 | "**You can use inline values**" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "var r1 = query.Where(\"o => o.Price > 100\").Take(1).ToList();\n", 71 | "Console.WriteLine(string.Join(\", \", r1.Select(o => o.Id)));" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "**Or you can pass ordered values, @0 will be replaced with first argument**" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "var r2 = query.Where(\"o => o.Price < @0\", 50).ToList();\n", 88 | "Console.WriteLine(string.Join(\", \", r2.Select(o => o.Id)));" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "**Or you can use named variables, AvgId will be replaced with value from given dictionary**" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": null, 101 | "metadata": {}, 102 | "outputs": [], 103 | "source": [ 104 | "var prms = new Dictionary { { \"avgId\", 3 } };\n", 105 | "var r3 = query.Where(\"o => o.Id > avgId\", prms).ToList();\n", 106 | "Console.WriteLine(string.Join(\", \", r3.Select(o => o.Id)));" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "## Supported Methods\n", 114 | "\n", 115 | "Aggregate, All, Any, Average, Concat, Contains, Count, DefaultIfEmpty, Distinct, Except, ElementAt, ElementAtOrDefault, First, FirstOrDefault, GroupBy, GroupJoin, Intersect, Join, Last, LastOrDefault, LongCount, Max, Min, OrderBy, OrderByDescending, Reverse, Select, SelectMany, SequenceEqual, Single, SingleOrDefault, Skip, SkipWhile, Sum, Take, TakeWhile, ThenBy, ThenByDescending, Union, Where, Zip" 116 | ] 117 | } 118 | ], 119 | "metadata": { 120 | "kernelspec": { 121 | "display_name": ".NET (C#)", 122 | "language": "C#", 123 | "name": ".net-csharp" 124 | }, 125 | "language_info": { 126 | "file_extension": ".cs", 127 | "mimetype": "text/x-csharp", 128 | "name": "C#", 129 | "pygments_lexer": "csharp", 130 | "version": "8.0" 131 | } 132 | }, 133 | "nbformat": 4, 134 | "nbformat_minor": 4 135 | } -------------------------------------------------------------------------------- /src/DynamicQueryable/DynamicQueryable.Filter.cs: -------------------------------------------------------------------------------- 1 | using Jokenizer.Net; 2 | using VarType = System.Collections.Generic.IDictionary; 3 | 4 | // ReSharper disable once CheckNamespace 5 | namespace System.Linq.Dynamic; 6 | 7 | public static partial class DynamicQueryable { 8 | 9 | public static IQueryable Where(this IQueryable source, string predicate, params object[] values) 10 | => Where(source, predicate, null, null, values); 11 | 12 | public static IQueryable Where(this IQueryable source, string predicate, Settings settings, params object[] values) 13 | => Where(source, predicate, null, settings, values); 14 | 15 | public static IQueryable Where(this IQueryable source, string predicate, VarType variables, params object[] values) 16 | => Where(source, predicate, variables, null, values); 17 | 18 | public static IQueryable Where(this IQueryable source, string predicate, VarType? variables, Settings? settings, params object[] values) 19 | => (IQueryable)Where((IQueryable)source, predicate, variables, settings, values); 20 | 21 | public static IQueryable Where(this IQueryable source, string predicate, params object[] values) 22 | => Where(source, predicate, null, null, values); 23 | 24 | public static IQueryable Where(this IQueryable source, string predicate, Settings settings, params object[] values) 25 | => Where(source, predicate, null, settings, values); 26 | 27 | public static IQueryable Where(this IQueryable source, string predicate, VarType variables, params object[] values) 28 | => Where(source, predicate, variables, null, values); 29 | 30 | public static IQueryable Where(this IQueryable source, string predicate, VarType? variables, Settings? settings, params object[] values) 31 | => HandleLambda(source, "Where", predicate, false, variables, values, settings); 32 | 33 | public static IQueryable SkipWhile(this IQueryable source, string predicate, params object[] values) 34 | => SkipWhile(source, predicate, null, null, values); 35 | 36 | public static IQueryable SkipWhile(this IQueryable source, string predicate, Settings settings, params object[] values) 37 | => SkipWhile(source, predicate, null, settings, values); 38 | 39 | public static IQueryable SkipWhile(this IQueryable source, string predicate, VarType variables, params object[] values) 40 | => SkipWhile(source, predicate, variables, null, values); 41 | 42 | public static IQueryable SkipWhile(this IQueryable source, string predicate, VarType? variables, Settings? settings, params object[] values) 43 | => (IQueryable)SkipWhile((IQueryable)source, predicate, variables, settings, values); 44 | 45 | public static IQueryable SkipWhile(this IQueryable source, string predicate, params object[] values) 46 | => SkipWhile(source, predicate, null, null, values); 47 | 48 | public static IQueryable SkipWhile(this IQueryable source, string predicate, Settings settings, params object[] values) 49 | => SkipWhile(source, predicate, null, settings, values); 50 | 51 | public static IQueryable SkipWhile(this IQueryable source, string predicate, VarType variables, params object[] values) 52 | => SkipWhile(source, predicate, variables, null, values); 53 | 54 | public static IQueryable SkipWhile(this IQueryable source, string predicate, VarType? variables, Settings? settings, params object[] values) 55 | => HandleLambda(source, "SkipWhile", predicate, false, variables, values, settings); 56 | 57 | public static IQueryable TakeWhile(this IQueryable source, string predicate, params object[] values) 58 | => TakeWhile(source, predicate, null, null, values); 59 | 60 | public static IQueryable TakeWhile(this IQueryable source, string predicate, Settings settings, params object[] values) 61 | => TakeWhile(source, predicate, null, settings, values); 62 | 63 | public static IQueryable TakeWhile(this IQueryable source, string predicate, VarType variables, params object[] values) 64 | => TakeWhile(source, predicate, variables, null, values); 65 | 66 | public static IQueryable TakeWhile(this IQueryable source, string predicate, VarType? variables, Settings? settings, params object[] values) 67 | => (IQueryable)TakeWhile((IQueryable)source, predicate, variables, settings, values); 68 | 69 | public static IQueryable TakeWhile(this IQueryable source, string predicate, params object[] values) 70 | => TakeWhile(source, predicate, null, null, values); 71 | 72 | public static IQueryable TakeWhile(this IQueryable source, string predicate, Settings settings, params object[] values) 73 | => TakeWhile(source, predicate, null, settings, values); 74 | 75 | public static IQueryable TakeWhile(this IQueryable source, string predicate, VarType variables, params object[] values) 76 | => TakeWhile(source, predicate, variables, null, values); 77 | 78 | public static IQueryable TakeWhile(this IQueryable source, string predicate, VarType? variables, Settings? settings, params object[] values) 79 | => HandleLambda(source, "TakeWhile", predicate, false, variables, values, settings); 80 | } -------------------------------------------------------------------------------- /src/DynamicQueryable/DynamicQueryable.Group.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | using Jokenizer.Net; 4 | using VarType = System.Collections.Generic.IDictionary; 5 | 6 | // ReSharper disable once CheckNamespace 7 | namespace System.Linq.Dynamic; 8 | 9 | public static partial class DynamicQueryable { 10 | 11 | public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, string resultSelector, params object[] values) 12 | => GroupBy(source, keySelector, elementSelector, resultSelector, null, null, values); 13 | 14 | public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, string resultSelector, Settings settings, params object[] values) 15 | => GroupBy(source, keySelector, elementSelector, resultSelector, null, settings, values); 16 | 17 | public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, string resultSelector, VarType variables, params object[] values) 18 | => GroupBy(source, keySelector, elementSelector, resultSelector, variables, null, values); 19 | 20 | public static IQueryable GroupBy(this IQueryable source, string keySelector, string elementSelector, string resultSelector, VarType? variables, Settings? settings, params object[] values) { 21 | if (source == null) throw new ArgumentNullException(nameof(source)); 22 | if (string.IsNullOrWhiteSpace(keySelector)) throw new ArgumentNullException(nameof(keySelector)); 23 | if (string.IsNullOrWhiteSpace(elementSelector)) throw new ArgumentNullException(nameof(elementSelector)); 24 | if (string.IsNullOrWhiteSpace(resultSelector)) throw new ArgumentNullException(nameof(resultSelector)); 25 | 26 | var keyLambda = Evaluator.ToLambda(keySelector, [source.ElementType], variables, settings, values); 27 | var elementLambda = Evaluator.ToLambda(elementSelector, [source.ElementType], variables, settings, values); 28 | var enumElementType = typeof(IEnumerable<>).MakeGenericType(elementLambda.Body.Type); 29 | var resultLambda = Evaluator.ToLambda(resultSelector, [keyLambda.Body.Type, enumElementType], variables, settings, values); 30 | 31 | return source.Provider.CreateQuery( 32 | Expression.Call( 33 | typeof(Queryable), 34 | "GroupBy", 35 | [source.ElementType, keyLambda.Body.Type, elementLambda.Body.Type, resultLambda.Body.Type], 36 | source.Expression, 37 | Expression.Quote(keyLambda), 38 | Expression.Quote(elementLambda), 39 | Expression.Quote(resultLambda) 40 | ) 41 | ); 42 | } 43 | 44 | public static IQueryable GroupBy(this IQueryable source, string keySelector, string resultSelector, params object[] values) 45 | => GroupBy(source, keySelector, resultSelector, (VarType?)null, null, values); 46 | 47 | public static IQueryable GroupBy(this IQueryable source, string keySelector, string resultSelector, Settings settings, params object[] values) 48 | => GroupBy(source, keySelector, resultSelector, (VarType?)null, settings, values); 49 | 50 | public static IQueryable GroupBy(this IQueryable source, string keySelector, string resultSelector, VarType variables, params object[] values) 51 | => GroupBy(source, keySelector, resultSelector, variables, null, values); 52 | 53 | public static IQueryable GroupBy(this IQueryable source, string keySelector, string resultSelector, VarType? variables, Settings? settings, params object[] values) { 54 | if (source == null) throw new ArgumentNullException(nameof(source)); 55 | if (string.IsNullOrWhiteSpace(keySelector)) throw new ArgumentNullException(nameof(keySelector)); 56 | if (string.IsNullOrWhiteSpace(resultSelector)) throw new ArgumentNullException(nameof(resultSelector)); 57 | 58 | var keyLambda = Evaluator.ToLambda(keySelector, [source.ElementType], variables, settings, values); 59 | var enumSourceType = typeof(IEnumerable<>).MakeGenericType(source.ElementType); 60 | var resultLambda = Evaluator.ToLambda(resultSelector, [keyLambda.Body.Type, enumSourceType], variables, settings, values); 61 | 62 | return source.Provider.CreateQuery( 63 | Expression.Call( 64 | typeof(Queryable), 65 | "GroupBy", 66 | [source.ElementType, keyLambda.Body.Type, resultLambda.Body.Type], 67 | source.Expression, 68 | Expression.Quote(keyLambda), 69 | Expression.Quote(resultLambda) 70 | ) 71 | ); 72 | } 73 | 74 | public static IQueryable GroupBy(this IQueryable source, string keySelector, params object[] values) 75 | => GroupBy(source, keySelector, (VarType?)null, null, values); 76 | 77 | public static IQueryable GroupBy(this IQueryable source, string keySelector, Settings settings, params object[] values) 78 | => GroupBy(source, keySelector, (VarType?)null, settings, values); 79 | 80 | public static IQueryable GroupBy(this IQueryable source, string keySelector, VarType variables, params object[] values) 81 | => GroupBy(source, keySelector, variables, null, values); 82 | 83 | public static IQueryable GroupBy(this IQueryable source, string keySelector, VarType? variables, Settings? settings, params object[] values) 84 | => HandleLambda(source, "GroupBy", keySelector, true, variables, values, settings); 85 | } -------------------------------------------------------------------------------- /src/DynamicQueryable/DynamicQueryable.Order.cs: -------------------------------------------------------------------------------- 1 | using Jokenizer.Net; 2 | using VarType = System.Collections.Generic.IDictionary; 3 | 4 | // ReSharper disable once CheckNamespace 5 | namespace System.Linq.Dynamic; 6 | 7 | public static partial class DynamicQueryable { 8 | 9 | public static IQueryable OrderBy(this IQueryable source, string selector, params object[] values) 10 | => OrderBy(source, selector, null, null, values); 11 | 12 | public static IQueryable OrderBy(this IQueryable source, string selector, Settings settings, params object[] values) 13 | => OrderBy(source, selector, null, settings, values); 14 | 15 | public static IQueryable OrderBy(this IQueryable source, string selector, VarType variables, params object[] values) 16 | => OrderBy(source, selector, variables, null, values); 17 | 18 | public static IQueryable OrderBy(this IQueryable source, string selector, VarType? variables, Settings? settings, params object[] values) 19 | => (IQueryable)OrderBy((IQueryable)source, selector, variables, settings, values); 20 | 21 | public static IQueryable OrderBy(this IQueryable source, string selector, params object[] values) 22 | => OrderBy(source, selector, null, null, values); 23 | 24 | public static IQueryable OrderBy(this IQueryable source, string selector, Settings settings, params object[] values) 25 | => OrderBy(source, selector, null, settings, values); 26 | 27 | public static IQueryable OrderBy(this IQueryable source, string selector, VarType variables, params object[] values) 28 | => OrderBy(source, selector, variables, null, values); 29 | 30 | public static IQueryable OrderBy(this IQueryable source, string selector, VarType? variables, Settings? settings, params object[] values) 31 | => HandleLambda(source, "OrderBy", selector, true, variables, values, settings); 32 | 33 | public static IQueryable OrderByDescending(this IQueryable source, string selector, params object[] values) 34 | => OrderByDescending(source, selector, null, null, values); 35 | 36 | public static IQueryable OrderByDescending(this IQueryable source, string selector, Settings settings, params object[] values) 37 | => OrderByDescending(source, selector, null, settings, values); 38 | 39 | public static IQueryable OrderByDescending(this IQueryable source, string selector, VarType variables, params object[] values) 40 | => OrderByDescending(source, selector, variables, null, values); 41 | 42 | public static IQueryable OrderByDescending(this IQueryable source, string selector, VarType? variables, Settings? settings, params object[] values) 43 | => (IQueryable)OrderByDescending((IQueryable)source, selector, variables, settings, values); 44 | 45 | public static IQueryable OrderByDescending(this IQueryable source, string selector, params object[] values) 46 | => OrderByDescending(source, selector, null, null, values); 47 | 48 | public static IQueryable OrderByDescending(this IQueryable source, string selector, Settings settings, params object[] values) 49 | => OrderByDescending(source, selector, null, settings, values); 50 | 51 | public static IQueryable OrderByDescending(this IQueryable source, string selector, VarType variables, params object[] values) 52 | => OrderByDescending(source, selector, variables, null, values); 53 | 54 | public static IQueryable OrderByDescending(this IQueryable source, string selector, VarType? variables, Settings? settings, params object[] values) 55 | => HandleLambda(source, "OrderByDescending", selector, true, variables, values, settings); 56 | 57 | public static IQueryable ThenBy(this IQueryable source, string selector, params object[] values) 58 | => ThenBy(source, selector, null, null, values); 59 | 60 | public static IQueryable ThenBy(this IQueryable source, string selector, Settings settings, params object[] values) 61 | => ThenBy(source, selector, null, settings, values); 62 | 63 | public static IQueryable ThenBy(this IQueryable source, string selector, VarType variables, params object[] values) 64 | => ThenBy(source, selector, variables, null, values); 65 | 66 | public static IQueryable ThenBy(this IQueryable source, string selector, VarType? variables, Settings? settings, params object[] values) 67 | => (IQueryable)ThenBy((IQueryable)source, selector, variables, settings, values); 68 | 69 | public static IQueryable ThenBy(this IQueryable source, string selector, params object[] values) 70 | => ThenBy(source, selector, null, null, values); 71 | 72 | public static IQueryable ThenBy(this IQueryable source, string selector, Settings settings, params object[] values) 73 | => ThenBy(source, selector, null, settings, values); 74 | 75 | public static IQueryable ThenBy(this IQueryable source, string selector, VarType variables, params object[] values) 76 | => ThenBy(source, selector, variables, null, values); 77 | 78 | public static IQueryable ThenBy(this IQueryable source, string selector, VarType? variables, Settings? settings, params object[] values) 79 | => HandleLambda(source, "ThenBy", selector, true, variables, values, settings); 80 | 81 | public static IQueryable ThenByDescending(this IQueryable source, string selector, params object[] values) 82 | => ThenByDescending(source, selector, null, null, values); 83 | 84 | public static IQueryable ThenByDescending(this IQueryable source, string selector, Settings settings, params object[] values) 85 | => ThenByDescending(source, selector, null, settings, values); 86 | 87 | public static IQueryable ThenByDescending(this IQueryable source, string selector, VarType variables, params object[] values) 88 | => ThenByDescending(source, selector, variables, null, values); 89 | 90 | public static IQueryable ThenByDescending(this IQueryable source, string selector, VarType? variables, Settings? settings, params object[] values) 91 | => (IQueryable)ThenByDescending((IQueryable)source, selector, variables, settings, values); 92 | 93 | public static IQueryable ThenByDescending(this IQueryable source, string selector, params object[] values) 94 | => ThenByDescending(source, selector, null, null, values); 95 | 96 | public static IQueryable ThenByDescending(this IQueryable source, string selector, Settings settings, params object[] values) 97 | => ThenByDescending(source, selector, null, settings, values); 98 | 99 | public static IQueryable ThenByDescending(this IQueryable source, string selector, VarType variables, params object[] values) 100 | => ThenByDescending(source, selector, variables, null, values); 101 | 102 | public static IQueryable ThenByDescending(this IQueryable source, string selector, VarType? variables, Settings? settings, params object[] values) 103 | => HandleLambda(source, "ThenByDescending", selector, true, variables, values, settings); 104 | } -------------------------------------------------------------------------------- /src/DynamicQueryable/DynamicQueryable.Join.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | using Jokenizer.Net; 4 | using VarType = System.Collections.Generic.IDictionary; 5 | 6 | // ReSharper disable once CheckNamespace 7 | namespace System.Linq.Dynamic; 8 | 9 | public static partial class DynamicQueryable { 10 | 11 | public static IQueryable Join(this IQueryable outer, IQueryable inner, string outerKeySelector, string innerKeySelector, string resultSelector, params object[] values) 12 | => Join(outer, inner, outerKeySelector, innerKeySelector, resultSelector, null, null, values); 13 | 14 | public static IQueryable Join(this IQueryable outer, IQueryable inner, string outerKeySelector, string innerKeySelector, string resultSelector, Settings settings, params object[] values) 15 | => Join(outer, inner, outerKeySelector, innerKeySelector, resultSelector, null, settings, values); 16 | 17 | public static IQueryable Join(this IQueryable outer, IQueryable inner, string outerKeySelector, string innerKeySelector, string resultSelector, VarType variables, params object[] values) 18 | => Join(outer, inner, outerKeySelector, innerKeySelector, resultSelector, variables, null, values); 19 | 20 | public static IQueryable Join(this IQueryable outer, IQueryable inner, string outerKeySelector, string innerKeySelector, string resultSelector, VarType? variables, Settings? settings, params object[] values) { 21 | if (outer == null) throw new ArgumentNullException(nameof(outer)); 22 | if (inner == null) throw new ArgumentNullException(nameof(inner)); 23 | if (string.IsNullOrWhiteSpace(outerKeySelector)) throw new ArgumentNullException(nameof(outerKeySelector)); 24 | if (string.IsNullOrWhiteSpace(innerKeySelector)) throw new ArgumentNullException(nameof(innerKeySelector)); 25 | if (string.IsNullOrWhiteSpace(resultSelector)) throw new ArgumentNullException(nameof(resultSelector)); 26 | 27 | var outerKeyLambda = Evaluator.ToLambda(outerKeySelector, [outer.ElementType], variables, settings, values); 28 | var innerKeyLambda = Evaluator.ToLambda(innerKeySelector, [inner.ElementType], variables, settings, values); 29 | var resultLambda = Evaluator.ToLambda(resultSelector, [outer.ElementType, inner.ElementType], variables, settings, values); 30 | 31 | return outer.Provider.CreateQuery( 32 | Expression.Call( 33 | typeof(Queryable), 34 | "Join", 35 | [outer.ElementType, inner.ElementType, outerKeyLambda.Body.Type, resultLambda.Body.Type], 36 | outer.Expression, 37 | Expression.Constant(inner), 38 | Expression.Quote(outerKeyLambda), 39 | Expression.Quote(innerKeyLambda), 40 | Expression.Quote(resultLambda) 41 | ) 42 | ); 43 | } 44 | 45 | public static IQueryable GroupJoin(this IQueryable outer, IQueryable inner, string outerKeySelector, string innerKeySelector, string resultSelector, params object[] values) 46 | => GroupJoin(outer, inner, outerKeySelector, innerKeySelector, resultSelector, null, null, values); 47 | 48 | public static IQueryable GroupJoin(this IQueryable outer, IQueryable inner, string outerKeySelector, string innerKeySelector, string resultSelector, Settings settings, params object[] values) 49 | => GroupJoin(outer, inner, outerKeySelector, innerKeySelector, resultSelector, null, settings, values); 50 | 51 | public static IQueryable GroupJoin(this IQueryable outer, IQueryable inner, string outerKeySelector, string innerKeySelector, string resultSelector, VarType variables, params object[] values) 52 | => GroupJoin(outer, inner, outerKeySelector, innerKeySelector, resultSelector, variables, null, values); 53 | 54 | public static IQueryable GroupJoin(this IQueryable outer, IQueryable inner, string outerKeySelector, string innerKeySelector, string resultSelector, VarType? variables, Settings? settings, params object[] values) { 55 | if (outer == null) throw new ArgumentNullException(nameof(outer)); 56 | if (inner == null) throw new ArgumentNullException(nameof(inner)); 57 | if (string.IsNullOrWhiteSpace(outerKeySelector)) throw new ArgumentNullException(nameof(outerKeySelector)); 58 | if (string.IsNullOrWhiteSpace(innerKeySelector)) throw new ArgumentNullException(nameof(innerKeySelector)); 59 | if (string.IsNullOrWhiteSpace(resultSelector)) throw new ArgumentNullException(nameof(resultSelector)); 60 | 61 | var outerKeyLambda = Evaluator.ToLambda(outerKeySelector, [outer.ElementType], variables, settings, values); 62 | var innerKeyLambda = Evaluator.ToLambda(innerKeySelector, [inner.ElementType], variables, settings, values); 63 | var innerEnumType = typeof(IEnumerable<>).MakeGenericType(inner.ElementType); 64 | var resultLambda = Evaluator.ToLambda(resultSelector, [outer.ElementType, innerEnumType], variables, settings, values); 65 | 66 | return outer.Provider.CreateQuery( 67 | Expression.Call( 68 | typeof(Queryable), 69 | "GroupJoin", 70 | [outer.ElementType, inner.ElementType, outerKeyLambda.Body.Type, resultLambda.Body.Type], 71 | outer.Expression, 72 | Expression.Constant(inner), 73 | Expression.Quote(outerKeyLambda), 74 | Expression.Quote(innerKeyLambda), 75 | Expression.Quote(resultLambda) 76 | ) 77 | ); 78 | } 79 | 80 | public static IQueryable Zip(this IQueryable first, IEnumerable second, string resultSelector, params object[] values) 81 | => Zip(first, second, resultSelector, null, null, values); 82 | 83 | public static IQueryable Zip(this IQueryable first, IEnumerable second, string resultSelector, Settings settings, params object[] values) 84 | => Zip(first, second, resultSelector, null, settings, values); 85 | 86 | public static IQueryable Zip(this IQueryable first, IEnumerable second, string resultSelector, VarType variables, params object[] values) 87 | => Zip(first, second, resultSelector, variables, null, values); 88 | 89 | public static IQueryable Zip(this IQueryable first, IEnumerable second, string resultSelector, VarType? variables, Settings? settings, params object[] values) { 90 | if (first == null) throw new ArgumentNullException(nameof(first)); 91 | if (second == null) throw new ArgumentNullException(nameof(second)); 92 | if (string.IsNullOrWhiteSpace(resultSelector)) throw new ArgumentNullException(nameof(resultSelector)); 93 | 94 | var secondType = typeof(T); 95 | var resultLambda = Evaluator.ToLambda(resultSelector, [first.ElementType, secondType], variables, settings, values); 96 | 97 | return first.Provider.CreateQuery( 98 | Expression.Call( 99 | typeof(Queryable), 100 | "Zip", 101 | [first.ElementType, secondType, resultLambda.Body.Type], 102 | first.Expression, 103 | Expression.Constant(second), 104 | Expression.Quote(resultLambda) 105 | ) 106 | ); 107 | } 108 | } -------------------------------------------------------------------------------- /src/DynamicQueryable/DynamicQueryable.Aggregate.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | using Jokenizer.Net; 3 | using VarType = System.Collections.Generic.IDictionary; 4 | 5 | // ReSharper disable once CheckNamespace 6 | namespace System.Linq.Dynamic; 7 | 8 | public static partial class DynamicQueryable { 9 | 10 | public static object Average(this IQueryable source, string? selector = null, params object[] values) 11 | => Average(source, selector, null, null, values); 12 | 13 | public static object Average(this IQueryable source, string selector, Settings settings, params object[] values) 14 | => Average(source, selector, null, settings, values); 15 | 16 | public static object Average(this IQueryable source, string selector, VarType variables, params object[] values) 17 | => Average(source, selector, variables, null, values); 18 | 19 | public static object Average(this IQueryable source, string? selector, VarType? variables, Settings? settings, params object[] values) 20 | => ExecuteOptionalExpression(source, "Average", selector, false, variables, values, settings)!; 21 | 22 | public static object Sum(this IQueryable source, string? selector = null, params object[] values) 23 | => Sum(source, selector, null, null, values); 24 | 25 | public static object Sum(this IQueryable source, string selector, Settings settings, params object[] values) 26 | => Sum(source, selector, null, settings, values); 27 | 28 | public static object Sum(this IQueryable source, string selector, VarType variables, params object[] values) 29 | => Sum(source, selector, variables, null, values); 30 | 31 | public static object Sum(this IQueryable source, string? selector, VarType? variables, Settings? settings, params object[] values) 32 | => ExecuteOptionalExpression(source, "Sum", selector, false, variables, values, settings)!; 33 | 34 | public static object Max(this IQueryable source, string? selector = null, params object[] values) 35 | => Max(source, selector, null, null, values); 36 | 37 | public static object Max(this IQueryable source, string selector, Settings settings, params object[] values) 38 | => Max(source, selector, null, settings, values); 39 | 40 | public static object Max(this IQueryable source, string selector, VarType variables, params object[] values) 41 | => Max(source, selector, variables, null, values); 42 | 43 | public static object Max(this IQueryable source, string? selector, VarType? variables, Settings? settings, params object[] values) 44 | => ExecuteOptionalExpression(source, "Max", selector, true, variables, values, settings)!; 45 | 46 | public static object Min(this IQueryable source, string? selector = null, params object[] values) 47 | => Min(source, selector, null, null, values); 48 | 49 | public static object Min(this IQueryable source, string selector, Settings settings, params object[] values) 50 | => Min(source, selector, null, settings, values); 51 | 52 | public static object Min(this IQueryable source, string selector, VarType variables, params object[] values) 53 | => Min(source, selector, variables, null, values); 54 | 55 | public static object Min(this IQueryable source, string? selector, VarType? variables, Settings? settings, params object[] values) 56 | => ExecuteOptionalExpression(source, "Min", selector, true, variables, values, settings)!; 57 | 58 | public static object Aggregate(this IQueryable source, string func, params object[] values) 59 | => Aggregate(source, func, null, null, values); 60 | 61 | public static object Aggregate(this IQueryable source, string func, Settings settings, params object[] values) 62 | => Aggregate(source, func, null, settings, values); 63 | 64 | public static object Aggregate(this IQueryable source, string func, VarType variables, params object[] values) 65 | => Aggregate(source, func, variables, null, values); 66 | 67 | public static object Aggregate(this IQueryable source, string func, VarType? variables, Settings? settings, params object[] values) { 68 | if (source == null) throw new ArgumentNullException(nameof(source)); 69 | if (string.IsNullOrWhiteSpace(func)) throw new ArgumentNullException(nameof(func)); 70 | 71 | var funcLambda = Evaluator.ToLambda(func, [source.ElementType, source.ElementType], variables, settings, values); 72 | 73 | return source.Provider.Execute( 74 | Expression.Call( 75 | typeof(Queryable), 76 | "Aggregate", 77 | [source.ElementType], 78 | source.Expression, 79 | Expression.Quote(funcLambda) 80 | ) 81 | ); 82 | } 83 | 84 | public static object Aggregate(this IQueryable source, object seed, string func, params object[] values) 85 | => Aggregate(source, seed, func, (VarType?)null, null, values); 86 | 87 | public static object Aggregate(this IQueryable source, object seed, string func, Settings settings, params object[] values) 88 | => Aggregate(source, seed, func, (VarType?)null, settings, values); 89 | 90 | public static object Aggregate(this IQueryable source, object seed, string func, VarType variables, params object[] values) 91 | => Aggregate(source, seed, func, variables, null, values); 92 | 93 | public static object Aggregate(this IQueryable source, object seed, string func, VarType? variables, Settings? settings, params object[] values) { 94 | if (source == null) throw new ArgumentNullException(nameof(source)); 95 | if (seed == null) throw new ArgumentNullException(nameof(seed)); 96 | if (string.IsNullOrWhiteSpace(func)) throw new ArgumentNullException(nameof(func)); 97 | 98 | var funcLambda = Evaluator.ToLambda(func, [seed.GetType(), source.ElementType], variables, settings, values); 99 | 100 | return source.Provider.Execute( 101 | Expression.Call( 102 | typeof(Queryable), 103 | "Aggregate", 104 | [source.ElementType, seed.GetType()], 105 | source.Expression, 106 | Expression.Constant(seed), 107 | Expression.Quote(funcLambda) 108 | ) 109 | ); 110 | } 111 | 112 | public static object Aggregate(this IQueryable source, object seed, string func, string selector, params object[] values) 113 | => Aggregate(source, seed, func, selector, null, null, values); 114 | 115 | public static object Aggregate(this IQueryable source, object seed, string func, string selector, Settings settings, params object[] values) 116 | => Aggregate(source, seed, func, selector, null, settings, values); 117 | 118 | public static object Aggregate(this IQueryable source, object seed, string func, string selector, VarType variables, params object[] values) 119 | => Aggregate(source, seed, func, selector, variables, null, values); 120 | 121 | public static object Aggregate(this IQueryable source, object seed, string func, string selector, VarType? variables, Settings? settings, params object[] values) { 122 | if (source == null) throw new ArgumentNullException(nameof(source)); 123 | if (seed == null) throw new ArgumentNullException(nameof(seed)); 124 | if (string.IsNullOrWhiteSpace(func)) throw new ArgumentNullException(nameof(func)); 125 | if (string.IsNullOrWhiteSpace(selector)) throw new ArgumentNullException(nameof(func)); 126 | 127 | var funcLambda = Evaluator.ToLambda(func, [seed.GetType(), source.ElementType], variables, settings, values); 128 | var selectorLambda = Evaluator.ToLambda(selector, [seed.GetType()], variables, settings, values); 129 | 130 | return source.Provider.Execute( 131 | Expression.Call( 132 | typeof(Queryable), 133 | "Aggregate", 134 | [source.ElementType, seed.GetType(), selectorLambda.Body.Type], 135 | source.Expression, 136 | Expression.Constant(seed), 137 | Expression.Quote(funcLambda), 138 | Expression.Quote(selectorLambda) 139 | ) 140 | ); 141 | } 142 | } -------------------------------------------------------------------------------- /src/DynamicQueryable/DynamicQueryable.Get.cs: -------------------------------------------------------------------------------- 1 | using Jokenizer.Net; 2 | using VarType = System.Collections.Generic.IDictionary; 3 | 4 | // ReSharper disable once CheckNamespace 5 | namespace System.Linq.Dynamic; 6 | 7 | public static partial class DynamicQueryable { 8 | 9 | public static T First(this IQueryable source, string? predicate = null, params object[] values) 10 | => First(source, predicate, null, null, values); 11 | 12 | public static T First(this IQueryable source, string predicate, Settings settings, params object[] values) 13 | => First(source, predicate, null, settings, values); 14 | 15 | public static T First(this IQueryable source, string predicate, VarType variables, params object[] values) 16 | => First(source, predicate, variables, null, values); 17 | 18 | public static T First(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 19 | => (T)First((IQueryable)source, predicate, variables, settings, values); 20 | 21 | public static object First(this IQueryable source, string? predicate = null, params object[] values) 22 | => First(source, predicate, null, null, values); 23 | 24 | public static object First(this IQueryable source, string predicate, Settings settings, params object[] values) 25 | => First(source, predicate, null, settings, values); 26 | 27 | public static object First(this IQueryable source, string predicate, VarType variables, params object[] values) 28 | => First(source, predicate, variables, null, values); 29 | 30 | public static object First(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 31 | => ExecuteOptionalExpression(source, "First", predicate, string.IsNullOrEmpty(predicate), variables, values, settings)!; 32 | 33 | public static T? FirstOrDefault(this IQueryable source, string? predicate = null, params object[] values) 34 | => FirstOrDefault(source, predicate, null, null, values); 35 | 36 | public static T? FirstOrDefault(this IQueryable source, string predicate, Settings settings, params object[] values) 37 | => FirstOrDefault(source, predicate, null, settings, values); 38 | 39 | public static T? FirstOrDefault(this IQueryable source, string predicate, VarType variables, params object[] values) 40 | => FirstOrDefault(source, predicate, variables, null, values); 41 | 42 | public static T? FirstOrDefault(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 43 | => (T?)FirstOrDefault((IQueryable)source, predicate, variables, settings, values); 44 | 45 | public static object? FirstOrDefault(this IQueryable source, string? predicate = null, params object[] values) 46 | => FirstOrDefault(source, predicate, null, null, values); 47 | 48 | public static object? FirstOrDefault(this IQueryable source, string predicate, Settings settings, params object[] values) 49 | => FirstOrDefault(source, predicate, null, settings, values); 50 | 51 | public static object? FirstOrDefault(this IQueryable source, string predicate, VarType variables, params object[] values) 52 | => FirstOrDefault(source, predicate, variables, null, values); 53 | 54 | public static object? FirstOrDefault(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 55 | => ExecuteOptionalExpression(source, "FirstOrDefault", predicate, string.IsNullOrEmpty(predicate), variables, values, settings); 56 | 57 | public static T Single(this IQueryable source, string? predicate = null, params object[] values) 58 | => Single(source, predicate, null, null, values); 59 | 60 | public static T Single(this IQueryable source, string predicate, Settings settings, params object[] values) 61 | => Single(source, predicate, null, settings, values); 62 | 63 | public static T Single(this IQueryable source, string predicate, VarType variables, params object[] values) 64 | => Single(source, predicate, variables, null, values); 65 | 66 | public static T Single(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 67 | => (T)Single((IQueryable)source, predicate, variables, settings, values); 68 | 69 | public static object Single(this IQueryable source, string? predicate = null, params object[] values) 70 | => Single(source, predicate, null, null, values); 71 | 72 | public static object Single(this IQueryable source, string predicate, Settings settings, params object[] values) 73 | => Single(source, predicate, null, settings, values); 74 | 75 | public static object Single(this IQueryable source, string predicate, VarType variables, params object[] values) 76 | => Single(source, predicate, variables, null, values); 77 | 78 | public static object Single(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 79 | => ExecuteOptionalExpression(source, "Single", predicate, string.IsNullOrEmpty(predicate), variables, values, settings)!; 80 | 81 | public static T? SingleOrDefault(this IQueryable source, string? predicate = null, params object[] values) 82 | => SingleOrDefault(source, predicate, null, null, values); 83 | 84 | public static T? SingleOrDefault(this IQueryable source, string predicate, Settings settings, params object[] values) 85 | => SingleOrDefault(source, predicate, null, settings, values); 86 | 87 | public static T? SingleOrDefault(this IQueryable source, string predicate, VarType variables, params object[] values) 88 | => SingleOrDefault(source, predicate, variables, null, values); 89 | 90 | public static T? SingleOrDefault(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 91 | => (T?)SingleOrDefault((IQueryable)source, predicate, variables, settings, values); 92 | 93 | public static object? SingleOrDefault(this IQueryable source, string? predicate = null, params object[] values) 94 | => SingleOrDefault(source, predicate, null, null, values); 95 | 96 | public static object? SingleOrDefault(this IQueryable source, string predicate, Settings settings, params object[] values) 97 | => SingleOrDefault(source, predicate, null, settings, values); 98 | 99 | public static object? SingleOrDefault(this IQueryable source, string predicate, VarType variables, params object[] values) 100 | => SingleOrDefault(source, predicate, variables, null, values); 101 | 102 | public static object? SingleOrDefault(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 103 | => ExecuteOptionalExpression(source, "SingleOrDefault", predicate, string.IsNullOrEmpty(predicate), variables, values, settings); 104 | 105 | public static T Last(this IQueryable source, string? predicate = null, params object[] values) 106 | => Last(source, predicate, null, null, values); 107 | 108 | public static T Last(this IQueryable source, string predicate, Settings settings, params object[] values) 109 | => Last(source, predicate, null, settings, values); 110 | 111 | public static T Last(this IQueryable source, string predicate, VarType variables, params object[] values) 112 | => Last(source, predicate, variables, null, values); 113 | 114 | public static T Last(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 115 | => (T)Last((IQueryable)source, predicate, variables, settings, values); 116 | 117 | public static object Last(this IQueryable source, string? predicate = null, params object[] values) 118 | => Last(source, predicate, null, null, values); 119 | 120 | public static object Last(this IQueryable source, string predicate, Settings settings, params object[] values) 121 | => Last(source, predicate, null, settings, values); 122 | 123 | public static object Last(this IQueryable source, string predicate, VarType variables, params object[] values) 124 | => Last(source, predicate, variables, null, values); 125 | 126 | public static object Last(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 127 | => ExecuteOptionalExpression(source, "Last", predicate, string.IsNullOrEmpty(predicate), variables, values, settings)!; 128 | 129 | public static T? LastOrDefault(this IQueryable source, string? predicate = null, params object[] values) 130 | => LastOrDefault(source, predicate, null, null, values); 131 | 132 | public static T? LastOrDefault(this IQueryable source, string predicate, Settings settings, params object[] values) 133 | => LastOrDefault(source, predicate, null, settings, values); 134 | 135 | public static T? LastOrDefault(this IQueryable source, string predicate, VarType variables, params object[] values) 136 | => LastOrDefault(source, predicate, variables, null, values); 137 | 138 | public static T? LastOrDefault(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 139 | => (T?)LastOrDefault((IQueryable)source, predicate, variables, settings, values); 140 | 141 | public static object? LastOrDefault(this IQueryable source, string? predicate = null, params object[] values) 142 | => LastOrDefault(source, predicate, null, null, values); 143 | 144 | public static object? LastOrDefault(this IQueryable source, string predicate, Settings settings, params object[] values) 145 | => LastOrDefault(source, predicate, null, settings, values); 146 | 147 | public static object? LastOrDefault(this IQueryable source, string predicate, VarType variables, params object[] values) 148 | => LastOrDefault(source, predicate, variables, null, values); 149 | 150 | public static object? LastOrDefault(this IQueryable source, string? predicate, VarType? variables, Settings? settings, params object[] values) 151 | => ExecuteOptionalExpression(source, "LastOrDefault", predicate, string.IsNullOrEmpty(predicate), variables, values, settings); 152 | 153 | public static object ElementAt(this IQueryable source, int index) 154 | => ExecuteConstant(source, "ElementAt", index)!; 155 | 156 | public static object? ElementAtOrDefault(this IQueryable source, int index) 157 | => ExecuteConstant(source, "ElementAtOrDefault", index); 158 | } -------------------------------------------------------------------------------- /test/DynamicQueryable.Tests/ExpressionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Dynamic; 5 | using Jokenizer.Net; 6 | using Xunit; 7 | 8 | namespace DynamicQueryable.Tests; 9 | 10 | using Fixture; 11 | using Dyn = System.Linq.Dynamic.DynamicQueryable; 12 | 13 | public class ExpressionTests { 14 | private readonly IQueryable _query; 15 | private readonly double _avgId; 16 | private readonly Settings _ignoreCaseSettings = new() { IgnoreMemberCase = true }; 17 | private readonly Dictionary _emptyParameters = new(); 18 | 19 | public ExpressionTests() { 20 | var orders = CreateOrders(); 21 | _avgId = orders.Average(o => o.Id); 22 | _query = orders.AsQueryable(); 23 | } 24 | 25 | private static List CreateOrders() { 26 | var random = new Random(); 27 | 28 | var companies = Enumerable.Range(1, 10) 29 | .Select(i => new Company { 30 | Id = i, 31 | CompanyName = $"Company {i}", 32 | Phone = $"(555) {random.Next(100, 999)}-{random.Next(1000, 9999)}" 33 | }) 34 | .ToList(); 35 | 36 | var products = Enumerable.Range(1, 50) 37 | .Select(i => new Product { 38 | Id = i, 39 | Name = $"Product {i}", 40 | Supplier = companies[random.Next(companies.Count)] 41 | }) 42 | .ToList(); 43 | 44 | var orders = Enumerable.Range(1, 20) 45 | .Select(orderId => new Order { 46 | Id = orderId, 47 | OrderNo = $"ORD{orderId:0000}", 48 | OrderDate = DateTime.Today.AddDays(-random.Next(30)), 49 | Price = null, 50 | Lines = Enumerable.Range(1, random.Next(3, 15)) 51 | .Select(lineId => new OrderLine { 52 | Id = lineId, 53 | OrderId = orderId, 54 | Count = random.Next(1, 10), 55 | UnitPrice = Math.Round(random.NextDouble() * 100, 2), 56 | Product = products[random.Next(products.Count)] 57 | }) 58 | .ToList() 59 | }) 60 | .ToList(); 61 | 62 | foreach (var order in orders) { 63 | order.Price = order.Lines.Sum(line => (line.UnitPrice ?? 0) * (line.Count ?? 1)); 64 | } 65 | 66 | return orders; 67 | } 68 | 69 | [Fact] 70 | public void ShouldHandleAggregate() { 71 | var query = _query.Select(o => o.Id); 72 | var diffPrm = new Dictionary { { "diff", 10 } }; 73 | 74 | var sumId1 = query.Aggregate((i1, i2) => i1 + i2 + 10); 75 | var dynSumId11 = query.Aggregate("(i1, i2) => i1 + i2 + @0", 10); 76 | var dynSumId12 = query.Aggregate("(i1, i2) => i1 + i2 + diff", diffPrm); 77 | var dynSumId13 = query.Aggregate("(i1, i2) => i1 + i2 + 10", _ignoreCaseSettings, 10); 78 | Assert.Equal(sumId1, dynSumId11); 79 | Assert.Equal(sumId1, dynSumId12); 80 | Assert.Equal(sumId1, dynSumId13); 81 | 82 | var sumId2 = query.Aggregate(42, (i1, i2) => i1 + i2 + 10); 83 | var dynSumId21 = query.Aggregate(42, "(i1, i2) => i1 + i2 + @0", 10); 84 | var dynSumId22 = query.Aggregate(42, "(i1, i2) => i1 + i2 + diff", diffPrm); 85 | var dynSumId23 = query.Aggregate(42, "(i1, i2) => i1 + i2 + @0", _ignoreCaseSettings, 10); 86 | Assert.Equal(sumId2, dynSumId21); 87 | Assert.Equal(sumId2, dynSumId22); 88 | Assert.Equal(sumId2, dynSumId23); 89 | 90 | var sumId3 = query.Aggregate(42, (i1, i2) => i1 + i2 + 10, i => i.ToString()); 91 | var dynSumId31 = query.Aggregate(42, "(i1, i2) => i1 + i2 + 10", "i => i.ToString()", 10); 92 | var dynSumId32 = query.Aggregate(42, "(i1, i2) => i1 + i2 + diff", "i => i.ToString()", diffPrm); 93 | var dynSumId33 = query.Aggregate(42, "(i1, i2) => i1 + i2 + @0", "i => i.ToString()", _ignoreCaseSettings, 10); 94 | Assert.Equal(sumId3, dynSumId31); 95 | Assert.Equal(sumId3, dynSumId32); 96 | Assert.Equal(sumId3, dynSumId33); 97 | 98 | Assert.Throws(() => Dyn.Aggregate(null!, "")); 99 | Assert.Throws(() => _query.Aggregate("")); 100 | Assert.Throws(() => Dyn.Aggregate(null!, ((object?)null)!, "")); 101 | Assert.Throws(() => _query.Aggregate(((object?)null)!, "")); 102 | Assert.Throws(() => _query.Aggregate(42, "")); 103 | Assert.Throws(() => Dyn.Aggregate(null!, ((object?)null)!, "", "")); 104 | Assert.Throws(() => _query.Aggregate(((object?)null)!, "", "")); 105 | Assert.Throws(() => _query.Aggregate(42, "", "")); 106 | Assert.Throws(() => _query.Aggregate(42, "Id", "")); 107 | } 108 | 109 | [Fact] 110 | public void ShouldHandleAll() { 111 | var all = _query.All(o => o.Id != 42); 112 | var dynAll1 = _query.All("o => o.Id != " + 42); 113 | var dynAll2 = _query.All("o => o.Id != @0", 42); 114 | var dynAll3 = _query.All("o => o.Id != Meaning", new Dictionary { { "Meaning", 42 } }); 115 | var dynAll4 = _query.All("o => o.id != @0", _ignoreCaseSettings, 42); 116 | 117 | Assert.Equal(all, dynAll1); 118 | Assert.Equal(all, dynAll2); 119 | Assert.Equal(all, dynAll3); 120 | Assert.Equal(all, dynAll4); 121 | } 122 | 123 | [Fact] 124 | public void ShouldHandleAny() { 125 | var order = _query.Any(o => o.Id < _avgId); 126 | var dynOrder1 = _query.Any("o => o.Id < " + _avgId); 127 | var dynOrder2 = _query.Any("o => o.Id < @0", _avgId); 128 | var dynOrder3 = _query.Any("o => o.Id < AvgId", new Dictionary { { "AvgId", _avgId } }); 129 | var dynOrder4 = _query.Any("o => o.iD < @0", _ignoreCaseSettings, _avgId); 130 | var dynOrder5 = ((IQueryable)_query).Any(); 131 | 132 | Assert.Equal(order, dynOrder1); 133 | Assert.Equal(order, dynOrder2); 134 | Assert.Equal(order, dynOrder3); 135 | Assert.Equal(order, dynOrder4); 136 | Assert.Equal(_query.Any(), dynOrder5); 137 | } 138 | 139 | [Fact] 140 | public void ShouldHandleAverage() { 141 | var avg = _query.Average(o => o.Price + 42); 142 | var dynAvg1 = _query.Average("o => o.Price + 42" ); 143 | var dynAvg2 = _query.Average("o => o.Price + @0", 42); 144 | var dynAvg3 = _query.Average("o => o.Price + diff", new Dictionary { { "diff", 42 } }); 145 | var dynAvg4 = _query.Average("o => o.price + @0", _ignoreCaseSettings, 42); 146 | 147 | Assert.Equal(avg, dynAvg1); 148 | Assert.Equal(avg, dynAvg2); 149 | Assert.Equal(avg, dynAvg3); 150 | Assert.Equal(avg, dynAvg4); 151 | } 152 | 153 | [Fact] 154 | public void ShouldHandleConcat() { 155 | var orders1 = _query.Skip(1).Take(4).AsQueryable(); 156 | var orders2 = _query.Take(2).ToList(); 157 | var concat = orders1.Concat(orders2).ToList(); 158 | var dynConcat = ((IQueryable)orders1).Concat(orders2).Cast().ToList(); 159 | 160 | Assert.Equal(concat, dynConcat); 161 | } 162 | 163 | [Fact] 164 | public void ShouldHandleContains() { 165 | var order = _query.Last(); 166 | 167 | Assert.True(((IQueryable)_query).Contains(order)); 168 | } 169 | 170 | [Fact] 171 | public void ShouldHandleCount() { 172 | var order = _query.Count(o => o.Id < _avgId); 173 | var dynOrder1 = _query.Count("o => o.Id < " + _avgId); 174 | var dynOrder2 = _query.Count("o => o.Id < @0", _avgId); 175 | var dynOrder3 = _query.Count("o => o.Id < AvgId", new Dictionary { { "AvgId", _avgId } }); 176 | var dynOrder4 = _query.Count("o => o.id < @0", _ignoreCaseSettings, _avgId); 177 | var dynOrder5 = ((IQueryable)_query).Count(); 178 | 179 | Assert.Equal(order, dynOrder1); 180 | Assert.Equal(order, dynOrder2); 181 | Assert.Equal(order, dynOrder3); 182 | Assert.Equal(order, dynOrder4); 183 | Assert.Equal(_query.Count(), dynOrder5); 184 | } 185 | 186 | [Fact] 187 | public void ShouldHandleDefaultIfEmpty() { 188 | var query = new List().AsQueryable(); 189 | Assert.Equal(query.DefaultIfEmpty(), ((IQueryable)query).DefaultIfEmpty()); 190 | var order = new Order(); 191 | Assert.Equal(query.DefaultIfEmpty(order), ((IQueryable)query).DefaultIfEmpty(order)); 192 | } 193 | 194 | [Fact] 195 | public void ShouldHandleDistinct() { 196 | var orders = new List { 197 | new() { Id = 1, Price = 1 }, 198 | new() { Id = 2, Price = 1 }, 199 | new() { Id = 3, Price = 2 } 200 | }; 201 | orders.Add(orders[1]); 202 | 203 | var dynDistinct = ((IQueryable)orders.AsQueryable()).Distinct().Cast(); 204 | Assert.Equal(orders.Distinct(), dynDistinct); 205 | } 206 | 207 | [Fact] 208 | public void ShouldHandleElementAt() { 209 | var order1 = _query.ElementAt(4); 210 | var dynOrder1 = ((IQueryable)_query).ElementAt(4); 211 | 212 | Assert.Equal(order1, dynOrder1); 213 | Assert.Null(_query.ElementAtOrDefault(42)); 214 | } 215 | 216 | [Fact] 217 | public void ShouldHandleElementAtOrDefault() { 218 | var order = _query.ElementAtOrDefault(4); 219 | var dynOrder = ((IQueryable)_query).ElementAtOrDefault(4); 220 | 221 | Assert.Equal(order, dynOrder); 222 | Assert.Null(((IQueryable)_query).ElementAtOrDefault(42)); 223 | } 224 | 225 | [Fact] 226 | public void ShouldHandleExcept() { 227 | var orders1 = _query.Take(4).AsQueryable(); 228 | var orders2 = _query.Take(2).ToList(); 229 | var except = orders1.Except(orders2).ToList(); 230 | var dynExcept = ((IQueryable)orders1).Except(orders2).Cast().ToList(); 231 | 232 | Assert.Equal(except, dynExcept); 233 | } 234 | 235 | [Fact] 236 | public void ShouldHandleFirst() { 237 | var order = _query.First(o => o.Id > _avgId); 238 | var first = _query.First(); 239 | var avgPrm = new Dictionary { { "AvgId", _avgId } }; 240 | 241 | var dynOrder1 = _query.First("o => o.Id > @0", _avgId); 242 | var dynOrder2 = _query.First("o => o.Id > AvgId", avgPrm); 243 | var dynOrder3 = _query.First("o => o.id > @0", _ignoreCaseSettings, _avgId); 244 | Assert.Equal(order, dynOrder1); 245 | Assert.Equal(order, dynOrder2); 246 | Assert.Equal(order, dynOrder3); 247 | 248 | IQueryable q = _query; 249 | var dynOrder4 = q.First("Id > @0", _avgId); 250 | var dynOrder5 = q.First("o => o.Id > AvgId", avgPrm); 251 | var dynOrder6 = q.First("iD > @0", _ignoreCaseSettings, _avgId); 252 | var dynOrder7 = q.First(); 253 | Assert.Equal(order, dynOrder4); 254 | Assert.Equal(order, dynOrder5); 255 | Assert.Equal(order, dynOrder6); 256 | Assert.Equal(first, dynOrder7); 257 | 258 | Assert.Throws(() => _query.Take(0).First("Id == 1")); 259 | Assert.Throws(() => ((IQueryable)_query.Take(0)).First()); 260 | Assert.Throws(() => ((IQueryable)_query.Take(0)).First("Id == 1")); 261 | Assert.Throws(() => Dyn.First(null!, "Id > 1")); 262 | } 263 | 264 | [Fact] 265 | public void ShouldHandleFirstOrDefault() { 266 | var order = _query.FirstOrDefault(o => o.Id > _avgId); 267 | var first = _query.FirstOrDefault(); 268 | var avgPrm = new Dictionary { { "AvgId", _avgId } }; 269 | 270 | var dynOrder1 = _query.FirstOrDefault("o => o.Id > @0", _avgId); 271 | var dynOrder2 = _query.FirstOrDefault("o => o.Id > AvgId", avgPrm); 272 | var dynOrder3 = _query.FirstOrDefault("o => o.id > @0", _ignoreCaseSettings, _avgId); 273 | Assert.Equal(order, dynOrder1); 274 | Assert.Equal(order, dynOrder2); 275 | Assert.Equal(order, dynOrder3); 276 | 277 | var dynOrder4 = ((IQueryable)_query).FirstOrDefault("Id > @0", _avgId); 278 | var dynOrder5 = ((IQueryable)_query).FirstOrDefault("Id > AvgId", avgPrm); 279 | var dynOrder6 = ((IQueryable)_query).FirstOrDefault("iD > @0", _ignoreCaseSettings, _avgId); 280 | var dynOrder7 = ((IQueryable)_query).FirstOrDefault(); 281 | Assert.Equal(order, dynOrder4); 282 | Assert.Equal(order, dynOrder5); 283 | Assert.Equal(order, dynOrder6); 284 | Assert.Equal(first, dynOrder7); 285 | 286 | Assert.Null(_query.Take(0).FirstOrDefault("Id == 1")); 287 | Assert.Null(((IQueryable)_query.Take(0)).FirstOrDefault()); 288 | Assert.Null(((IQueryable)_query.Take(0)).FirstOrDefault("Id == 1")); 289 | 290 | var max = _query.Max(o => o.Id); 291 | var dynOrder8 = _query.FirstOrDefault("o => o.Id > @0", max); 292 | Assert.Null(dynOrder8); 293 | } 294 | 295 | [Fact] 296 | public void ShouldHandleGroupBy() { 297 | var lines = _query.SelectMany(o => o.Lines).ToList().AsQueryable(); 298 | 299 | var group1 = lines.GroupBy(l => l.OrderId, l => l.Id, (k, lid) => lid.Count()).ToList(); 300 | var dynGroup11 = lines.GroupBy("l => l.OrderId", "l => l.Id", "(k, lid) => lid.Count()", 0).Cast().ToList(); 301 | var dynGroup12 = lines.GroupBy("l => l.OrderId", "l => l.Id", "(k, lid) => lid.Count()", _emptyParameters).Cast().ToList(); 302 | var dynGroup13 = lines.GroupBy("l => l.orderId", "l => l.ID", "(k, lid) => lid.Count()", _ignoreCaseSettings).Cast().ToList(); 303 | 304 | Assert.Equal(group1, dynGroup11); 305 | Assert.Equal(group1, dynGroup12); 306 | Assert.Equal(group1, dynGroup13); 307 | 308 | var group2 = lines.GroupBy(l => l.OrderId, (k, l) => l.Count()).ToList(); 309 | var dynGroup21 = lines.GroupBy("l => l.OrderId", "(k, l) => l.Count()", 0).Cast().ToList(); 310 | var dynGroup22 = lines.GroupBy("l => l.OrderId", "(k, l) => l.Count()", _emptyParameters).Cast().ToList(); 311 | var dynGroup23 = lines.GroupBy("l => l.orderid", "(k, l) => l.Count()", _ignoreCaseSettings).Cast().ToList(); 312 | 313 | Assert.Equal(group2, dynGroup21); 314 | Assert.Equal(group2, dynGroup22); 315 | Assert.Equal(group2, dynGroup23); 316 | 317 | var group3 = lines.GroupBy(l => l.OrderId).ToList(); 318 | var dynGroup31 = lines.GroupBy("l => l.OrderId").Cast>().ToList(); 319 | var dynGroup32 = lines.GroupBy("l => l.OrderId", _emptyParameters).Cast>().ToList(); 320 | var dynGroup33 = lines.GroupBy("l => l.OrderId", _ignoreCaseSettings).Cast>().ToList(); 321 | 322 | Assert.Equal(group3, dynGroup31); 323 | Assert.Equal(group3, dynGroup32); 324 | Assert.Equal(group3, dynGroup33); 325 | 326 | Assert.Throws(() => Dyn.GroupBy(null!, "")); 327 | Assert.Throws(() => _query.GroupBy("")); 328 | Assert.Throws(() => Dyn.GroupBy(null!, "", "")); 329 | Assert.Throws(() => _query.GroupBy("", "")); 330 | Assert.Throws(() => _query.GroupBy("Id", "")); 331 | Assert.Throws(() => Dyn.GroupBy(null!, "", "", "")); 332 | Assert.Throws(() => _query.GroupBy("", "", "")); 333 | Assert.Throws(() => _query.GroupBy("Id", "", "")); 334 | Assert.Throws(() => _query.GroupBy("Id", "Id", "")); 335 | } 336 | 337 | [Fact] 338 | public void ShouldHandleGroupJoin() { 339 | var orders = _query.ToList().AsQueryable(); 340 | var lines = _query.SelectMany(o => o.Lines).ToList().AsQueryable(); 341 | 342 | var groupJoin = orders.GroupJoin(lines, o => o.Id, l => l.OrderId, (o, l) => o.Id + l.Max(x => x.Id) + 10).ToList(); 343 | var dynGroupJoin1 = orders.GroupJoin(lines, "o => o.Id", "l => l.OrderId", "(o, l) => o.Id + l.Max(x => x.Id) + @0", 10).Cast().ToList(); 344 | var dynGroupJoin2 = orders.GroupJoin(lines, "o => o.Id", "l => l.OrderId", 345 | "(o, l) => o.Id + l.Max(x => x.Id) + diff", new Dictionary { { "diff", 10 } }) 346 | .Cast() 347 | .ToList(); 348 | var dynGroupJoin3 = orders.GroupJoin(lines, "o => o.id", "l => l.orderId", "(o, l) => o.Id + l.Max(x => x.Id) + @0", _ignoreCaseSettings, 10).Cast().ToList(); 349 | 350 | Assert.Equal(groupJoin, dynGroupJoin1); 351 | Assert.Equal(groupJoin, dynGroupJoin2); 352 | Assert.Equal(groupJoin, dynGroupJoin3); 353 | 354 | Assert.Throws(() => Dyn.GroupJoin(null!, null!, "", "", "")); 355 | Assert.Throws(() => _query.GroupJoin(null!, "", "", "")); 356 | Assert.Throws(() => _query.GroupJoin(_query, "", "", "")); 357 | Assert.Throws(() => _query.GroupJoin(_query, "Id", "", "")); 358 | Assert.Throws(() => _query.GroupJoin(_query, "Id", "Id", "")); 359 | } 360 | 361 | [Fact] 362 | public void ShouldHandleIntersect() { 363 | var orders1 = _query.Take(4).AsQueryable(); 364 | var orders2 = _query.Take(2).ToList(); 365 | var intersect = orders1.Intersect(orders2).ToList(); 366 | var dynIntersect = ((IQueryable)orders1).Intersect(orders2).Cast().ToList(); 367 | 368 | Assert.Equal(intersect, dynIntersect); 369 | } 370 | 371 | [Fact] 372 | public void ShouldHandleJoin() { 373 | var orders = _query.ToList().AsQueryable(); 374 | var lines = _query.SelectMany(o => o.Lines).ToList().AsQueryable(); 375 | 376 | var join = orders.Join(lines, o => o.Id, l => l.OrderId, (o, l) => o.Id + l.Id + 10).ToList(); 377 | var dynJoin1 = orders.Join(lines, "o => o.Id", "l => l.OrderId", "(o, l) => o.Id + l.Id + @0", 10).Cast().ToList(); 378 | var dynJoin2 = orders.Join(lines, "o => o.Id", "l => l.OrderId", "(o, l) => o.Id + l.Id + diff", 379 | new Dictionary { { "diff", 10 } }).Cast().ToList(); 380 | var dynJoin3 = orders.Join(lines, "o => o.id", "l => l.orderId", "(o, l) => o.Id + l.Id + @0", _ignoreCaseSettings, 10).Cast().ToList(); 381 | 382 | Assert.Equal(join, dynJoin1); 383 | Assert.Equal(join, dynJoin2); 384 | Assert.Equal(join, dynJoin3); 385 | 386 | Assert.Throws(() => Dyn.Join(null!, null!, "", "", "")); 387 | Assert.Throws(() => _query.Join(null!, "", "", "")); 388 | Assert.Throws(() => _query.Join(_query, "", "", "")); 389 | Assert.Throws(() => _query.Join(_query, "Id", "", "")); 390 | Assert.Throws(() => _query.Join(_query, "Id", "Id", "")); 391 | } 392 | 393 | [Fact] 394 | public void ShouldHandleLast() { 395 | var order = _query.Last(o => o.Id < _avgId); 396 | var avgPrm = new Dictionary { { "AvgId", _avgId } }; 397 | 398 | var dynOrder1 = _query.Last("o => o.Id < @0", _avgId); 399 | var dynOrder2 = _query.Last("o => o.Id < AvgId", avgPrm); 400 | var dynOrder3 = _query.Last("o => o.id < @0", _ignoreCaseSettings, _avgId); 401 | Assert.Equal(order, dynOrder1); 402 | Assert.Equal(order, dynOrder2); 403 | Assert.Equal(order, dynOrder3); 404 | 405 | IQueryable q = _query; 406 | var dynOrder4 = q.Last("o => o.Id < @0", _avgId); 407 | var dynOrder5 = q.Last("o => o.Id < AvgId", avgPrm); 408 | var dynOrder6 = q.Last("o => o.id < @0", _ignoreCaseSettings, _avgId); 409 | var dynOrder7 = ((IQueryable)_query).Last(); 410 | Assert.Equal(order, dynOrder4); 411 | Assert.Equal(order, dynOrder5); 412 | Assert.Equal(order, dynOrder6); 413 | Assert.Equal(_query.Last(), dynOrder7); 414 | 415 | Assert.Throws(() => _query.Take(0).Last("Id == 1")); 416 | Assert.Throws(() => ((IQueryable)_query.Take(0)).Last()); 417 | Assert.Throws(() => ((IQueryable)_query.Take(0)).Last("Id == 1")); 418 | } 419 | 420 | [Fact] 421 | public void ShouldHandleLastOrDefault() { 422 | var order = _query.LastOrDefault(o => o.Id < _avgId); 423 | var avgPrm = new Dictionary { { "AvgId", _avgId } }; 424 | 425 | var dynOrder1 = _query.LastOrDefault("o => o.Id < @0", _avgId); 426 | var dynOrder2 = _query.LastOrDefault("o => o.Id < AvgId", avgPrm); 427 | var dynOrder3 = _query.LastOrDefault("o => o.id < @0", _ignoreCaseSettings, _avgId); 428 | Assert.Equal(order, dynOrder1); 429 | Assert.Equal(order, dynOrder2); 430 | Assert.Equal(order, dynOrder3); 431 | 432 | IQueryable q = _query; 433 | var dynOrder4 = q.LastOrDefault("o => o.Id < @0", _avgId); 434 | var dynOrder5 = q.LastOrDefault("o => o.Id < AvgId", avgPrm); 435 | var dynOrder6 = q.LastOrDefault("o => o.id < @0", _ignoreCaseSettings, _avgId); 436 | var dynOrder7 = ((IQueryable)_query).LastOrDefault(); 437 | Assert.Equal(order, dynOrder4); 438 | Assert.Equal(order, dynOrder5); 439 | Assert.Equal(order, dynOrder6); 440 | Assert.Equal(_query.LastOrDefault(), dynOrder7); 441 | 442 | var max = _query.Max(o => o.Id); 443 | var dynOrder8 = _query.LastOrDefault("o => o.Id > @0", max); 444 | Assert.Null(dynOrder8); 445 | } 446 | 447 | [Fact] 448 | public void ShouldHandleLongCount() { 449 | var order = _query.LongCount(o => o.Id < _avgId); 450 | var dynOrder1 = _query.LongCount("o => o.Id < @0", _avgId); 451 | var dynOrder2 = _query.LongCount("o => o.Id < AvgId", new Dictionary { { "AvgId", _avgId } }); 452 | var dynOrder3 = ((IQueryable)_query).LongCount(); 453 | var dynOrder4 = _query.LongCount("o => o.iD < @0", _ignoreCaseSettings, _avgId); 454 | 455 | Assert.Equal(order, dynOrder1); 456 | Assert.Equal(order, dynOrder2); 457 | Assert.Equal(_query.LongCount(), dynOrder3); 458 | Assert.Equal(order, dynOrder4); 459 | } 460 | 461 | [Fact] 462 | public void ShouldHandleMax() { 463 | var avg = _query.Max(o => o.Price + 10); 464 | var dynAvg1 = _query.Max("o => o.Price + @0", 10); 465 | var dynAvg2 = _query.Max("o => o.Price + diff", new Dictionary { { "diff", 10 }}); 466 | var dynAvg3 = _query.Max("o => o.PRiCE + @0", _ignoreCaseSettings, 10); 467 | 468 | Assert.Equal(avg, dynAvg1); 469 | Assert.Equal(avg, dynAvg2); 470 | Assert.Equal(avg, dynAvg3); 471 | } 472 | 473 | [Fact] 474 | public void ShouldHandleMin() { 475 | var avg = _query.Min(o => o.Price - 10); 476 | var dynAvg1 = _query.Min("o => o.Price - @0", 10); 477 | var dynAvg2 = _query.Min("o => o.Price - diff", new Dictionary { { "diff", 10 } }); 478 | var dynAvg3 = _query.Min("o => o.prIce - @0", _ignoreCaseSettings, 10); 479 | 480 | Assert.Equal(avg, dynAvg1); 481 | Assert.Equal(avg, dynAvg2); 482 | Assert.Equal(avg, dynAvg3); 483 | } 484 | 485 | [Fact] 486 | public void ShouldHandleOrderBy() { 487 | var order = _query.OrderBy(o => o.Id + 10).First(); 488 | 489 | var dynOrder1 = _query.OrderBy("o => o.Id + @0", 10).First(); 490 | var dynOrder2 = _query.OrderBy("o => o.Id + diff", new Dictionary { { "diff", 10 } }).First(); 491 | var dynOrder3 = _query.OrderBy("o => o.id + @0", _ignoreCaseSettings, 10).First(); 492 | Assert.Equal(order, dynOrder1); 493 | Assert.Equal(order, dynOrder2); 494 | Assert.Equal(order, dynOrder3); 495 | 496 | IQueryable q = _query; 497 | var dynOrder4 = q.OrderBy("o => o.Id + @0", 10).First(); 498 | var dynOrder5 = q.OrderBy("o => o.Id + diff", new Dictionary { { "diff", 10 } }).First(); 499 | var dynOrder6 = q.OrderBy("o => o.id + @0", _ignoreCaseSettings, 10).First(); 500 | Assert.Equal(order, dynOrder4); 501 | Assert.Equal(order, dynOrder5); 502 | Assert.Equal(order, dynOrder6); 503 | } 504 | 505 | [Fact] 506 | public void ShouldHandleOrderByDescending() { 507 | var order = _query.OrderByDescending(o => o.Id + 10).First(); 508 | 509 | var dynOrder1 = _query.OrderByDescending("o => o.Id + @0", 10).First(); 510 | var dynOrder2 = _query.OrderByDescending("o => o.Id + diff", new Dictionary { { "diff", 10 } }).First(); 511 | var dynOrder3 = _query.OrderByDescending("o => o.id + @0", _ignoreCaseSettings, 10).First(); 512 | Assert.Equal(order, dynOrder1); 513 | Assert.Equal(order, dynOrder2); 514 | Assert.Equal(order, dynOrder3); 515 | 516 | IQueryable q = _query; 517 | var dynOrder4 = q.OrderByDescending("o => o.Id + @0", 10).First(); 518 | var dynOrder5 = q.OrderByDescending("o => o.Id + diff", new Dictionary { { "diff", 10 } }).First(); 519 | var dynOrder6 = q.OrderByDescending("o => o.id + @0", _ignoreCaseSettings, 10).First(); 520 | Assert.Equal(order, dynOrder4); 521 | Assert.Equal(order, dynOrder5); 522 | Assert.Equal(order, dynOrder6); 523 | } 524 | 525 | [Fact] 526 | public void ShouldHandleReverse() { 527 | Assert.Equal(_query.Reverse(), ((IQueryable)_query).Reverse()); 528 | } 529 | 530 | [Fact] 531 | public void ShouldHandleSelect() { 532 | var order = _query.Select(o => new { o.Id, o.OrderDate, Age = 10 }).First(); 533 | var dynOrder1 = _query.Select("o => new { Id = o.Id, OrderDate = o.OrderDate, Age = @0 }", 10).Cast().First(); 534 | var dynOrder2 = _query.Select("o => new { Id = o.Id, OrderDate = o.OrderDate, Age = age }", 535 | new Dictionary { { "age", 10 } }) 536 | .Cast() 537 | .First(); 538 | var dynOrder3 = _query.Select("o => new { Id = o.Id, OrderDate = o.OrderDate, Age = @0 }", Settings.Default, 10).Cast().First(); 539 | 540 | Assert.Equal(order.OrderDate, dynOrder1.OrderDate); 541 | Assert.Equal(order.Age, dynOrder1.Age); 542 | Assert.Equal(order.OrderDate, dynOrder2.OrderDate); 543 | Assert.Equal(order.Age, dynOrder2.Age); 544 | Assert.Equal(order.OrderDate, dynOrder3.OrderDate); 545 | Assert.Equal(order.Age, dynOrder3.Age); 546 | } 547 | 548 | [Fact] 549 | public void ShouldHandleSelectMany() { 550 | var lines = _query.SelectMany(o => o.Lines).ToList(); 551 | var dynLines1 = _query.SelectMany("o => o.Lines").Cast().ToList(); 552 | var dynLines2 = _query.SelectMany("o => o.Lines", _emptyParameters).Cast().ToList(); 553 | var dynLines3 = _query.SelectMany("o => o.lines", _ignoreCaseSettings).Cast().ToList(); 554 | 555 | Assert.Equal(lines, dynLines1); 556 | Assert.Equal(lines, dynLines2); 557 | Assert.Equal(lines, dynLines3); 558 | Assert.Throws(() => Dyn.SelectMany(null!, "")); 559 | Assert.Throws(() => _query.SelectMany("")); 560 | } 561 | 562 | [Fact] 563 | public void ShouldHandleSequenceEqual() { 564 | var orders = _query.Skip(4).Take(2).ToList(); 565 | 566 | Assert.True(((IQueryable)_query.Skip(4).Take(2)).SequenceEqual(orders)); 567 | } 568 | 569 | [Fact] 570 | public void ShouldHandleSingle() { 571 | var orders = new List { 572 | new() { Id = 1, Price = 1 }, 573 | new() { Id = 2, Price = 1 }, 574 | new() { Id = 3, Price = 2 } 575 | }; 576 | var query = orders.AsQueryable(); 577 | var result = query.Single(o => o.Id > 2); 578 | var searchPrm = new Dictionary { { "SearchId", 2 } }; 579 | 580 | var dynOrder1 = query.Single("o => o.Id > @0", 2); 581 | var dynOrder2 = query.Single("o => o.Id > SearchId", searchPrm); 582 | var dynOrder3 = query.Single("o => o.iD > @0", _ignoreCaseSettings, 2); 583 | Assert.Equal(result, dynOrder1); 584 | Assert.Equal(result, dynOrder2); 585 | Assert.Equal(result, dynOrder3); 586 | 587 | IQueryable q = query; 588 | var dynOrder4 = q.Single("o => o.Id > @0", 2); 589 | var dynOrder5 = q.Single("Id > SearchId", searchPrm); 590 | var dynOrder6 = q.Single("id > @0", _ignoreCaseSettings, 2); 591 | var dynOrder7 = q.Take(1).Single(); 592 | 593 | Assert.Equal(result, dynOrder4); 594 | Assert.Equal(result, dynOrder5); 595 | Assert.Equal(result, dynOrder6); 596 | Assert.Equal(orders[0], dynOrder7); 597 | 598 | Assert.Throws(() => query.Single("o => o.Id > 1")); 599 | Assert.Throws(() => query.Single("o => o.Id > 3")); 600 | Assert.Throws(() => ((IQueryable)query.Take(0)).Single()); 601 | Assert.Throws(() => ((IQueryable)query).Single("o => o.Id > 1")); 602 | Assert.Throws(() => ((IQueryable)query).Single("o => o.Id > 3")); 603 | } 604 | 605 | [Fact] 606 | public void ShouldHandleSingleOrDefault() { 607 | var orders = new List { 608 | new() { Id = 1, Price = 1 }, 609 | new() { Id = 2, Price = 1 }, 610 | new() { Id = 3, Price = 2 } 611 | }; 612 | var query = orders.AsQueryable(); 613 | var result = query.SingleOrDefault(o => o.Id > 2); 614 | var searchPrm = new Dictionary { { "SearchId", 2 } }; 615 | 616 | var dynOrder1 = query.SingleOrDefault("o => o.Id > @0", 2); 617 | var dynOrder2 = query.SingleOrDefault("o => o.Id > SearchId", searchPrm); 618 | var dynOrder3 = query.SingleOrDefault("o => o.id > @0", _ignoreCaseSettings, 2); 619 | Assert.Equal(result, dynOrder1); 620 | Assert.Equal(result, dynOrder2); 621 | Assert.Equal(result, dynOrder3); 622 | 623 | IQueryable q = query; 624 | var dynOrder4 = q.SingleOrDefault("o => o.Id > @0", 2); 625 | var dynOrder5 = q.SingleOrDefault("o => o.Id > SearchId", searchPrm); 626 | var dynOrder6 = q.SingleOrDefault("o => o.id > @0", _ignoreCaseSettings, 2); 627 | var dynOrder7 = q.Take(1).SingleOrDefault(); 628 | 629 | Assert.Equal(result, dynOrder4); 630 | Assert.Equal(result, dynOrder5); 631 | Assert.Equal(result, dynOrder6); 632 | Assert.Equal(orders[0], dynOrder7); 633 | 634 | var max = _query.Max(o => o.Id); 635 | var dynOrder8 = _query.SingleOrDefault("o => o.Id > @0", max); 636 | Assert.Null(dynOrder8); 637 | 638 | Assert.Null(query.SingleOrDefault("o => o.Id > 3")); 639 | Assert.Null(((IQueryable)query.Take(0)).SingleOrDefault()); 640 | Assert.Null(((IQueryable)query).SingleOrDefault("o => o.Id > 3")); 641 | 642 | Assert.Throws(() => query.SingleOrDefault("o => o.Id > 1")); 643 | Assert.Throws(() => ((IQueryable)query).SingleOrDefault()); 644 | Assert.Throws(() => ((IQueryable)query).SingleOrDefault("o => o.Id > 1")); 645 | } 646 | 647 | [Fact] 648 | public void ShouldHandleSkip() { 649 | var orders = _query.Skip(3); 650 | var dynOrders = ((IQueryable)_query).Skip(3); 651 | 652 | Assert.Equal(orders, dynOrders); 653 | } 654 | 655 | [Fact] 656 | public void ShouldHandleSkipWhile() { 657 | var avgPrm = new Dictionary { { "AvgId", _avgId } }; 658 | 659 | var orders = _query.SkipWhile(o => o.Id > _avgId).ToList(); 660 | var dynOrders1 = _query.SkipWhile("o => o.Id > @0", _avgId).ToList(); 661 | var dynOrders2 = _query.SkipWhile("o => o.Id > AvgId", avgPrm).ToList(); 662 | var dynOrders3 = _query.SkipWhile("o => o.iD > @0", _ignoreCaseSettings, _avgId).ToList(); 663 | 664 | Assert.Equal(orders, dynOrders1); 665 | Assert.Equal(orders, dynOrders2); 666 | Assert.Equal(orders, dynOrders3); 667 | 668 | IQueryable q = _query; 669 | var dynOrders4 = q.SkipWhile("Id > @0", _avgId).Cast().ToList(); 670 | var dynOrders5 = q.SkipWhile("Id > AvgId", avgPrm).Cast().ToList(); 671 | var dynOrders6 = q.SkipWhile("id > @0", _ignoreCaseSettings, _avgId).Cast().ToList(); 672 | 673 | Assert.Equal(orders, dynOrders4); 674 | Assert.Equal(orders, dynOrders5); 675 | Assert.Equal(orders, dynOrders6); 676 | } 677 | 678 | [Fact] 679 | public void ShouldHandleSum() { 680 | var avg = _query.Sum(o => o.Price + 42); 681 | var dynAvg1 = _query.Sum("o => o.Price + @0", 42); 682 | var dynAvg2 = _query.Sum("o => o.Price + diff", new Dictionary { { "diff", 42 } }); 683 | var dynAvg3 = _query.Sum("o => o.pRiCe + @0", _ignoreCaseSettings, 42); 684 | 685 | Assert.Equal(avg, dynAvg1); 686 | Assert.Equal(avg, dynAvg2); 687 | Assert.Equal(avg, dynAvg3); 688 | } 689 | 690 | [Fact] 691 | public void ShouldHandleTake() { 692 | var orders = _query.Take(3); 693 | var dynOrders = ((IQueryable)_query).Take(3); 694 | 695 | Assert.Equal(orders, dynOrders); 696 | Assert.Throws(() => Dyn.Take(null!, 1)); 697 | } 698 | 699 | [Fact] 700 | public void ShouldHandleTakeWhile() { 701 | var avgPrm = new Dictionary { { "AvgId", _avgId } }; 702 | 703 | var orders = _query.TakeWhile(o => o.Id > _avgId).ToList(); 704 | var dynOrders1 = _query.TakeWhile("o => o.Id > @0", _avgId).ToList(); 705 | var dynOrders2 = _query.TakeWhile("o => o.Id > AvgId", avgPrm).ToList(); 706 | var dynOrders3 = _query.TakeWhile("o => o.iD > @0", _ignoreCaseSettings, _avgId).ToList(); 707 | 708 | Assert.Equal(orders, dynOrders1); 709 | Assert.Equal(orders, dynOrders2); 710 | Assert.Equal(orders, dynOrders3); 711 | 712 | IQueryable q = _query; 713 | var dynOrders4 = q.TakeWhile("Id > @0", _avgId).Cast().ToList(); 714 | var dynOrders5 = q.TakeWhile("Id > AvgId", avgPrm).Cast().ToList(); 715 | var dynOrders6 = q.TakeWhile("id > @0", _ignoreCaseSettings, _avgId).Cast().ToList(); 716 | 717 | Assert.Equal(orders, dynOrders4); 718 | Assert.Equal(orders, dynOrders5); 719 | Assert.Equal(orders, dynOrders6); 720 | } 721 | 722 | [Fact] 723 | public void ShouldHandleThenBy() { 724 | var order = _query.OrderBy(o => o.Id).ThenBy(o => o.Price + 1).First(); 725 | 726 | var q1 = _query.OrderBy(o => o.Id); 727 | var dynOrder1 = q1.ThenBy("o => o.Price + @0", 1).First(); 728 | var dynOrder2 = q1.ThenBy("o => o.Price + diff", new Dictionary { { "diff", 1 } }).First(); 729 | var dynOrder3 = q1.ThenBy("o => o.price + @0", _ignoreCaseSettings, 1).First(); 730 | Assert.Equal(order, dynOrder1); 731 | Assert.Equal(order, dynOrder2); 732 | Assert.Equal(order, dynOrder3); 733 | 734 | IQueryable q2 = _query.OrderBy(o => o.Id); 735 | var dynOrder4 = q2.ThenBy("o => o.Price + @0", 1).First(); 736 | var dynOrder5 = q2.ThenBy("o => o.Price + diff", new Dictionary { { "diff", 1 } }).First(); 737 | var dynOrder6 = q2.ThenBy("o => o.price + @0", _ignoreCaseSettings, 1).First(); 738 | Assert.Equal(order, dynOrder4); 739 | Assert.Equal(order, dynOrder5); 740 | Assert.Equal(order, dynOrder6); 741 | } 742 | 743 | [Fact] 744 | public void ShouldHandleThenByDescending() { 745 | var order = _query.OrderBy(o => o.Id).ThenByDescending(o => o.Price + 1).First(); 746 | 747 | var q1 = _query.OrderBy(o => o.Id); 748 | var dynOrder1 = q1.ThenByDescending("o => o.Price + @0", 1).First(); 749 | var dynOrder2 = q1.ThenByDescending("o => o.Price + diff", new Dictionary { { "diff", 1 } }).First(); 750 | var dynOrder3 = q1.ThenByDescending("o => o.price + @0", _ignoreCaseSettings, 1).First(); 751 | Assert.Equal(order, dynOrder1); 752 | Assert.Equal(order, dynOrder2); 753 | Assert.Equal(order, dynOrder3); 754 | 755 | IQueryable q2 = _query.OrderBy(o => o.Id); 756 | var dynOrder4 = q2.ThenByDescending("o => o.Price + @0", 1).First(); 757 | var dynOrder5 = q2.ThenByDescending("o => o.Price + diff", new Dictionary { { "diff", 1 } }).First(); 758 | var dynOrder6 = q2.ThenByDescending("o => o.price + @0", _ignoreCaseSettings, 1).First(); 759 | Assert.Equal(order, dynOrder4); 760 | Assert.Equal(order, dynOrder5); 761 | Assert.Equal(order, dynOrder6); 762 | } 763 | 764 | [Fact] 765 | public void ShouldHandleUnion() { 766 | var orders1 = _query.Skip(1).Take(4).AsQueryable(); 767 | var orders2 = _query.Take(2).ToList(); 768 | var union = orders1.Union(orders2).ToList(); 769 | var dynUnion = ((IQueryable)orders1).Union(orders2).Cast().ToList(); 770 | 771 | Assert.Equal(union, dynUnion); 772 | } 773 | 774 | [Fact] 775 | public void ShouldHandleWhere() { 776 | var orders = _query.Where(o => o.Id > _avgId).ToList(); 777 | var dynOrders1 = _query.Where("o => o.Id > @0", _avgId).ToList(); 778 | var dynOrders2 = _query.Where("o => o.Id > AvgId", new Dictionary { { "AvgId", _avgId } }).ToList(); 779 | var dynOrders3 = _query.Where("o => o.id > @0", _ignoreCaseSettings, _avgId).ToList(); 780 | 781 | Assert.Equal(orders, dynOrders1); 782 | Assert.Equal(orders, dynOrders2); 783 | Assert.Equal(orders, dynOrders3); 784 | 785 | IQueryable q = _query; 786 | var dynOrders4 = q.Where("Id > @0", _avgId).Cast().ToList(); 787 | var dynOrders5 = q.Where("Id > AvgId", new Dictionary { { "AvgId", _avgId } }).Cast().ToList(); 788 | var dynOrders6 = q.Where("id > @0", _ignoreCaseSettings, _avgId).Cast().ToList(); 789 | Assert.Equal(orders, dynOrders4); 790 | Assert.Equal(orders, dynOrders5); 791 | Assert.Equal(orders, dynOrders6); 792 | 793 | Assert.Throws(() => _query.Where("")); 794 | } 795 | 796 | [Fact] 797 | public void ShouldHandleZip() { 798 | var lineCounts = _query.Select(o => o.Lines.Count).ToList(); 799 | 800 | var zip = _query.Zip(lineCounts, (o, l) => o.Id + l + 10).ToList(); 801 | var dynZip1 = _query.Zip(lineCounts, "(o, l) => o.Id + l + @0", 10).Cast().ToList(); 802 | var dynZip2 = _query.Zip(lineCounts, "(o, l) => o.Id + l + diff", new Dictionary { { "diff", 10 } }).Cast().ToList(); 803 | var dynZip3 = _query.Zip(lineCounts, "(o, l) => o.id + l + @0", _ignoreCaseSettings, 10).Cast().ToList(); 804 | 805 | Assert.Equal(zip, dynZip1); 806 | Assert.Equal(zip, dynZip2); 807 | Assert.Equal(zip, dynZip3); 808 | 809 | Assert.Throws(() => Dyn.Zip(null!, null!, "")); 810 | Assert.Throws(() => _query.Zip(null!, "")); 811 | Assert.Throws(() => _query.Zip([], "")); 812 | } 813 | 814 | [Fact] 815 | public void ShouldHandleEnumerableParameter() { 816 | var source = new[] { 1, 2, 3, 4, 5 }; 817 | var sample = new[] { 2, 4 }; 818 | var result = source.AsQueryable().Where($"i => @0.Contains(i)", sample).ToList(); 819 | 820 | Assert.Equal([2, 4], result); 821 | } 822 | 823 | [Fact] 824 | public void ShouldHandleBooleanOperatorGrouping() { 825 | var list = new List { 826 | new() { 827 | IsActive = true, 828 | IsSomething = 1 829 | }, 830 | new() { 831 | IsActive = false, 832 | IsSomething = 1 833 | }, 834 | new() { 835 | IsActive = true, 836 | IsSomething = 2 837 | }, 838 | new() { 839 | IsActive = false, 840 | IsSomething = 2 841 | }, 842 | }.AsQueryable(); 843 | 844 | var linqResult = list.Where(o => (o.IsActive && o.IsSomething == 1) || (o.IsActive == false && o.IsSomething == 2)).ToArray(); 845 | var dynamicResult = list.Where("o => (o.IsActive && o.IsSomething == 1) || (o.IsActive == false && o.IsSomething == 2)").ToArray(); 846 | 847 | Assert.Equal(linqResult, dynamicResult); 848 | } 849 | 850 | [Fact] 851 | public void NullableTests() { 852 | const string searchText = "1"; 853 | var data = new List { 854 | new() { Name = "proof search 1 me" }, 855 | new() { Name = "This has age 1", Age = 1 }, 856 | new() { Name = "This has non null number 1", Number = 1 }, 857 | new() { Name = "This address has zip 1", Address = new Address { Zip = 1 } }, 858 | new() { Name = "This address has non null number 1", Address = new Address { Number = 1 } } 859 | }.AsQueryable(); 860 | var parameters = new Dictionary { { "searchText", searchText } }; 861 | 862 | // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract 863 | var r1 = data.Where(i => i.Name != null && i.Name.Contains(searchText)).ToList(); 864 | var d1 = data.Where("i => i.Name != null && i.Name.Contains(searchText)", parameters).ToList(); 865 | Assert.Equal(r1, d1); 866 | 867 | var r2 = data.OrderByDescending(i => i.Name).ToList(); 868 | var d2 = data.OrderByDescending("i => i.Name").ToList(); 869 | Assert.Equal(r2, d2); 870 | 871 | #pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' 872 | var r3 = data.Where(i => i.Age != null && i.Age.ToString().Contains(searchText)).ToList(); 873 | #pragma warning restore CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' 874 | var d3 = data.Where("i => i.Age != null && i.Age.ToString().Contains(searchText)", parameters).ToList(); 875 | Assert.Equal(r3, d3); 876 | 877 | var r4 = data.Where(i => i.Number != null && i.Number!.ToString()!.Contains(searchText)).ToList(); 878 | var d4 = data.Where("i => i.Number != null && i.Number.ToString().Contains(searchText)", parameters).ToList(); 879 | Assert.Equal(r4, d4); 880 | 881 | #pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' 882 | var r5 = data.Where(i => i.Address != null! && i.Address.Zip != null && i.Address.Zip.ToString().Contains(searchText)).ToList(); 883 | #pragma warning restore CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' 884 | var d5 = data.Where("i => i.Address != null && i.Address.Zip != null && i.Address.Zip.ToString().Contains(searchText)", parameters).ToList(); 885 | Assert.Equal(r5, d5); 886 | 887 | var r6 = data.Where(i => i.Address != null! && i.Address.Number != null && i.Address.Number!.ToString()!.Contains(searchText)).ToList(); 888 | var d6 = data.Where("i => i.Address != null && i.Address.Number != null && i.Address.Number.ToString().Contains(searchText)", parameters).ToList(); 889 | Assert.Equal(r6, d6); 890 | 891 | var func = Evaluator.ToFunc( 892 | "i.Age != null && i.Age.ToString().Contains(searchText)", 893 | new Dictionary {{ "searchText", searchText }, { "i", data.First() }} 894 | ); 895 | Assert.False(func()); 896 | } 897 | } 898 | --------------------------------------------------------------------------------