├── .gitattributes
├── .gitignore
├── DynamicQuery.sln
├── LICENSE.md
├── PoweredSoft.DynamicQuery.AspNetCore.NewtonsoftJson
├── MvcBuilderExtensions.cs
└── PoweredSoft.DynamicQuery.AspNetCore.NewtonsoftJson.csproj
├── PoweredSoft.DynamicQuery.AspNetCore
├── MvcBuilderExtensions.cs
└── PoweredSoft.DynamicQuery.AspNetCore.csproj
├── PoweredSoft.DynamicQuery.Cli
├── PoweredSoft.DynamicQuery.Cli.csproj
└── Program.cs
├── PoweredSoft.DynamicQuery.Core
├── AggregateType.cs
├── FilterType.cs
├── IAfterReadInterceptor.cs
├── IAggregate.cs
├── IAggregateInterceptor.cs
├── IBeforeQueryFilterInterceptor.cs
├── ICompositeFilter.cs
├── IFilter.cs
├── IFilterInterceptor.cs
├── IGroup.cs
├── IGroupingInterceptor.cs
├── IIncludeStrategyInterceptor.cs
├── IInterceptQueryExecutionOptions.cs
├── INoSortInterceptor.cs
├── IQueryConvertInterceptor.cs
├── IQueryCriteria.cs
├── IQueryExecutionOptions.cs
├── IQueryHandler.cs
├── IQueryInterceptor.cs
├── IQueryInterceptorProvider.cs
├── IQueryResult.cs
├── ISimpleFilter.cs
├── ISort.cs
├── ISortInterceptor.cs
├── PoweredSoft.DynamicQuery.Core.csproj
└── QueryExecutionOptions.cs
├── PoweredSoft.DynamicQuery.NewtonsoftJson
├── DynamicQueryJsonConverter.cs
├── Extensions.cs
└── PoweredSoft.DynamicQuery.NewtonsoftJson.csproj
├── PoweredSoft.DynamicQuery.Test
├── AggregateInterceptorTests.cs
├── AggregateTests.cs
├── AsyncTests.cs
├── BeforeFilterTests.cs
├── ConvertibleInterceptorTests.cs
├── CriteriaTests.cs
├── DeserializeTests.cs
├── FilterInterceptorTests.cs
├── FilterTests.cs
├── GroupInterceptorTests.cs
├── GroupTests.cs
├── IncludeStrategyTests.cs
├── Mock
│ ├── Entities.cs
│ ├── MockContext.cs
│ ├── MockContextFactory.cs
│ ├── TestSeeders.cs
│ └── Ticket.cs
├── MockQueryExecutionOptionsInterceptor.cs
├── NoSortTests.cs
├── PoweredSoft.DynamicQuery.Test.csproj
├── QueryProviderTests.cs
├── SortInterceptorTests.cs
└── SortTests.cs
├── PoweredSoft.DynamicQuery
├── Aggregate.cs
├── Extensions
│ ├── FilterExtensions.cs
│ ├── FilterTypeExtensions.cs
│ └── GroupResultExtensions.cs
├── Filter.cs
├── Group.cs
├── PoweredSoft.DynamicQuery.csproj
├── QueryCriteria.cs
├── QueryHandler.cs
├── QueryHandlerAsync.cs
├── QueryHandlerBase.cs
├── QueryInterceptorEqualityComparer.cs
├── QueryResult.cs
├── ServiceCollectionExtensions.cs
└── Sort.cs
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
--------------------------------------------------------------------------------
/DynamicQuery.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29326.143
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.DynamicQuery.Core", "PoweredSoft.DynamicQuery.Core\PoweredSoft.DynamicQuery.Core.csproj", "{E614658D-6852-4405-B5BE-3695C3E96B13}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.DynamicQuery", "PoweredSoft.DynamicQuery\PoweredSoft.DynamicQuery.csproj", "{A9F74387-6B09-423A-96BC-F8FF345193EE}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.DynamicQuery.Cli", "PoweredSoft.DynamicQuery.Cli\PoweredSoft.DynamicQuery.Cli.csproj", "{7FC0F790-A8B9-4335-8D72-09797DEB0359}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BD742C81-D26A-466C-AB9B-840996D59FA6}"
13 | ProjectSection(SolutionItems) = preProject
14 | LICENSE.md = LICENSE.md
15 | README.md = README.md
16 | EndProjectSection
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.DynamicQuery.Test", "PoweredSoft.DynamicQuery.Test\PoweredSoft.DynamicQuery.Test.csproj", "{3EAD8217-8E10-4261-9055-50444905922C}"
19 | EndProject
20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.DynamicQuery.AspNetCore", "PoweredSoft.DynamicQuery.AspNetCore\PoweredSoft.DynamicQuery.AspNetCore.csproj", "{DF58BD18-AB47-4018-B1EA-D1118D93B408}"
21 | EndProject
22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.DynamicQuery.NewtonsoftJson", "PoweredSoft.DynamicQuery.NewtonsoftJson\PoweredSoft.DynamicQuery.NewtonsoftJson.csproj", "{201D7ACB-E11A-47EE-80F6-3F6BFB947DCA}"
23 | EndProject
24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.DynamicQuery.AspNetCore.NewtonsoftJson", "PoweredSoft.DynamicQuery.AspNetCore.NewtonsoftJson\PoweredSoft.DynamicQuery.AspNetCore.NewtonsoftJson.csproj", "{7BBEEE93-A3DB-443B-8254-E148AC9663EA}"
25 | EndProject
26 | Global
27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
28 | Debug|Any CPU = Debug|Any CPU
29 | Release|Any CPU = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
32 | {E614658D-6852-4405-B5BE-3695C3E96B13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {E614658D-6852-4405-B5BE-3695C3E96B13}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {E614658D-6852-4405-B5BE-3695C3E96B13}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {E614658D-6852-4405-B5BE-3695C3E96B13}.Release|Any CPU.Build.0 = Release|Any CPU
36 | {A9F74387-6B09-423A-96BC-F8FF345193EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {A9F74387-6B09-423A-96BC-F8FF345193EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {A9F74387-6B09-423A-96BC-F8FF345193EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {A9F74387-6B09-423A-96BC-F8FF345193EE}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {7FC0F790-A8B9-4335-8D72-09797DEB0359}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {7FC0F790-A8B9-4335-8D72-09797DEB0359}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {7FC0F790-A8B9-4335-8D72-09797DEB0359}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {7FC0F790-A8B9-4335-8D72-09797DEB0359}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {3EAD8217-8E10-4261-9055-50444905922C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {3EAD8217-8E10-4261-9055-50444905922C}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {3EAD8217-8E10-4261-9055-50444905922C}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {3EAD8217-8E10-4261-9055-50444905922C}.Release|Any CPU.Build.0 = Release|Any CPU
48 | {DF58BD18-AB47-4018-B1EA-D1118D93B408}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {DF58BD18-AB47-4018-B1EA-D1118D93B408}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {DF58BD18-AB47-4018-B1EA-D1118D93B408}.Release|Any CPU.ActiveCfg = Release|Any CPU
51 | {DF58BD18-AB47-4018-B1EA-D1118D93B408}.Release|Any CPU.Build.0 = Release|Any CPU
52 | {201D7ACB-E11A-47EE-80F6-3F6BFB947DCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
53 | {201D7ACB-E11A-47EE-80F6-3F6BFB947DCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
54 | {201D7ACB-E11A-47EE-80F6-3F6BFB947DCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
55 | {201D7ACB-E11A-47EE-80F6-3F6BFB947DCA}.Release|Any CPU.Build.0 = Release|Any CPU
56 | {7BBEEE93-A3DB-443B-8254-E148AC9663EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
57 | {7BBEEE93-A3DB-443B-8254-E148AC9663EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
58 | {7BBEEE93-A3DB-443B-8254-E148AC9663EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
59 | {7BBEEE93-A3DB-443B-8254-E148AC9663EA}.Release|Any CPU.Build.0 = Release|Any CPU
60 | EndGlobalSection
61 | GlobalSection(SolutionProperties) = preSolution
62 | HideSolutionNode = FALSE
63 | EndGlobalSection
64 | GlobalSection(ExtensibilityGlobals) = postSolution
65 | SolutionGuid = {3770CB3C-28E4-4C15-A537-4DA38CFBA4F3}
66 | EndGlobalSection
67 | EndGlobal
68 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Powered Softwares Inc.
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 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.AspNetCore.NewtonsoftJson/MvcBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using PoweredSoft.DynamicQuery.NewtonsoftJson;
3 |
4 | namespace PoweredSoft.DynamicQuery.AspNetCore.NewtonsoftJson
5 | {
6 | public static class MvcBuilderExtensions
7 | {
8 | public static IMvcBuilder AddPoweredSoftJsonNetDynamicQuery(this IMvcBuilder mvcBuilder, bool enableStringEnumConverter = true)
9 | {
10 | mvcBuilder.AddPoweredSoftDynamicQuery();
11 | var serviceProvider = mvcBuilder.Services.BuildServiceProvider();
12 |
13 | mvcBuilder.AddNewtonsoftJson(o =>
14 | {
15 | o.SerializerSettings.AddPoweredSoftDynamicQueryNewtonsoftJson(serviceProvider, enableStringEnumConverter: enableStringEnumConverter);
16 | });
17 |
18 | return mvcBuilder;
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.AspNetCore.NewtonsoftJson/PoweredSoft.DynamicQuery.AspNetCore.NewtonsoftJson.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1;net5.0
5 | Powered Softwares Inc.
6 | MIT
7 | https://github.com/PoweredSoft/DynamicQuery
8 | https://github.com/PoweredSoft/DynamicQuery
9 | github
10 | powered,soft,dynamic,criteria,query,builder,asp,net,core
11 | 3.0.0$(VersionSuffix)
12 | https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&r=g&d=retro
13 | This projects makes it easier to use dynamic query in a asp.net core mvc project.
14 | PoweredSoft.DynamicQuery.AspNetCore.NewtonsoftJson
15 | False
16 | Powered Soft
17 | David Lebee
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.AspNetCore/MvcBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using PoweredSoft.Data;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Text;
6 |
7 | namespace PoweredSoft.DynamicQuery.AspNetCore
8 | {
9 | public static class MvcBuilderExtensions
10 | {
11 | public static IMvcBuilder AddPoweredSoftDynamicQuery(this IMvcBuilder builder)
12 | {
13 | builder.Services.AddPoweredSoftDataServices();
14 | builder.Services.AddPoweredSoftDynamicQuery();
15 | return builder;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.AspNetCore/PoweredSoft.DynamicQuery.AspNetCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.1;net5.0
5 | Powered Softwares Inc.
6 | MIT
7 | https://github.com/PoweredSoft/DynamicQuery
8 | https://github.com/PoweredSoft/DynamicQuery
9 | github
10 | powered,soft,dynamic,criteria,query,builder,asp,net,core
11 | 3.0.0$(VersionSuffix)
12 | https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&r=g&d=retro
13 | PoweredSoft.DynamicQuery.AspNetCore
14 | This projects makes it easier to use dynamic query in a asp.net core mvc project.
15 | PoweredSoft.DynamicQuery.AspNetCore
16 | False
17 | Powered Soft
18 | David Lebee
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Cli/PoweredSoft.DynamicQuery.Cli.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net5.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Cli/Program.cs:
--------------------------------------------------------------------------------
1 | using PoweredSoft.DynamicQuery.Core;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using Newtonsoft.Json;
6 | using Newtonsoft.Json.Converters;
7 |
8 | namespace PoweredSoft.DynamicQuery.Cli
9 | {
10 | public class PersonQueryInterceptor : IQueryInterceptor
11 | , IAggregateInterceptor
12 | , IQueryConvertInterceptor
13 | //, IBeforeQueryAlteredInterceptor
14 | //, IFilterInterceptor
15 | {
16 | public IQueryable InterceptQueryBeforeAltered(IQueryCriteria criteria, IQueryable queryable)
17 | => queryable.Where(t => t.FirstName.StartsWith("Da"));
18 |
19 | public IFilter InterceptFilter(IFilter filter)
20 | {
21 | if (filter is SimpleFilter)
22 | {
23 | var simpleFilter = filter as ISimpleFilter;
24 | if (simpleFilter.Path == "FirstName" && simpleFilter.Value is string && ((string)simpleFilter.Value).Contains(","))
25 | {
26 | var firstNames = ((string) simpleFilter.Value).Split(',');
27 | var filters = firstNames.Select(firstName => new SimpleFilter
28 | {
29 | Path = "FirstName",
30 | Type = FilterType.Equal,
31 | Value = firstName
32 | }).Cast().ToList();
33 |
34 | return new CompositeFilter
35 | {
36 | Type = FilterType.Composite,
37 | Filters = filters,
38 | And = true
39 | };
40 | }
41 | }
42 |
43 | return filter;
44 | }
45 |
46 | public IAggregate InterceptAggregate(IAggregate aggregate)
47 | {
48 | if (aggregate.Path == nameof(PersonModel.AgeStr))
49 | return new Aggregate {Type = aggregate.Type, Path = nameof(Person.Age)};
50 | return aggregate;
51 | }
52 |
53 | public object InterceptResultTo(Person entity)
54 | {
55 | var personModel = new PersonModel();
56 | personModel.Id = entity.Id;
57 | personModel.FirstName = entity.FirstName;
58 | personModel.LastName = entity.LastName;
59 | personModel.Age = entity.Age;
60 | personModel.Sex = entity.Sex;
61 | return personModel;
62 | }
63 | }
64 |
65 | public class Person
66 | {
67 | public int Id { get; set; }
68 | public string FirstName { get; set; }
69 | public string LastName { get; set; }
70 | public int Age { get; set; }
71 | public string Sex { get; set; }
72 | }
73 |
74 | public class PersonModel
75 | {
76 | public int Id { get; set; }
77 | public string FirstName { get; set; }
78 | public string LastName { get; set; }
79 | public int Age { get; set; }
80 | public string Sex { get; set; }
81 | public string AgeStr => $"{Age} years old";
82 | public string FullName => $"{FirstName} {LastName}";
83 | }
84 |
85 | class Program
86 | {
87 | static void Main(string[] args)
88 | {
89 | Play1();
90 |
91 |
92 | }
93 |
94 | private static void Play1()
95 | {
96 | var list = new List()
97 | {
98 | new Person{ Id = 1, FirstName = "David", LastName = "Lebee", Sex = "Male", Age = 29 },
99 | new Person{ Id = 2, FirstName = "Michaela", LastName = "Lebee", Sex = "Female", Age = 29},
100 | new Person{ Id = 3, FirstName = "Zohra", LastName = "Lebee", Sex = "Female", Age = 20},
101 | new Person{ Id = 4, FirstName = "Eric", LastName = "Vickar", Sex = "Male", Age = 30},
102 | new Person{ Id = 5, FirstName = "Susan", LastName = "Vickar", Sex = "Female", Age = 30},
103 | };
104 |
105 | var queryable = list.AsQueryable();
106 | var criteria = new QueryCriteria();
107 | criteria.Page = 1;
108 | criteria.PageSize = 10;
109 |
110 | criteria.Groups = new List()
111 | {
112 | new Group { Path = "LastName" },
113 | new Group { Path = "Sexe" }
114 | };
115 |
116 | criteria.Aggregates = new List()
117 | {
118 | new Aggregate { Type = AggregateType.Count },
119 | new Aggregate { Path = "AgeStr", Type = AggregateType.Avg }
120 | };;
121 |
122 | var handler = new QueryHandler(Enumerable.Empty());
123 | handler.AddInterceptor(new PersonQueryInterceptor());
124 | var result = handler.Execute(queryable, criteria);
125 |
126 | var jsonSettings = new JsonSerializerSettings()
127 | {
128 | ReferenceLoopHandling = ReferenceLoopHandling.Ignore
129 | };
130 |
131 | jsonSettings.Converters.Add(new StringEnumConverter { AllowIntegerValues = false });
132 |
133 | Console.WriteLine("Request:\n");
134 | Console.WriteLine(JsonConvert.SerializeObject(criteria, Formatting.Indented, jsonSettings));
135 | Console.WriteLine("");
136 | Console.WriteLine("Response:\n");
137 | Console.WriteLine(JsonConvert.SerializeObject(result, Formatting.Indented, jsonSettings));
138 | Console.ReadKey();
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/AggregateType.cs:
--------------------------------------------------------------------------------
1 | namespace PoweredSoft.DynamicQuery.Core
2 | {
3 | public enum AggregateType
4 | {
5 | Count,
6 | Sum,
7 | Avg,
8 | LongCount,
9 | Min,
10 | Max,
11 | First,
12 | FirstOrDefault,
13 | Last,
14 | LastOrDefault
15 | }
16 | }
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/FilterType.cs:
--------------------------------------------------------------------------------
1 | namespace PoweredSoft.DynamicQuery.Core
2 | {
3 | public enum FilterType
4 | {
5 | Equal,
6 | Contains,
7 | StartsWith,
8 | EndsWith,
9 | Composite,
10 | NotEqual,
11 | GreaterThan,
12 | LessThanOrEqual,
13 | GreaterThanOrEqual,
14 | LessThan,
15 | In,
16 | NotIn
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IAfterReadInterceptor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace PoweredSoft.DynamicQuery.Core
7 | {
8 | public interface IAfterReadEntityInterceptor : IQueryInterceptor
9 | {
10 | void AfterReadEntity(List entities);
11 | }
12 |
13 | public interface IAfterReadEntityInterceptorAsync : IQueryInterceptor
14 | {
15 | Task AfterReadEntityAsync(List entities, CancellationToken cancellationToken = default(CancellationToken));
16 | }
17 |
18 | public interface IAfterReadInterceptor : IQueryInterceptor
19 | {
20 | void AfterRead(List> pairs);
21 | }
22 |
23 | public interface IAfterReadInterceptorAsync : IQueryInterceptor
24 | {
25 | Task AfterReadAsync(List> pairs, CancellationToken cancellationToken = default(CancellationToken));
26 | }
27 |
28 | public interface IAfterReadInterceptor : IQueryInterceptor
29 | {
30 | void AfterRead(List> pairs);
31 | }
32 |
33 | public interface IAfterReadInterceptorAsync : IQueryInterceptor
34 | {
35 | Task AfterReadAsync(List> pairs, CancellationToken cancellationToken = default(CancellationToken));
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IAggregate.cs:
--------------------------------------------------------------------------------
1 | namespace PoweredSoft.DynamicQuery.Core
2 | {
3 | public interface IAggregate
4 | {
5 | string Path { get; set; }
6 | AggregateType Type { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IAggregateInterceptor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace PoweredSoft.DynamicQuery.Core
6 | {
7 | public interface IAggregateInterceptor : IQueryInterceptor
8 | {
9 | IAggregate InterceptAggregate(IAggregate aggregate);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IBeforeQueryFilterInterceptor.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 |
3 | namespace PoweredSoft.DynamicQuery.Core
4 | {
5 | public interface IBeforeQueryFilterInterceptor : IQueryInterceptor
6 | {
7 | IQueryable InterceptBeforeFiltering(IQueryCriteria criteria, IQueryable queryable);
8 | }
9 |
10 | public interface IBeforeQueryFilterInterceptor : IQueryInterceptor
11 | {
12 | IQueryable InterceptBeforeFiltering(IQueryCriteria criteria, IQueryable queryable);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/ICompositeFilter.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace PoweredSoft.DynamicQuery.Core
4 | {
5 | public interface ICompositeFilter : IFilter
6 | {
7 | List Filters { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IFilter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace PoweredSoft.DynamicQuery.Core
4 | {
5 | public interface IFilter
6 | {
7 | bool? And { get; set; }
8 | FilterType Type { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IFilterInterceptor.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace PoweredSoft.DynamicQuery.Core
4 | {
5 | public interface IFilterInterceptor : IQueryInterceptor
6 | {
7 | IFilter InterceptFilter(IFilter filter);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IGroup.cs:
--------------------------------------------------------------------------------
1 | namespace PoweredSoft.DynamicQuery.Core
2 | {
3 | public interface IGroup
4 | {
5 | string Path { get; set; }
6 | bool? Ascending { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IGroupingInterceptor.cs:
--------------------------------------------------------------------------------
1 | namespace PoweredSoft.DynamicQuery.Core
2 | {
3 | public interface IGroupInterceptor : IQueryInterceptor
4 | {
5 | IGroup InterceptGroup(IGroup group);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IIncludeStrategyInterceptor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace PoweredSoft.DynamicQuery.Core
7 | {
8 | public interface IIncludeStrategyInterceptor : IQueryInterceptor
9 | {
10 | IQueryable InterceptIncludeStrategy(IQueryCriteria criteria, IQueryable queryable);
11 | }
12 |
13 | public interface IIncludeStrategyInterceptor : IQueryInterceptor
14 | {
15 | IQueryable InterceptIncludeStrategy(IQueryCriteria criteria, IQueryable queryable);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IInterceptQueryExecutionOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace PoweredSoft.DynamicQuery.Core
7 | {
8 | public interface IQueryExecutionOptionsInterceptor : IQueryInterceptor
9 | {
10 | IQueryExecutionOptions InterceptQueryExecutionOptions(IQueryable queryable, IQueryExecutionOptions current);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/INoSortInterceptor.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 |
3 | namespace PoweredSoft.DynamicQuery.Core
4 | {
5 | public interface INoSortInterceptor : IQueryInterceptor
6 | {
7 | IQueryable InterceptNoSort(IQueryCriteria criteria, IQueryable queryable);
8 | }
9 |
10 | public interface INoSortInterceptor : IQueryInterceptor
11 | {
12 | IQueryable InterceptNoSort(IQueryCriteria criteria, IQueryable queryable);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IQueryConvertInterceptor.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace PoweredSoft.DynamicQuery.Core
6 | {
7 | public interface IQueryConvertInterceptor : IQueryInterceptor
8 | {
9 | object InterceptResultTo(object entity);
10 | }
11 |
12 | public interface IQueryConvertInterceptor : IQueryInterceptor
13 | {
14 | object InterceptResultTo(T entity);
15 | }
16 |
17 | public interface IQueryConvertInterceptor : IQueryInterceptor
18 | {
19 | T2 InterceptResultTo(T entity);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IQueryCriteria.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace PoweredSoft.DynamicQuery.Core
6 | {
7 | public interface IQueryCriteria
8 | {
9 | int? Page { get; set; }
10 | int? PageSize { get; set; }
11 | List Sorts { get; set; }
12 | List Filters { get; set; }
13 | List Groups { get; set; }
14 | List Aggregates { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IQueryExecutionOptions.cs:
--------------------------------------------------------------------------------
1 | namespace PoweredSoft.DynamicQuery.Core
2 | {
3 | public interface IQueryExecutionOptions
4 | {
5 | bool GroupByInMemory { get; set; }
6 | bool GroupByInMemoryNullCheck { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IQueryHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | namespace PoweredSoft.DynamicQuery.Core
9 | {
10 | public interface IInterceptableQueryHandler
11 | {
12 | void AddInterceptor(IQueryInterceptor interceptor);
13 | IReadOnlyList ResolveInterceptors(IQueryCriteria criteria, IQueryable queryable);
14 | }
15 |
16 | public interface IQueryHandler : IInterceptableQueryHandler
17 | {
18 | IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria);
19 | IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria);
20 | IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria, IQueryExecutionOptions options);
21 | IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria, IQueryExecutionOptions options);
22 | }
23 |
24 | public interface IQueryHandlerAsync : IInterceptableQueryHandler
25 | {
26 | Task> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default);
27 | Task> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default);
28 | Task> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria, IQueryExecutionOptions options, CancellationToken cancellationToken = default);
29 | Task> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria, IQueryExecutionOptions options, CancellationToken cancellationToken = default);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IQueryInterceptor.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 |
3 | namespace PoweredSoft.DynamicQuery.Core
4 | {
5 | public interface IQueryInterceptor
6 | {
7 |
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IQueryInterceptorProvider.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace PoweredSoft.DynamicQuery.Core
5 | {
6 | public interface IQueryInterceptorProvider
7 | {
8 | IEnumerable GetInterceptors(IQueryCriteria queryCriteria, IQueryable queryable);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/IQueryResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace PoweredSoft.DynamicQuery.Core
6 | {
7 | public interface IAggregateResult
8 | {
9 | string Path { get; set; }
10 | AggregateType Type { get; set; }
11 | object Value { get; set; }
12 | }
13 |
14 | public interface IQueryResult
15 | {
16 | List Aggregates { get; }
17 | List Data { get; }
18 | }
19 |
20 | public interface IGroupQueryResult : IQueryResult
21 | {
22 | string GroupPath { get; set; }
23 | object GroupValue { get; set; }
24 | bool HasSubGroups { get; }
25 | List> SubGroups { get; set; }
26 | }
27 |
28 | public interface IQueryExecutionResultPaging
29 | {
30 | long TotalRecords { get; set; }
31 | long? NumberOfPages { get; set; }
32 | }
33 |
34 | public interface IQueryExecutionResult : IQueryResult, IQueryExecutionResultPaging
35 | {
36 |
37 | }
38 |
39 | public interface IQueryExecutionGroupResult : IQueryExecutionResult
40 | {
41 | List> Groups { get; set; }
42 | }
43 |
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/ISimpleFilter.cs:
--------------------------------------------------------------------------------
1 | namespace PoweredSoft.DynamicQuery.Core
2 | {
3 | public interface ISimpleFilter : IFilter
4 | {
5 | string Path { get; set; }
6 | object Value { get; set; }
7 | bool? Not { get; set; }
8 | bool? CaseInsensitive { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/ISort.cs:
--------------------------------------------------------------------------------
1 | namespace PoweredSoft.DynamicQuery.Core
2 | {
3 | public interface ISort
4 | {
5 | string Path { get; set; }
6 | bool? Ascending { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/ISortInterceptor.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace PoweredSoft.DynamicQuery.Core
4 | {
5 | public interface ISortInterceptor : IQueryInterceptor
6 | {
7 | IEnumerable InterceptSort(IEnumerable sort);
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/PoweredSoft.DynamicQuery.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | Powered Softwares Inc.
6 | MIT
7 | https://github.com/PoweredSoft/DynamicQuery
8 | https://github.com/PoweredSoft/DynamicQuery.Core/
9 | github
10 | powered,soft,dynamic,criteria,query,builder
11 | 3.0.0$(VersionSuffix)
12 | https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&r=g&d=retro
13 | PoweredSoft.DynamicQuery.Core
14 | core abstractions
15 | PoweredSoft.DynamicQuery.Core
16 | False
17 | Poweredsoft
18 | David Lebee
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Core/QueryExecutionOptions.cs:
--------------------------------------------------------------------------------
1 | namespace PoweredSoft.DynamicQuery.Core
2 | {
3 | public class QueryExecutionOptions : IQueryExecutionOptions
4 | {
5 | public bool GroupByInMemory { get; set; } = false;
6 | public bool GroupByInMemoryNullCheck { get; set; } = false;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.NewtonsoftJson/DynamicQueryJsonConverter.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Newtonsoft.Json.Linq;
3 | using PoweredSoft.DynamicQuery.Core;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Threading.Tasks;
8 |
9 | namespace PoweredSoft.DynamicQuery.NewtonsoftJson
10 | {
11 | public class DynamicQueryJsonConverter : JsonConverter
12 | {
13 | public override bool CanRead => true;
14 | public override bool CanWrite => false;
15 |
16 | private Type[] DynamicQueryTypes { get; } = new Type[]
17 | {
18 | typeof(IFilter),
19 | typeof(ISimpleFilter),
20 | typeof(ICompositeFilter),
21 | typeof(IAggregate),
22 | typeof(ISort),
23 | typeof(IGroup),
24 | typeof(IQueryCriteria),
25 | typeof(IQueryHandler)
26 | };
27 |
28 | public IServiceProvider ServiceProvider { get; }
29 |
30 | public DynamicQueryJsonConverter(IServiceProvider serviceProvider)
31 | {
32 | ServiceProvider = serviceProvider;
33 | }
34 |
35 | public override bool CanConvert(Type objectType) => objectType.IsInterface && DynamicQueryTypes.Contains(objectType);
36 |
37 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
38 | {
39 | if (reader.TokenType == JsonToken.Null)
40 | return (object)null;
41 |
42 | if (objectType == typeof(IFilter))
43 | {
44 | var jo = JObject.Load(reader);
45 |
46 | bool isComposite = false;
47 | if (jo.ContainsKey("type"))
48 | {
49 | isComposite = jo.GetValue("type").Value()
50 | .Equals("composite", StringComparison.OrdinalIgnoreCase);
51 | }
52 | else if (jo.ContainsKey("Type"))
53 | {
54 | isComposite = jo.GetValue("Type").Value()
55 | .Equals("composite", StringComparison.OrdinalIgnoreCase);
56 | }
57 | else
58 | {
59 | throw new Exception("IFilter should have a type property..");
60 | }
61 |
62 | var filterObj = ServiceProvider.GetService(isComposite ? typeof(ICompositeFilter) : typeof(ISimpleFilter));
63 | var filterType = filterObj.GetType();
64 | filterObj = jo.ToObject(filterType, serializer);
65 | return filterObj;
66 | }
67 |
68 | var obj = ServiceProvider.GetService(objectType);
69 | if (obj == null)
70 | throw new JsonSerializationException("No object created.");
71 |
72 | serializer.Populate(reader, obj);
73 | return obj;
74 | }
75 |
76 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
77 | {
78 | throw new NotImplementedException();
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.NewtonsoftJson/Extensions.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Newtonsoft.Json.Converters;
3 | using System;
4 |
5 | namespace PoweredSoft.DynamicQuery.NewtonsoftJson
6 | {
7 | public static class JsonNetSerializationSettingsExtensions
8 | {
9 | public static JsonSerializerSettings AddPoweredSoftDynamicQueryNewtonsoftJson(this JsonSerializerSettings settings, IServiceProvider serviceProvider, bool enableStringEnumConverter = true)
10 | {
11 | if (enableStringEnumConverter)
12 | settings.Converters.Add(new StringEnumConverter());
13 |
14 | settings.Converters.Add(new DynamicQueryJsonConverter(serviceProvider));
15 | return settings;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.NewtonsoftJson/PoweredSoft.DynamicQuery.NewtonsoftJson.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | Powered Softwares Inc.
6 | MIT
7 | https://github.com/PoweredSoft/DynamicQuery
8 | https://github.com/PoweredSoft/DynamicQuery.Core/
9 | github
10 | powered,soft,dynamic,criteria,query,builder
11 | 3.0.0$(VersionSuffix)
12 | https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&r=g&d=retro
13 | PoweredSoft.DynamicQuery.Newtonsoft.Json
14 | False
15 | Poweredsoft
16 | David Lebee
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/AggregateInterceptorTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using PoweredSoft.DynamicQuery.Core;
3 | using PoweredSoft.DynamicQuery.Test.Mock;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using Xunit;
9 |
10 | namespace PoweredSoft.DynamicQuery.Test
11 | {
12 | public class AggregateInterceptorTests
13 | {
14 | private class MockAggregateInterceptor : IAggregateInterceptor
15 | {
16 | public IAggregate InterceptAggregate(IAggregate aggregate) => new Aggregate
17 | {
18 | Path = "Price",
19 | Type = AggregateType.Avg
20 | };
21 | }
22 |
23 | [Fact]
24 | public void Simple()
25 | {
26 | MockContextFactory.SeedAndTestContextFor("AggregatorInterceptorTests_Simple", TestSeeders.SimpleSeedScenario, ctx =>
27 | {
28 | var expected = ctx.Items
29 | .GroupBy(t => true)
30 | .Select(t => new
31 | {
32 | PriceAtTheTime = t.Average(t2 => t2.Price)
33 | }).First();
34 |
35 | var criteria = new QueryCriteria();
36 | criteria.Aggregates.Add(new Aggregate
37 | {
38 | Type = AggregateType.Avg,
39 | Path = "ItemPrice"
40 | });
41 | var queryHandler = new QueryHandler(Enumerable.Empty());
42 | queryHandler.AddInterceptor(new MockAggregateInterceptor());
43 | var result = queryHandler.Execute(ctx.Items, criteria);
44 | Assert.Equal(expected.PriceAtTheTime, result.Aggregates.First().Value);
45 | });
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/AggregateTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using PoweredSoft.DynamicQuery.Core;
3 | using PoweredSoft.DynamicQuery.Test.Mock;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using Xunit;
9 |
10 | namespace PoweredSoft.DynamicQuery.Test
11 | {
12 |
13 | public class AggregateTests
14 | {
15 | [Fact]
16 | public void WithoutGrouping()
17 | {
18 | MockContextFactory.SeedAndTestContextFor("AggregateTests_WithoutGrouping", TestSeeders.SimpleSeedScenario, ctx =>
19 | {
20 | var shouldResult = ctx.OrderItems
21 | .GroupBy(t => true)
22 | .Select(t => new
23 | {
24 | Count = t.Count(),
25 |
26 | ItemQuantityMin = t.Min(t2 => t2.Quantity),
27 | ItemQuantityMax = t.Min(t2 => t2.Quantity),
28 | ItemQuantityAverage = t.Average(t2 => t2.Quantity),
29 | ItemQuantitySum = t.Sum(t2 => t2.Quantity),
30 | AvgOfPrice = t.Average(t2 => t2.PriceAtTheTime),
31 | /* not supported by ef core 3.0
32 | First = t.First(),
33 | FirstOrDefault = t.FirstOrDefault(),
34 | Last = t.Last(),
35 | LastOrDefault = t.LastOrDefault()*/
36 | })
37 | .First();
38 |
39 | // query handler that is empty should be the same as running to list.
40 | var criteria = new QueryCriteria()
41 | {
42 | Aggregates = new List
43 | {
44 | new Aggregate { Type = AggregateType.Count },
45 | new Aggregate { Type = AggregateType.Avg, Path = "Quantity" },
46 | new Aggregate { Type = AggregateType.Sum, Path = "Quantity" },
47 | new Aggregate { Type = AggregateType.Avg, Path = "PriceAtTheTime"},
48 | new Aggregate { Type = AggregateType.Min, Path = "Quantity"},
49 | new Aggregate { Type = AggregateType.Max, Path = "Quantity" },
50 | /*not support by ef core 3.0
51 | new Aggregate { Type = AggregateType.First },
52 | new Aggregate { Type = AggregateType.FirstOrDefault },
53 | new Aggregate { Type = AggregateType.Last },
54 | new Aggregate { Type = AggregateType.LastOrDefault },
55 | */
56 | }
57 | };
58 |
59 | var queryHandler = new QueryHandler(Enumerable.Empty());
60 | var result = queryHandler.Execute(ctx.OrderItems, criteria, new QueryExecutionOptions
61 | {
62 | GroupByInMemory = true
63 | });
64 |
65 | var aggCount = result.Aggregates.First(t => t.Type == AggregateType.Count);
66 |
67 | /*
68 | var aggFirst = result.Aggregates.First(t => t.Type == AggregateType.First);
69 | var aggFirstOrDefault = result.Aggregates.First(t => t.Type == AggregateType.FirstOrDefault);
70 | var aggLast = result.Aggregates.First(t => t.Type == AggregateType.Last);
71 | var aggLastOrDefault = result.Aggregates.First(t => t.Type == AggregateType.LastOrDefault);*/
72 |
73 | var aggItemQuantityMin = result.Aggregates.First(t => t.Type == AggregateType.Min && t.Path == "Quantity");
74 | var aggItemQuantityMax = result.Aggregates.First(t => t.Type == AggregateType.Max && t.Path == "Quantity");
75 | var aggItemQuantityAverage = result.Aggregates.First(t => t.Type == AggregateType.Avg && t.Path == "Quantity");
76 | var aggItemQuantitySum = result.Aggregates.First(t => t.Type == AggregateType.Sum && t.Path == "Quantity");
77 | var aggAvgOfPrice = result.Aggregates.First(t => t.Type == AggregateType.Avg && t.Path == "PriceAtTheTime");
78 |
79 | Assert.Equal(shouldResult.Count, aggCount.Value);
80 | /*
81 | Assert.Equal(shouldResult.First?.Id, (aggFirst.Value as OrderItem)?.Id);
82 | Assert.Equal(shouldResult.FirstOrDefault?.Id, (aggFirstOrDefault.Value as OrderItem)?.Id);
83 | Assert.Equal(shouldResult.Last?.Id, (aggLast.Value as OrderItem)?.Id);
84 | Assert.Equal(shouldResult.LastOrDefault?.Id, (aggLastOrDefault.Value as OrderItem)?.Id);*/
85 |
86 | Assert.Equal(shouldResult.ItemQuantityAverage, aggItemQuantityAverage.Value);
87 | Assert.Equal(shouldResult.ItemQuantitySum, aggItemQuantitySum.Value);
88 | Assert.Equal(shouldResult.AvgOfPrice, aggAvgOfPrice.Value);
89 | });
90 | }
91 |
92 | [Fact]
93 | public void WithGrouping()
94 | {
95 | MockContextFactory.SeedAndTestContextFor("AggregateTests_WithGrouping", TestSeeders.SimpleSeedScenario, ctx =>
96 | {
97 | var shouldResults = ctx.OrderItems
98 | .GroupBy(t => t.Order.CustomerId)
99 | .Select(t => new
100 | {
101 | GroupValue = t.Key,
102 | Count = t.Count(),
103 | ItemQuantityAverage = t.Average(t2 => t2.Quantity),
104 | ItemQuantitySum = t.Sum(t2 => t2.Quantity),
105 | AvgOfPrice = t.Average(t2 => t2.PriceAtTheTime)
106 | })
107 | .ToList();
108 |
109 | // query handler that is empty should be the same as running to list.
110 | var criteria = new QueryCriteria()
111 | {
112 | Groups = new List
113 | {
114 | new Group { Path = "Order.CustomerId" }
115 | },
116 | Aggregates = new List
117 | {
118 | new Aggregate { Type = AggregateType.Count },
119 | new Aggregate { Type = AggregateType.Avg, Path = "Quantity" },
120 | new Aggregate { Type = AggregateType.Sum, Path = "Quantity" },
121 | new Aggregate { Type = AggregateType.Avg, Path = "PriceAtTheTime"}
122 | }
123 | };
124 |
125 | var queryHandler = new QueryHandler(Enumerable.Empty());
126 | var queryable = ctx.OrderItems.Include(t => t.Order);
127 | var result = queryHandler.Execute(queryable, criteria, new QueryExecutionOptions
128 | {
129 | GroupByInMemory = true
130 | });
131 |
132 | var groupedResult = result as IQueryExecutionGroupResult;
133 | Assert.NotNull(groupedResult);
134 |
135 | var groups = groupedResult.Groups;
136 |
137 | // validate group and aggregates of groups.
138 | Assert.Equal(groups.Count, shouldResults.Count);
139 | Assert.All(groups, g =>
140 | {
141 | var index = groups.IndexOf(g);
142 | var shouldResult = shouldResults[index];
143 |
144 | // validate the group value.
145 | Assert.Equal(g.GroupValue, shouldResult.GroupValue);
146 |
147 | // validate the group aggregates.
148 | var aggCount = g.Aggregates.First(t => t.Type == AggregateType.Count);
149 | var aggItemQuantityAverage = g.Aggregates.First(t => t.Type == AggregateType.Avg && t.Path == "Quantity");
150 | var aggItemQuantitySum = g.Aggregates.First(t => t.Type == AggregateType.Sum && t.Path == "Quantity");
151 | var aggAvgOfPrice = g.Aggregates.First(t => t.Type == AggregateType.Avg && t.Path == "PriceAtTheTime");
152 | Assert.Equal(shouldResult.Count, aggCount.Value);
153 | Assert.Equal(shouldResult.ItemQuantityAverage, aggItemQuantityAverage.Value);
154 | Assert.Equal(shouldResult.ItemQuantitySum, aggItemQuantitySum.Value);
155 | Assert.Equal(shouldResult.AvgOfPrice, aggAvgOfPrice.Value);
156 | });
157 | });
158 | }
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/AsyncTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using PoweredSoft.Data;
3 | using PoweredSoft.Data.EntityFrameworkCore;
4 | using PoweredSoft.DynamicQuery.Core;
5 | using PoweredSoft.DynamicQuery.Extensions;
6 | using PoweredSoft.DynamicQuery.Test.Mock;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Linq;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 | using Xunit;
13 | using static PoweredSoft.DynamicQuery.Test.GroupInterceptorTests;
14 |
15 | namespace PoweredSoft.DynamicQuery.Test
16 | {
17 | public class AsyncTests
18 | {
19 | [Fact]
20 | public void TestEmptyCriteria()
21 | {
22 | MockContextFactory.SeedAndTestContextFor("AsyncTests_TestEmptyCriteria", TestSeeders.SimpleSeedScenario, async ctx =>
23 | {
24 | var resultShouldMatch = ctx.Items.ToList();
25 | var queryable = ctx.Items.AsQueryable();
26 |
27 | // query handler that is empty should be the same as running to list.
28 | var aqf = new AsyncQueryableService(new[] { new AsyncQueryableHandlerService() });
29 | var criteria = new QueryCriteria();
30 | var queryHandler = new QueryHandlerAsync(aqf, Enumerable.Empty());
31 | var result = await queryHandler.ExecuteAsync(queryable, criteria);
32 | Assert.Equal(resultShouldMatch, result.Data);
33 | });
34 | }
35 |
36 | [Fact]
37 | public void WithGrouping()
38 | {
39 | MockContextFactory.SeedAndTestContextFor("AsyncTests_WithGrouping", TestSeeders.SimpleSeedScenario, async ctx =>
40 | {
41 | var shouldResults = ctx.OrderItems
42 | .GroupBy(t => t.Order.CustomerId)
43 | .Select(t => new
44 | {
45 | GroupValue = t.Key,
46 | Count = t.Count(),
47 | ItemQuantityAverage = t.Average(t2 => t2.Quantity),
48 | ItemQuantitySum = t.Sum(t2 => t2.Quantity),
49 | AvgOfPrice = t.Average(t2 => t2.PriceAtTheTime)
50 | })
51 | .ToList();
52 |
53 | // query handler that is empty should be the same as running to list.
54 | var criteria = new QueryCriteria()
55 | {
56 | Groups = new List
57 | {
58 | new Group { Path = "Order.CustomerId" }
59 | },
60 | Aggregates = new List
61 | {
62 | new Aggregate { Type = AggregateType.Count },
63 | new Aggregate { Type = AggregateType.Avg, Path = "Quantity" },
64 | new Aggregate { Type = AggregateType.Sum, Path = "Quantity" },
65 | new Aggregate { Type = AggregateType.Avg, Path = "PriceAtTheTime"}
66 | }
67 | };
68 | var asyncService = new AsyncQueryableService(new[] { new AsyncQueryableHandlerService() });
69 | var queryHandler = new QueryHandlerAsync(asyncService, Enumerable.Empty());
70 | var result = await queryHandler.ExecuteAsync(ctx.OrderItems.Include(t => t.Order.Customer), criteria, new QueryExecutionOptions
71 | {
72 | GroupByInMemory = true
73 | });
74 |
75 | var groups = result.GroupedResult().Groups;
76 |
77 | // validate group and aggregates of groups.
78 | Assert.Equal(groups.Count, shouldResults.Count);
79 | Assert.All(groups, g =>
80 | {
81 | var index = groups.IndexOf(g);
82 | var shouldResult = shouldResults[index];
83 |
84 | // validate the group value.
85 | Assert.Equal(g.GroupValue, shouldResult.GroupValue);
86 |
87 | // validate the group aggregates.
88 | var aggCount = g.Aggregates.First(t => t.Type == AggregateType.Count);
89 | var aggItemQuantityAverage = g.Aggregates.First(t => t.Type == AggregateType.Avg && t.Path == "Quantity");
90 | var aggItemQuantitySum = g.Aggregates.First(t => t.Type == AggregateType.Sum && t.Path == "Quantity");
91 | var aggAvgOfPrice = g.Aggregates.First(t => t.Type == AggregateType.Avg && t.Path == "PriceAtTheTime");
92 | Assert.Equal(shouldResult.Count, aggCount.Value);
93 | Assert.Equal(shouldResult.ItemQuantityAverage, aggItemQuantityAverage.Value);
94 | Assert.Equal(shouldResult.ItemQuantitySum, aggItemQuantitySum.Value);
95 | Assert.Equal(shouldResult.AvgOfPrice, aggAvgOfPrice.Value);
96 | });
97 | });
98 | }
99 |
100 | [Fact]
101 | public void SimpleFilter()
102 | {
103 | MockContextFactory.SeedAndTestContextFor("AsyncTests_SimpleFilter", TestSeeders.SimpleSeedScenario, async ctx =>
104 | {
105 | var resultShouldMatch = ctx.Items.Where(t => t.Name.EndsWith("Cables")).ToList();
106 |
107 | var criteria = new QueryCriteria()
108 | {
109 | Filters = new List
110 | {
111 | new SimpleFilter
112 | {
113 | Path = "Name",
114 | Type = FilterType.EndsWith,
115 | Value = "Cables"
116 | }
117 | }
118 | };
119 |
120 | var asyncService = new AsyncQueryableService(new[] { new AsyncQueryableHandlerService() });
121 | var queryHandler = new QueryHandlerAsync(asyncService, Enumerable.Empty());
122 | var result = await queryHandler.ExecuteAsync(ctx.Items, criteria);
123 | Assert.Equal(resultShouldMatch, result.Data);
124 | });
125 | }
126 |
127 | [Fact]
128 | public void SimpleFilterWithNot()
129 | {
130 | MockContextFactory.SeedAndTestContextFor("AsyncTests_SimpleFilter2", TestSeeders.SimpleSeedScenario, async ctx =>
131 | {
132 | var resultShouldMatch = ctx.Items.Where(t => !t.Name.EndsWith("Cables")).ToList();
133 |
134 | var criteria = new QueryCriteria()
135 | {
136 | Filters = new List
137 | {
138 | new SimpleFilter
139 | {
140 | Path = "Name",
141 | Type = FilterType.EndsWith,
142 | Value = "Cables",
143 | Not = true
144 | }
145 | }
146 | };
147 |
148 | var asyncService = new AsyncQueryableService(new[] { new AsyncQueryableHandlerService() });
149 | var queryHandler = new QueryHandlerAsync(asyncService, Enumerable.Empty());
150 | var result = await queryHandler.ExecuteAsync(ctx.Items, criteria);
151 | Assert.Equal(resultShouldMatch, result.Data);
152 | });
153 | }
154 |
155 | [Fact]
156 | public void TestPaging()
157 | {
158 | MockContextFactory.SeedAndTestContextFor("AsyncTests_TestPagging", TestSeeders.SimpleSeedScenario, async ctx =>
159 | {
160 | var resultShouldMatch = ctx.OrderItems.OrderBy(t => t.Id).Skip(5).Take(5).ToList();
161 |
162 | // query handler that is empty should be the same as running to list.
163 | var criteria = new QueryCriteria();
164 | criteria.Sorts.Add(new Sort("Id"));
165 | criteria.Page = 2;
166 | criteria.PageSize = 5;
167 |
168 | var asyncService = new AsyncQueryableService(new[] { new AsyncQueryableHandlerService() });
169 | var queryHandler = new QueryHandlerAsync(asyncService, Enumerable.Empty());
170 | var result = await queryHandler.ExecuteAsync(ctx.OrderItems, criteria);
171 | Assert.Equal(resultShouldMatch, result.Data);
172 | });
173 | }
174 |
175 | [Fact]
176 | public void WithGroupingInterceptorOptions()
177 | {
178 | MockContextFactory.SeedAndTestContextFor("AsyncTests_WithGroupingInterceptorOptions", TestSeeders.SimpleSeedScenario, async ctx =>
179 | {
180 | var shouldResults = ctx.OrderItems
181 | .GroupBy(t => t.Order.CustomerId)
182 | .Select(t => new
183 | {
184 | GroupValue = t.Key,
185 | Count = t.Count(),
186 | ItemQuantityAverage = t.Average(t2 => t2.Quantity),
187 | ItemQuantitySum = t.Sum(t2 => t2.Quantity),
188 | AvgOfPrice = t.Average(t2 => t2.PriceAtTheTime)
189 | })
190 | .ToList();
191 |
192 | // query handler that is empty should be the same as running to list.
193 | var criteria = new QueryCriteria()
194 | {
195 | Groups = new List
196 | {
197 | new Group { Path = "Order.CustomerId" }
198 | },
199 | Aggregates = new List
200 | {
201 | new Aggregate { Type = AggregateType.Count },
202 | new Aggregate { Type = AggregateType.Avg, Path = "Quantity" },
203 | new Aggregate { Type = AggregateType.Sum, Path = "Quantity" },
204 | new Aggregate { Type = AggregateType.Avg, Path = "PriceAtTheTime"}
205 | }
206 | };
207 | var asyncService = new AsyncQueryableService(new[] { new AsyncQueryableHandlerService() });
208 | var queryHandler = new QueryHandlerAsync(asyncService, Enumerable.Empty());
209 | queryHandler.AddInterceptor(new MockQueryExecutionOptionsInterceptor());
210 | var result = await queryHandler.ExecuteAsync(ctx.OrderItems.Include(t => t.Order.Customer), criteria);
211 |
212 | var groups = result.GroupedResult().Groups;
213 |
214 | // validate group and aggregates of groups.
215 | Assert.Equal(groups.Count, shouldResults.Count);
216 | Assert.All(groups, g =>
217 | {
218 | var index = groups.IndexOf(g);
219 | var shouldResult = shouldResults[index];
220 |
221 | // validate the group value.
222 | Assert.Equal(g.GroupValue, shouldResult.GroupValue);
223 |
224 | // validate the group aggregates.
225 | var aggCount = g.Aggregates.First(t => t.Type == AggregateType.Count);
226 | var aggItemQuantityAverage = g.Aggregates.First(t => t.Type == AggregateType.Avg && t.Path == "Quantity");
227 | var aggItemQuantitySum = g.Aggregates.First(t => t.Type == AggregateType.Sum && t.Path == "Quantity");
228 | var aggAvgOfPrice = g.Aggregates.First(t => t.Type == AggregateType.Avg && t.Path == "PriceAtTheTime");
229 | Assert.Equal(shouldResult.Count, aggCount.Value);
230 | Assert.Equal(shouldResult.ItemQuantityAverage, aggItemQuantityAverage.Value);
231 | Assert.Equal(shouldResult.ItemQuantitySum, aggItemQuantitySum.Value);
232 | Assert.Equal(shouldResult.AvgOfPrice, aggAvgOfPrice.Value);
233 | });
234 | });
235 | }
236 | }
237 |
238 | }
239 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/BeforeFilterTests.cs:
--------------------------------------------------------------------------------
1 | using PoweredSoft.DynamicQuery.Core;
2 | using PoweredSoft.DynamicQuery.Test.Mock;
3 | using PoweredSoft.DynamicLinq;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using Xunit;
9 |
10 | namespace PoweredSoft.DynamicQuery.Test
11 | {
12 | public class BeforeFilterTests
13 | {
14 | private class MockBeforeQueryFilterInterceptor : IBeforeQueryFilterInterceptor
15 | {
16 | public IQueryable InterceptBeforeFiltering(IQueryCriteria criteria, IQueryable queryable)
17 | {
18 | return queryable.Where(t => t.Equal("Customer.FirstName", "David"));
19 | }
20 | }
21 |
22 | private class MockBeforeQueryFilterGenericInterceptor :
23 | IBeforeQueryFilterInterceptor,
24 | IBeforeQueryFilterInterceptor
25 | {
26 | public IQueryable InterceptBeforeFiltering(IQueryCriteria criteria, IQueryable queryable)
27 | {
28 | return queryable.Where(t => t.Customer.FirstName == "David");
29 | }
30 |
31 | public IQueryable InterceptBeforeFiltering(IQueryCriteria criteria, IQueryable queryable)
32 | {
33 | // leave throw it validates the test, if it gets in here it shoulnd't
34 | throw new NotImplementedException();
35 | }
36 | }
37 |
38 | [Fact]
39 | public void NonGeneric()
40 | {
41 | MockContextFactory.SeedAndTestContextFor("BeforeFilterTests_NonGeneric", TestSeeders.SimpleSeedScenario, ctx =>
42 | {
43 | var criteria = new QueryCriteria();
44 | var interceptor = new MockBeforeQueryFilterInterceptor();
45 |
46 | // queryable of orders.
47 | var queryable = ctx.Orders.AsQueryable();
48 |
49 | // pass into the interceptor.
50 | queryable = (IQueryable)interceptor.InterceptBeforeFiltering(criteria, queryable);
51 |
52 | // query handler should pass by the same interceptor.
53 | var queryHandler = new QueryHandler(Enumerable.Empty());
54 | queryHandler.AddInterceptor(interceptor);
55 | var result = queryHandler.Execute(ctx.Orders, criteria);
56 |
57 | // compare results.
58 | var expected = queryable.ToList();
59 | Assert.Equal(expected, result.Data);
60 | });
61 | }
62 |
63 | [Fact]
64 | public void Generic()
65 | {
66 | MockContextFactory.SeedAndTestContextFor("BeforeFilterTests_Generic", TestSeeders.SimpleSeedScenario, ctx =>
67 | {
68 | var criteria = new QueryCriteria();
69 | var interceptor = new MockBeforeQueryFilterGenericInterceptor();
70 |
71 | // queryable of orders.
72 | var queryable = ctx.Orders.AsQueryable();
73 |
74 | // pass into the interceptor.
75 | queryable = interceptor.InterceptBeforeFiltering(criteria, queryable);
76 |
77 | // query handler should pass by the same interceptor.
78 | var queryHandler = new QueryHandler(Enumerable.Empty());
79 | queryHandler.AddInterceptor(interceptor);
80 | var result = queryHandler.Execute(ctx.Orders, criteria);
81 |
82 | // compare results.
83 | var expected = queryable.ToList();
84 | Assert.Equal(expected, result.Data);
85 | });
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/ConvertibleInterceptorTests.cs:
--------------------------------------------------------------------------------
1 | using PoweredSoft.DynamicQuery.Core;
2 | using PoweredSoft.DynamicQuery.Test.Mock;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using Xunit;
8 |
9 | namespace PoweredSoft.DynamicQuery.Test
10 | {
11 | public class QueryConvertInterceptorTests
12 | {
13 | private class CustomerModel
14 | {
15 | public long Id { get; set; }
16 | public string FirstName { get; set; }
17 | public string LastName { get; set; }
18 | public string FullName => $"{FirstName} {LastName}";
19 | }
20 |
21 | private class MockQueryConvertInterceptor : IQueryConvertInterceptor
22 | {
23 | public object InterceptResultTo(object entity)
24 | {
25 | var customer = entity as Customer;
26 | var personModel = new CustomerModel
27 | {
28 | Id = customer.Id,
29 | FirstName = customer.FirstName,
30 | LastName = customer.LastName
31 | };
32 | return personModel;
33 | }
34 | }
35 |
36 | private class MockQueryConvertGenericInterceptor :
37 | IQueryConvertInterceptor,
38 | IQueryConvertInterceptor
39 | {
40 | public object InterceptResultTo(Customer entity)
41 | {
42 | var customer = entity;
43 | var personModel = new CustomerModel
44 | {
45 | Id = customer.Id,
46 | FirstName = customer.FirstName,
47 | LastName = customer.LastName
48 | };
49 | return personModel;
50 | }
51 |
52 | public object InterceptResultTo(Order entity)
53 | {
54 | // leave the throw, its on purpose to match the type testing.
55 | throw new NotImplementedException();
56 | }
57 | }
58 |
59 | private class MockQueryConvertGenericInterceptor2 :
60 | IQueryConvertInterceptor
61 | {
62 | public CustomerModel InterceptResultTo(Customer entity)
63 | {
64 | var customer = entity;
65 | var personModel = new CustomerModel
66 | {
67 | Id = customer.Id,
68 | FirstName = customer.FirstName,
69 | LastName = customer.LastName
70 | };
71 | return personModel;
72 | }
73 | }
74 |
75 | [Fact]
76 | public void NonGeneric()
77 | {
78 | MockContextFactory.SeedAndTestContextFor("QueryConvertInterceptorTests_NonGeneric", TestSeeders.SimpleSeedScenario, ctx =>
79 | {
80 | var criteria = new QueryCriteria();
81 | var queryHandler = new QueryHandler(Enumerable.Empty());
82 | queryHandler.AddInterceptor(new MockQueryConvertInterceptor());
83 | var result = queryHandler.Execute(ctx.Customers, criteria);
84 | Assert.All(result.Data, t => Assert.IsType(t));
85 | });
86 | }
87 |
88 | [Fact]
89 | public void Generic()
90 | {
91 | MockContextFactory.SeedAndTestContextFor("ConvertibleIntereceptorTests_Generic", TestSeeders.SimpleSeedScenario, ctx =>
92 | {
93 | var criteria = new QueryCriteria();
94 | var queryHandler = new QueryHandler(Enumerable.Empty());
95 | queryHandler.AddInterceptor(new MockQueryConvertGenericInterceptor());
96 | var result = queryHandler.Execute(ctx.Customers, criteria);
97 | Assert.All(result.Data, t => Assert.IsType(t));
98 | });
99 | }
100 |
101 | [Fact]
102 | public void Generic2()
103 | {
104 | MockContextFactory.SeedAndTestContextFor("ConvertibleIntereceptorTests_Generic2", TestSeeders.SimpleSeedScenario, ctx =>
105 | {
106 | var criteria = new QueryCriteria();
107 | var queryHandler = new QueryHandler(Enumerable.Empty());
108 | queryHandler.AddInterceptor(new MockQueryConvertGenericInterceptor2());
109 | var result = queryHandler.Execute(ctx.Customers, criteria);
110 | Assert.All(result.Data, t => Assert.IsType(t));
111 | });
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/CriteriaTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.EntityFrameworkCore;
5 | using PoweredSoft.DynamicQuery.Core;
6 | using PoweredSoft.DynamicQuery.Test.Mock;
7 | using Xunit;
8 |
9 | namespace PoweredSoft.DynamicQuery.Test
10 | {
11 | public class CriteriaTests
12 | {
13 | [Fact]
14 | public void TestEmptyCriteria()
15 | {
16 | MockContextFactory.SeedAndTestContextFor("CriteriaTests_TestEmptyCriteria", TestSeeders.SimpleSeedScenario, ctx =>
17 | {
18 | var resultShouldMatch = ctx.Items.ToList();
19 | var queryable = ctx.Items.AsQueryable();
20 |
21 | // query handler that is empty should be the same as running to list.
22 | var criteria = new QueryCriteria();
23 | var queryHandler = new QueryHandler(Enumerable.Empty());
24 | var result = queryHandler.Execute(queryable, criteria);
25 | Assert.Equal(resultShouldMatch, result.Data);
26 | });
27 | }
28 |
29 | [Fact]
30 | public void TestPaging()
31 | {
32 | MockContextFactory.SeedAndTestContextFor("CriteriaTests_TestPagging", TestSeeders.SimpleSeedScenario, ctx =>
33 | {
34 | var resultShouldMatch = ctx.OrderItems.OrderBy(t => t.Id).Skip(5).Take(5).ToList();
35 |
36 | // query handler that is empty should be the same as running to list.
37 | var criteria = new QueryCriteria();
38 | criteria.Sorts.Add(new Sort("Id"));
39 | criteria.Page = 2;
40 | criteria.PageSize = 5;
41 |
42 | var queryHandler = new QueryHandler(Enumerable.Empty());
43 | var result = queryHandler.Execute(ctx.OrderItems, criteria);
44 | Assert.Equal(resultShouldMatch, result.Data);
45 | });
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/DeserializeTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Converters;
4 | using PoweredSoft.DynamicQuery.Core;
5 | using PoweredSoft.DynamicQuery.NewtonsoftJson;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Text;
9 | using Xunit;
10 |
11 | namespace PoweredSoft.DynamicQuery.Test
12 | {
13 | public class SerializationTests
14 | {
15 | [Fact]
16 | public void QueryCriteria()
17 | {
18 | var json = @"{""page"":1,""pageSize"":20,""filters"":[{""type"":""composite"",""filters"":[{""path"":""title"",""value"":""Qui"",""type"":""StartsWith"",""and"":false}]}]}";
19 |
20 | var serviceCollection = new ServiceCollection();
21 | serviceCollection.AddPoweredSoftDynamicQuery();
22 | var serviceProvider = serviceCollection.BuildServiceProvider();
23 |
24 | var settings = new JsonSerializerSettings();
25 |
26 | settings.Converters.Add(new StringEnumConverter());
27 | settings.Converters.Add(new DynamicQueryJsonConverter(serviceProvider));
28 |
29 | var data = JsonConvert.DeserializeObject(json, settings);
30 | Assert.NotNull(data);
31 | }
32 |
33 |
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/FilterInterceptorTests.cs:
--------------------------------------------------------------------------------
1 | using PoweredSoft.DynamicQuery.Core;
2 | using PoweredSoft.DynamicQuery.Extensions;
3 | using PoweredSoft.DynamicQuery.Test.Mock;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using Xunit;
9 |
10 | namespace PoweredSoft.DynamicQuery.Test
11 | {
12 | public class FilterInterceptorTests
13 | {
14 | private class MockFilterInterceptorA : IFilterInterceptor
15 | {
16 | public IFilter InterceptFilter(IFilter filter)
17 | {
18 | if (filter is ISimpleFilter && ((ISimpleFilter)filter).Path == "CustomerFirstName")
19 | return new SimpleFilter { Path = "Customer.FirstName", Type = FilterType.Contains, Value = "David" };
20 |
21 | return filter;
22 | }
23 | }
24 |
25 | private class MockFilterInterceptorAWithExtension : IFilterInterceptor
26 | {
27 | public IFilter InterceptFilter(IFilter filter)
28 | {
29 | if (filter.IsSimpleFilterOn("CustomerFirstName"))
30 | return filter.ReplaceByOn(t => t.Customer.FirstName);
31 | else if (filter.IsSimpleFilterOn("CustomerFullName"))
32 | return filter.ReplaceByCompositeOn(t => t.Customer.FirstName, t => t.Customer.LastName);
33 |
34 | return filter;
35 | }
36 | }
37 |
38 | private class MockFilterInterceptorB : IFilterInterceptor
39 | {
40 | public IFilter InterceptFilter(IFilter filter)
41 | {
42 | return new SimpleFilter { Path = "Customer.LastName", Type = FilterType.Contains, Value = "Norris" };
43 | }
44 | }
45 |
46 | [Fact]
47 | public void Simple()
48 | {
49 | MockContextFactory.SeedAndTestContextFor("FilterInterceptorTests_Simple", TestSeeders.SimpleSeedScenario, ctx =>
50 | {
51 | var queryable = ctx.Orders.AsQueryable();
52 |
53 | var criteria = new QueryCriteria()
54 | {
55 | Filters = new List
56 | {
57 | new SimpleFilter { Path = "CustomerFirstName", Value = "David", Type = FilterType.Contains }
58 | }
59 | };
60 |
61 | var query = new QueryHandler(Enumerable.Empty());
62 | query.AddInterceptor(new MockFilterInterceptorA());
63 | var result = query.Execute(queryable, criteria);
64 |
65 | var actual = result.Data;
66 | var expected = queryable.Where(t => t.Customer.FirstName == "David").ToList();
67 | Assert.Equal(expected, actual);
68 | });
69 | }
70 |
71 | [Fact]
72 | public void SimpleWithExtensions()
73 | {
74 | MockContextFactory.SeedAndTestContextFor("FilterInterceptorTests_SimpleWithExtensions", TestSeeders.SimpleSeedScenario, ctx =>
75 | {
76 | var queryable = ctx.Orders.AsQueryable();
77 |
78 | var criteria = new QueryCriteria()
79 | {
80 | Filters = new List
81 | {
82 | new SimpleFilter { Path = "CustomerFirstName", Value = "David", Type = FilterType.Contains }
83 | }
84 | };
85 |
86 | var query = new QueryHandler(Enumerable.Empty());
87 | query.AddInterceptor(new MockFilterInterceptorAWithExtension());
88 | var result = query.Execute(queryable, criteria);
89 |
90 | var actual = result.Data;
91 | var expected = queryable.Where(t => t.Customer.FirstName == "David").ToList();
92 | Assert.Equal(expected, actual);
93 | });
94 | }
95 |
96 | [Fact]
97 | public void SimpleWithExtensions2()
98 | {
99 | MockContextFactory.SeedAndTestContextFor("FilterInterceptorTests_SimpleWithExtensions2", TestSeeders.SimpleSeedScenario, ctx =>
100 | {
101 | var queryable = ctx.Orders.AsQueryable();
102 |
103 | var criteria = new QueryCriteria()
104 | {
105 | Filters = new List
106 | {
107 | new SimpleFilter { Path = "CustomerFullName", Value = "Da", Type = FilterType.Contains }
108 | }
109 | };
110 |
111 | var query = new QueryHandler(Enumerable.Empty());
112 | query.AddInterceptor(new MockFilterInterceptorAWithExtension());
113 | var result = query.Execute(queryable, criteria);
114 |
115 | var actual = result.Data;
116 | var expected = queryable.Where(t => t.Customer.FirstName.Contains("Da") || t.Customer.LastName.Contains("Da")).ToList();
117 | Assert.Equal(expected, actual);
118 | });
119 | }
120 |
121 | [Fact]
122 | public void Multi()
123 | {
124 | MockContextFactory.SeedAndTestContextFor("FilterInterceptorTests_Multi", TestSeeders.SimpleSeedScenario, ctx =>
125 | {
126 | var queryable = ctx.Orders.AsQueryable();
127 |
128 | var criteria = new QueryCriteria()
129 | {
130 | Filters = new List
131 | {
132 | new SimpleFilter { Path = "CustomerFirstName", Value = "David", Type = FilterType.Contains }
133 | }
134 | };
135 |
136 | var query = new QueryHandler(Enumerable.Empty());
137 | query.AddInterceptor(new MockFilterInterceptorA());
138 | query.AddInterceptor(new MockFilterInterceptorB());
139 | var result = query.Execute(queryable, criteria);
140 |
141 | var actual = result.Data;
142 | var expected = queryable.Where(t => t.Customer.LastName == "Norris").ToList();
143 | Assert.Equal(expected, actual);
144 | });
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/FilterTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using PoweredSoft.DynamicQuery.Core;
6 | using PoweredSoft.DynamicQuery.Test.Mock;
7 | using Xunit;
8 | using Xunit.Sdk;
9 |
10 | namespace PoweredSoft.DynamicQuery.Test
11 | {
12 | public class FilterTests
13 | {
14 | private class MockIsChuckFilter : ISimpleFilter
15 | {
16 | public bool? And { get; set; } = false;
17 | public FilterType Type { get; set; } = FilterType.Equal;
18 | public string Path { get; set; } = "FirstName";
19 | public object Value { get; set; } = "Chuck";
20 | public bool? Not { get; set; }
21 | public bool? CaseInsensitive { get; set; }
22 | }
23 |
24 | [Fact]
25 | public void TestInversionOfControl()
26 | {
27 | MockContextFactory.SeedAndTestContextFor("FilterTests_TestInversionOfControl", TestSeeders.SimpleSeedScenario, ctx =>
28 | {
29 | var resultShouldMatch = ctx.Customers.Where(t => t.FirstName == "Chuck").ToList();
30 |
31 | var criteria = new QueryCriteria()
32 | {
33 | Filters = new List { new MockIsChuckFilter() }
34 | };
35 |
36 | var queryHandler = new QueryHandler(Enumerable.Empty());
37 | var result = queryHandler.Execute(ctx.Customers, criteria);
38 | Assert.Equal(resultShouldMatch, result.Data);
39 | });
40 | }
41 |
42 |
43 |
44 | [Fact]
45 | public void SimpleFilter()
46 | {
47 | MockContextFactory.SeedAndTestContextFor("FilterTests_SimpleFilter", TestSeeders.SimpleSeedScenario, ctx =>
48 | {
49 | var resultShouldMatch = ctx.Items.Where(t => t.Name.EndsWith("Cables")).ToList();
50 |
51 | var criteria = new QueryCriteria()
52 | {
53 | Filters = new List
54 | {
55 | new SimpleFilter
56 | {
57 | Path = "Name",
58 | Type = FilterType.EndsWith,
59 | Value = "Cables"
60 | }
61 | }
62 | };
63 |
64 | var queryHandler = new QueryHandler(Enumerable.Empty());
65 | var result = queryHandler.Execute(ctx.Items, criteria);
66 | Assert.Equal(resultShouldMatch, result.Data);
67 | });
68 | }
69 |
70 | [Fact]
71 | public void SimpleFilterCaseInsensitive()
72 | {
73 | MockContextFactory.SeedAndTestContextFor("FilterTests_SimpleFilterCaseInsensitive", TestSeeders.SimpleSeedScenario, ctx =>
74 | {
75 | var resultShouldMatch = ctx.Items.Where(t => t.Name.ToLower().EndsWith("Cables".ToLower())).ToList();
76 |
77 | var criteria = new QueryCriteria()
78 | {
79 | Filters = new List
80 | {
81 | new SimpleFilter
82 | {
83 | Path = "Name",
84 | Type = FilterType.EndsWith,
85 | Value = "Cables",
86 | CaseInsensitive = true
87 | }
88 | }
89 | };
90 |
91 | var queryHandler = new QueryHandler(Enumerable.Empty());
92 | var result = queryHandler.Execute(ctx.Items, criteria);
93 | Assert.Equal(resultShouldMatch, result.Data);
94 | });
95 | }
96 |
97 |
98 |
99 | [Fact]
100 | public void CompositeFilter()
101 | {
102 | MockContextFactory.SeedAndTestContextFor("FilterTests_CompositeFilter", TestSeeders.SimpleSeedScenario, ctx =>
103 | {
104 | var resultShouldMatch = ctx.Customers.Where(t => t.FirstName == "John" || t.LastName == "Norris").ToList();
105 |
106 | var criteria = new QueryCriteria()
107 | {
108 | Filters = new List
109 | {
110 | new CompositeFilter()
111 | {
112 | Type = FilterType.Composite,
113 | Filters = new List
114 | {
115 | new SimpleFilter() { Path = "FirstName", Type = FilterType.Equal, Value = "John" },
116 | new SimpleFilter() { Path = "LastName", Type = FilterType.Equal, Value = "Norris"}
117 | }
118 | }
119 | }
120 | };
121 |
122 | var queryHandler = new QueryHandler(Enumerable.Empty());
123 | var result = queryHandler.Execute(ctx.Customers, criteria);
124 | Assert.Equal(resultShouldMatch, result.Data);
125 | });
126 | }
127 |
128 | [Fact]
129 | public void MoreComplexCompositeFilter()
130 | {
131 | MockContextFactory.SeedAndTestContextFor("FilterTests_MoreComplexCompositeFilter", TestSeeders.SimpleSeedScenario, ctx =>
132 | {
133 | var resultShouldMatch = ctx.Customers.Where(t => (t.FirstName == "John" || t.LastName == "Norris") && t.FirstName.Contains("o")).ToList();
134 |
135 | var criteria = new QueryCriteria()
136 | {
137 | Filters = new List
138 | {
139 | new CompositeFilter()
140 | {
141 | Type = FilterType.Composite,
142 | Filters = new List
143 | {
144 | new SimpleFilter() { Path = "FirstName", Type = FilterType.Equal, Value = "John" },
145 | new SimpleFilter() { Path = "LastName", Type = FilterType.Equal, Value = "Norris"}
146 | }
147 | },
148 | new SimpleFilter()
149 | {
150 | And = true,
151 | Path = "FirstName",
152 | Type = FilterType.Contains,
153 | Value = "o"
154 | }
155 | }
156 | };
157 |
158 | var queryHandler = new QueryHandler(Enumerable.Empty());
159 | var result = queryHandler.Execute(ctx.Customers, criteria);
160 | Assert.Equal(resultShouldMatch, result.Data);
161 | });
162 | }
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/GroupInterceptorTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using PoweredSoft.DynamicQuery.Core;
3 | using PoweredSoft.DynamicQuery.Extensions;
4 | using PoweredSoft.DynamicQuery.Test.Mock;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 | using Xunit;
10 |
11 | namespace PoweredSoft.DynamicQuery.Test
12 | {
13 | public partial class GroupInterceptorTests
14 | {
15 | private class MockGroupInterceptor : IGroupInterceptor
16 | {
17 | public IGroup InterceptGroup(IGroup group)
18 | {
19 | return new Group()
20 | {
21 | Path = "Customer.FirstName"
22 | };
23 | }
24 | }
25 |
26 | [Fact]
27 | public void Simple()
28 | {
29 | MockContextFactory.SeedAndTestContextFor("GroupInterceptorTests_Simple", TestSeeders.SimpleSeedScenario, ctx =>
30 | {
31 | var expected = ctx.Orders
32 | .OrderBy(t => t.Customer.FirstName)
33 | .GroupBy(t => t.Customer.FirstName)
34 | .Select(t => t.Key)
35 | .ToList();
36 |
37 | var criteria = new QueryCriteria();
38 | criteria.Groups.Add(new Group { Path = "CustomerFirstName" });
39 | var queryHandler = new QueryHandler(Enumerable.Empty());
40 | queryHandler.AddInterceptor(new MockGroupInterceptor());
41 | var result = queryHandler.Execute(ctx.Orders.Include(t => t.Customer), criteria, new QueryExecutionOptions
42 | {
43 | GroupByInMemory = true
44 | });
45 |
46 | var groupedResult = result.GroupedResult();
47 | var actual = groupedResult.Groups.Select(t => t.GroupValue).ToList();
48 | Assert.Equal(expected, actual);
49 | });
50 | }
51 |
52 | [Fact]
53 | public void WithInterptorSimple()
54 | {
55 | MockContextFactory.SeedAndTestContextFor("GroupInterceptorTests_WithInterptorSimple", TestSeeders.SimpleSeedScenario, ctx =>
56 | {
57 | var expected = ctx.Orders
58 | .OrderBy(t => t.Customer.FirstName)
59 | .GroupBy(t => t.Customer.FirstName)
60 | .Select(t => t.Key)
61 | .ToList();
62 |
63 | var criteria = new QueryCriteria();
64 | criteria.Groups.Add(new Group { Path = "CustomerFirstName" });
65 | var queryHandler = new QueryHandler(Enumerable.Empty());
66 | queryHandler.AddInterceptor(new MockGroupInterceptor());
67 | queryHandler.AddInterceptor(new MockQueryExecutionOptionsInterceptor());
68 | var result = queryHandler.Execute(ctx.Orders.Include(t => t.Customer), criteria);
69 |
70 | var groupedResult = result.GroupedResult();
71 | var actual = groupedResult.Groups.Select(t => t.GroupValue).ToList();
72 | Assert.Equal(expected, actual);
73 | });
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/GroupTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using PoweredSoft.DynamicQuery.Core;
3 | using PoweredSoft.DynamicQuery.Extensions;
4 | using PoweredSoft.DynamicQuery.Test.Mock;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 | using Xunit;
12 |
13 | namespace PoweredSoft.DynamicQuery.Test
14 | {
15 | public class GroupTests
16 | {
17 | [Fact]
18 | public void Simple()
19 | {
20 | MockContextFactory.SeedAndTestContextFor("GroupTests_Simple", TestSeeders.SimpleSeedScenario, ctx =>
21 | {
22 | var shouldResult = ctx.Orders
23 | .OrderBy(t => t.CustomerId)
24 | .ToList()
25 | .GroupBy(t => t.CustomerId)
26 | .Select(t => new
27 | {
28 | CustomerId = t.Key,
29 | Orders = t.ToList()
30 | })
31 | .ToList();
32 |
33 | // query handler that is empty should be the same as running to list.
34 | var criteria = new QueryCriteria()
35 | {
36 | Groups = new List
37 | {
38 | new Group { Path = "CustomerId" }
39 | }
40 | };
41 |
42 | var queryHandler = new QueryHandler(Enumerable.Empty());
43 | var result = queryHandler.Execute(ctx.Orders, criteria, new QueryExecutionOptions
44 | {
45 | GroupByInMemory = true,
46 | GroupByInMemoryNullCheck = false
47 | });
48 | var groupedResult = result.GroupedResult();
49 |
50 | // top level should have same amount of group levels.
51 | Assert.Equal(groupedResult.Groups.Count, shouldResult.Count);
52 | for (var i = 0; i < shouldResult.Count; i++)
53 | {
54 | var expected = shouldResult[0];
55 | var actual = groupedResult.Groups[0];
56 | Assert.Equal(expected.CustomerId, actual.GroupValue);
57 |
58 | var expectedOrderIds = expected.Orders.Select(t => t.Id).ToList();
59 | var actualOrderIds = actual.Data.Cast().Select(t => t.Id).ToList();
60 | Assert.Equal(expectedOrderIds, actualOrderIds);
61 | }
62 | });
63 | }
64 |
65 | [Fact]
66 | public void GroupComplex()
67 | {
68 | MockContextFactory.SeedAndTestContextFor("GroupTests_Complex", TestSeeders.SeedTicketScenario, ctx =>
69 | {
70 | var criteria = new QueryCriteria()
71 | {
72 | Groups = new List()
73 | {
74 | new Group { Path = "TicketType" },
75 | new Group { Path = "Priority" }
76 | },
77 | Aggregates = new List()
78 | {
79 | new Aggregate { Type = AggregateType.Count }
80 | }
81 | };
82 |
83 | var queryHandler = new QueryHandler(Enumerable.Empty());
84 | var result = queryHandler.Execute(ctx.Tickets, criteria, new QueryExecutionOptions
85 | {
86 | GroupByInMemory = true
87 | });
88 |
89 | var groupedResult = result.GroupedResult();
90 |
91 | var firstGroup = groupedResult.Groups.FirstOrDefault();
92 | Assert.NotNull(firstGroup);
93 | var secondGroup = groupedResult.Groups.Skip(1).FirstOrDefault();
94 | Assert.NotNull(secondGroup);
95 |
96 | var expected = ctx.Tickets.Select(t => t.TicketType).Distinct().Count();
97 | var c = groupedResult.Groups.Select(t => t.GroupValue).Count();
98 | Assert.Equal(expected, c);
99 | });
100 | }
101 |
102 | [Fact]
103 | public void InterceptorsWithGrouping()
104 | {
105 | MockContextFactory.SeedAndTestContextFor("GroupTests_InterceptorsWithGrouping", TestSeeders.SeedTicketScenario, ctx =>
106 | {
107 | var criteria = new QueryCriteria()
108 | {
109 | Groups = new List()
110 | {
111 | new Group { Path = "TicketType" }
112 | },
113 | Aggregates = new List()
114 | {
115 | new Aggregate { Type = AggregateType.Count }
116 | }
117 | };
118 |
119 | var interceptor = new InterceptorsWithGrouping();
120 | var queryHandler = new QueryHandler(Enumerable.Empty());
121 | queryHandler.AddInterceptor(interceptor);
122 | var result = queryHandler.Execute(ctx.Tickets, criteria, new QueryExecutionOptions
123 | {
124 | GroupByInMemory = true
125 | });
126 |
127 | Assert.Equal(4, interceptor.Count);
128 | Assert.True(interceptor.Test);
129 | Assert.True(interceptor.Test2);
130 | Assert.True(interceptor.Test3);
131 | Assert.True(interceptor.Test4);
132 | });
133 | }
134 | }
135 |
136 | class InterceptorWithGroupingFakeModel
137 | {
138 |
139 | }
140 |
141 | class InterceptorsWithGrouping :
142 | IAfterReadEntityInterceptor,
143 | IAfterReadEntityInterceptorAsync,
144 | IAfterReadInterceptor,
145 | IAfterReadInterceptorAsync,
146 | IQueryConvertInterceptor
147 | {
148 | public int Count { get; set; } = 0;
149 | public bool Test { get; set; } = false;
150 | public bool Test2 { get; set; } = false;
151 | public bool Test3 { get; set; } = false;
152 | public bool Test4 { get; set; } = false;
153 |
154 | public void AfterRead(List> pairs)
155 | {
156 | Test = true;
157 | Count++;
158 | }
159 |
160 | public Task AfterReadAsync(List> pairs, CancellationToken cancellationToken = default(CancellationToken))
161 | {
162 | Test2 = true;
163 | Count++;
164 | return Task.CompletedTask;
165 | }
166 |
167 | public void AfterReadEntity(List entities)
168 | {
169 | Test3 = true;
170 | Count++;
171 | }
172 |
173 | public Task AfterReadEntityAsync(List entities, CancellationToken cancellationToken = default(CancellationToken))
174 | {
175 | Test4 = true;
176 | Count++;
177 | return Task.CompletedTask;
178 | }
179 |
180 | public object InterceptResultTo(Ticket entity)
181 | {
182 | return new InterceptorWithGroupingFakeModel();
183 | }
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/IncludeStrategyTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using PoweredSoft.DynamicQuery.Core;
3 | using PoweredSoft.DynamicQuery.Test.Mock;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using Xunit;
9 |
10 | namespace PoweredSoft.DynamicQuery.Test
11 | {
12 | public class IncludeStrategyTests
13 | {
14 | private class MockIncludeStrategyInterceptor : IIncludeStrategyInterceptor
15 | {
16 | public IQueryable InterceptIncludeStrategy(IQueryCriteria criteria, IQueryable queryable)
17 | {
18 | queryable = ((IQueryable)queryable).Include(t => t.Customer);
19 | return queryable;
20 | }
21 | }
22 |
23 | private class MockIncludeStrategyGenericInterceptor :
24 | IIncludeStrategyInterceptor,
25 | IIncludeStrategyInterceptor
26 | {
27 | public IQueryable InterceptIncludeStrategy(IQueryCriteria criteria, IQueryable queryable)
28 | {
29 | return queryable.Include(t => t.Customer);
30 | }
31 |
32 | public IQueryable InterceptIncludeStrategy(IQueryCriteria criteria, IQueryable queryable)
33 | {
34 | // should not go in here.
35 | throw new NotImplementedException();
36 | }
37 | }
38 |
39 | [Fact]
40 | public void NonGeneric()
41 | {
42 | MockContextFactory.SeedAndTestContextFor("IncludeStrategyTests_NonGeneric", TestSeeders.SimpleSeedScenario, ctx =>
43 | {
44 | var criteria = new QueryCriteria();
45 | var interceptor = new MockIncludeStrategyInterceptor();
46 |
47 | // queryable of orders.
48 | var queryable = ctx.Orders.AsQueryable();
49 |
50 | // pass into the interceptor.
51 | queryable = (IQueryable)interceptor.InterceptIncludeStrategy(criteria, queryable);
52 |
53 | // query handler should pass by the same interceptor.
54 | var queryHandler = new QueryHandler(Enumerable.Empty());
55 | queryHandler.AddInterceptor(interceptor);
56 | var result = queryHandler.Execute(ctx.Orders, criteria);
57 |
58 | // compare results.
59 | var expected = queryable.ToList();
60 | Assert.Equal(expected, result.Data);
61 | });
62 | }
63 |
64 | [Fact]
65 | public void Generic()
66 | {
67 | MockContextFactory.SeedAndTestContextFor("IncludeStrategyTests_Generic", TestSeeders.SimpleSeedScenario, ctx =>
68 | {
69 | var criteria = new QueryCriteria();
70 | var interceptor = new MockIncludeStrategyGenericInterceptor();
71 |
72 | // queryable of orders.
73 | var queryable = ctx.Orders.AsQueryable();
74 |
75 | // pass into the interceptor.
76 | queryable = interceptor.InterceptIncludeStrategy(criteria, queryable);
77 |
78 | // query handler should pass by the same interceptor.
79 | var queryHandler = new QueryHandler(Enumerable.Empty());
80 | queryHandler.AddInterceptor(interceptor);
81 | var result = queryHandler.Execute(ctx.Orders, criteria);
82 |
83 | // compare results.
84 | var expected = queryable.ToList();
85 | Assert.Equal(expected, result.Data);
86 | });
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/Mock/Entities.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace PoweredSoft.DynamicQuery.Test.Mock
5 | {
6 | public class Order
7 | {
8 | public long Id { get; set; }
9 | public long OrderNum { get; set; }
10 | public DateTime Date { get; set; }
11 | public long CustomerId { get; set; }
12 |
13 | public virtual Customer Customer { get; set; }
14 | public ICollection OrderItems { get; set; } = new HashSet();
15 | }
16 |
17 | public class Customer
18 | {
19 | public long Id { get; set; }
20 | public string FirstName { get; set; }
21 | public string LastName { get; set; }
22 |
23 |
24 | public ICollection Orders { get; set; } = new HashSet();
25 | }
26 |
27 | public class Item
28 | {
29 | public long Id { get; set; }
30 | public string Name { get; set; }
31 | public decimal Price { get; set; }
32 |
33 | public virtual ICollection OrderItems { get; set; } = new HashSet();
34 | }
35 |
36 | public class OrderItem
37 | {
38 | public long Id { get; set; }
39 | public long Quantity { get; set; }
40 | public decimal PriceAtTheTime { get; set; }
41 | public long ItemId { get; set; }
42 | public long OrderId { get; set; }
43 |
44 | public virtual Item Item { get; set; }
45 | public virtual Order Order { get; set; }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/Mock/MockContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Microsoft.EntityFrameworkCore;
5 |
6 | namespace PoweredSoft.DynamicQuery.Test.Mock
7 | {
8 | public class MockContext : DbContext
9 | {
10 | public virtual DbSet Customers { get; set; }
11 | public virtual DbSet- Items { get; set; }
12 | public virtual DbSet Orders { get; set; }
13 | public virtual DbSet OrderItems { get; set; }
14 | public virtual DbSet Tickets { get; set; }
15 |
16 | public MockContext()
17 | {
18 |
19 | }
20 |
21 | public MockContext(DbContextOptions options)
22 | : base(options)
23 | {
24 |
25 | }
26 |
27 | protected override void OnModelCreating(ModelBuilder modelBuilder)
28 | {
29 |
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/Mock/MockContextFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading.Tasks;
5 | using Microsoft.EntityFrameworkCore;
6 | using Microsoft.EntityFrameworkCore.Diagnostics;
7 | using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine.ClientProtocol;
8 | using Xunit.Sdk;
9 |
10 | namespace PoweredSoft.DynamicQuery.Test.Mock
11 | {
12 | public static class MockContextFactory
13 | {
14 | public static void TestContextFor(string testName, Action action)
15 | {
16 | var options = new DbContextOptionsBuilder()
17 | .UseInMemoryDatabase(databaseName: testName).Options;
18 |
19 | using var ctx = new MockContext(options);
20 | action(ctx);
21 | }
22 |
23 | public static void SeedAndTestContextFor(string testName, Action seedAction, Action action)
24 | {
25 | seedAction(testName);
26 | TestContextFor(testName, action);
27 | }
28 |
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/Mock/TestSeeders.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace PoweredSoft.DynamicQuery.Test.Mock
5 | {
6 | public static class TestSeeders
7 | {
8 | public static void SimpleSeedScenario(string testName)
9 | {
10 | MockContextFactory.TestContextFor(testName, ctx =>
11 | {
12 | ctx.AddRange(new Customer[]
13 | {
14 | new Customer() { Id = 1, FirstName = "David", LastName = "Lebee" },
15 | new Customer() { Id = 2, FirstName = "John", LastName = "Doe" },
16 | new Customer() { Id = 3, FirstName = "Chuck", LastName = "Norris" },
17 | new Customer() { Id = 4, FirstName = "Nelson", LastName = "Mendela" },
18 | new Customer() { Id = 5, FirstName = "Jimi", LastName = "Hendrix" },
19 | new Customer() { Id = 6, FirstName = "Axel", LastName = "Rose" },
20 | new Customer() { Id = 7, FirstName = "John", LastName = "Frusciante" },
21 | new Customer() { Id = 8, FirstName = "Michael", LastName = "Jackson" },
22 | new Customer() { Id = 9, FirstName = "Anita", LastName = "Franklin" },
23 | });
24 |
25 | ctx.AddRange(new Item[]
26 | {
27 | new Item { Id = 1, Name = "Computer", Price = 1000M },
28 | new Item { Id = 2, Name = "Mice", Price = 25.99M },
29 | new Item { Id = 3, Name = "Keyboard", Price = 100M },
30 | new Item { Id = 4, Name = "Screen", Price = 499.98M },
31 | new Item { Id = 5, Name = "Printer", Price = 230.95M },
32 | new Item { Id = 6, Name = "HDMI Cables", Price = 20M },
33 | new Item { Id = 7, Name = "Power Cables", Price = 5.99M }
34 | });
35 |
36 | ctx.Orders.AddRange(new Order[]
37 | {
38 | new Order()
39 | {
40 | Id = 1,
41 | OrderNum = 1000,
42 | CustomerId = 1,
43 | Date = new DateTime(2018, 1, 1),
44 | OrderItems = new List()
45 | {
46 | new OrderItem() { Id = 1, ItemId = 1, PriceAtTheTime = 1000M, Quantity = 1 },
47 | new OrderItem() { Id = 2, ItemId = 2, PriceAtTheTime = 30M, Quantity = 1 },
48 | new OrderItem() { Id = 3, ItemId = 4, PriceAtTheTime = 399.99M, Quantity = 2 },
49 | new OrderItem() { Id = 4, ItemId = 6, PriceAtTheTime = 20, Quantity = 2 },
50 | new OrderItem() { Id = 8, ItemId = 6, PriceAtTheTime = 3.99M, Quantity = 3 }
51 | }
52 | },
53 | new Order()
54 | {
55 | Id = 2,
56 | OrderNum = 1001,
57 | CustomerId = 2,
58 | Date = new DateTime(2018, 2, 1),
59 | OrderItems = new List()
60 | {
61 | new OrderItem() { Id = 9, ItemId = 6, PriceAtTheTime = 20, Quantity = 2 },
62 | new OrderItem() { Id = 10, ItemId = 6, PriceAtTheTime = 3.99M, Quantity = 3 }
63 | }
64 | },
65 | new Order()
66 | {
67 | Id = 3,
68 | OrderNum = 1002,
69 | CustomerId = 3,
70 | Date = new DateTime(2018, 2, 1),
71 | OrderItems = new List()
72 | {
73 | new OrderItem() { Id = 11, ItemId = 5, PriceAtTheTime = 499.99M, Quantity = 1 },
74 | new OrderItem() { Id = 12, ItemId = 6, PriceAtTheTime = 20, Quantity = 1 },
75 | new OrderItem() { Id = 13, ItemId = 7, PriceAtTheTime = 3.99M, Quantity = 1 }
76 | }
77 | },
78 | new Order()
79 | {
80 | Id = 4,
81 | OrderNum = 1003,
82 | CustomerId = 1,
83 | Date = new DateTime(2018, 3, 1),
84 | OrderItems = new List()
85 | {
86 | new OrderItem() { Id = 14, ItemId = 2, PriceAtTheTime = 50M, Quantity = 1 },
87 | new OrderItem() { Id = 15, ItemId = 3, PriceAtTheTime = 75.50M, Quantity = 1 },
88 | }
89 | }
90 | });
91 |
92 | ctx.SaveChanges();
93 | });
94 | }
95 |
96 | internal static void SeedTicketScenario(string testName)
97 | {
98 | MockContextFactory.TestContextFor(testName, ctx =>
99 | {
100 | var owners = new List();
101 |
102 | for(var i = 0; i < 20; i++)
103 | {
104 | var f = new Bogus.Faker("en");
105 | owners.Add(f.Person.FullName);
106 | }
107 |
108 | var faker = new Bogus.Faker()
109 | .RuleFor(t => t.TicketType, (f, u) => f.PickRandom("new", "open", "refused", "closed"))
110 | .RuleFor(t => t.Title, (f, u) => f.Lorem.Sentence())
111 | .RuleFor(t => t.Details, (f, u) => f.Lorem.Paragraph())
112 | .RuleFor(t => t.IsHtml, (f, u) => false)
113 | .RuleFor(t => t.TagList, (f, u) => string.Join(",", f.Commerce.Categories(3)))
114 | .RuleFor(t => t.CreatedDate, (f, u) => f.Date.Recent(100))
115 | .RuleFor(t => t.Owner, (f, u) => f.PickRandom(owners))
116 | .RuleFor(t => t.AssignedTo, (f, u) => f.PickRandom(owners))
117 | .RuleFor(t => t.TicketStatus, (f, u) => f.PickRandom(1, 2, 3))
118 | .RuleFor(t => t.LastUpdateBy, (f, u) => f.Person.FullName)
119 | .RuleFor(t => t.LastUpdateDate, (f, u) => f.Date.Soon(5))
120 | .RuleFor(t => t.Priority, (f, u) => f.PickRandom("low", "medium", "high", "critical"))
121 | .RuleFor(t => t.AffectedCustomer, (f, u) => f.PickRandom(true, false))
122 | .RuleFor(t => t.Version, (f, u) => f.PickRandom("1.0.0", "1.1.0", "2.0.0"))
123 | .RuleFor(t => t.ProjectId, (f, u) => f.Random.Number(100))
124 | .RuleFor(t => t.DueDate, (f, u) => f.Date.Soon(5))
125 | .RuleFor(t => t.EstimatedDuration, (f, u) => f.Random.Number(20))
126 | .RuleFor(t => t.ActualDuration, (f, u) => f.Random.Number(20))
127 | .RuleFor(t => t.TargetDate, (f, u) => f.Date.Soon(5))
128 | .RuleFor(t => t.ResolutionDate, (f, u) => f.Date.Soon(5))
129 | .RuleFor(t => t.Type, (f, u) => f.PickRandom(1, 2, 3))
130 | .RuleFor(t => t.ParentId, () => 0)
131 | .RuleFor(t => t.PreferredLanguage, (f, u) => f.PickRandom("fr", "en", "es"))
132 | ;
133 |
134 | var fakeModels = new List();
135 | for (var i = 0; i < 500; i++)
136 | {
137 | var t = faker.Generate();
138 | t.TicketId = i + 1;
139 | fakeModels.Add(t);
140 | }
141 |
142 | ctx.AddRange(fakeModels);
143 | ctx.SaveChanges();
144 | });
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/Mock/Ticket.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace PoweredSoft.DynamicQuery.Test.Mock
6 | {
7 | public class Ticket
8 | {
9 | public int TicketId { get; set; }
10 | public string TicketType { get; set; }
11 | public string Title { get; set; }
12 | public string Details { get; set; }
13 | public bool IsHtml { get; set; }
14 | public string TagList { get; set; }
15 | public DateTimeOffset CreatedDate { get; set; }
16 | public string Owner { get; set; }
17 | public string AssignedTo { get; set; }
18 | public int TicketStatus { get; set; }
19 | public DateTimeOffset CurrentStatusDate { get; set; }
20 | public string CurrentStatusSetBy { get; set; }
21 | public string LastUpdateBy { get; set; }
22 | public DateTimeOffset LastUpdateDate { get; set; }
23 | public string Priority { get; set; }
24 | public bool AffectedCustomer { get; set; }
25 | public string Version { get; set; }
26 | public int ProjectId { get; set; }
27 | public DateTimeOffset DueDate { get; set; }
28 | public decimal EstimatedDuration { get; set; }
29 | public decimal ActualDuration { get; set; }
30 | public DateTimeOffset TargetDate { get; set; }
31 | public DateTimeOffset ResolutionDate { get; set; }
32 | public int Type { get; set; }
33 | public int ParentId { get; set; }
34 | public string PreferredLanguage { get; set; }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/MockQueryExecutionOptionsInterceptor.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore.Query;
2 | using PoweredSoft.DynamicQuery.Core;
3 | using System.Linq;
4 |
5 | namespace PoweredSoft.DynamicQuery.Test
6 | {
7 | public partial class GroupInterceptorTests
8 | {
9 | public class MockQueryExecutionOptionsInterceptor : IQueryExecutionOptionsInterceptor
10 | {
11 | public IQueryExecutionOptions InterceptQueryExecutionOptions(IQueryable queryable, IQueryExecutionOptions current)
12 | {
13 | if (queryable.Provider is IAsyncQueryProvider)
14 | {
15 | current.GroupByInMemory = true;
16 | }
17 |
18 | return current;
19 | }
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/NoSortTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using PoweredSoft.DynamicQuery.Core;
3 | using PoweredSoft.DynamicQuery.Test.Mock;
4 | using PoweredSoft.DynamicLinq;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 | using Xunit;
10 |
11 | namespace PoweredSoft.DynamicQuery.Test
12 | {
13 | public class NoSortTests
14 | {
15 | private class MockNoSortInterceptor : INoSortInterceptor
16 | {
17 | public IQueryable InterceptNoSort(IQueryCriteria criteria, IQueryable queryable)
18 | {
19 | return queryable.OrderBy("Customer.LastName");
20 | }
21 | }
22 |
23 | private class MockNoSortGenericInterceptor :
24 | INoSortInterceptor,
25 | INoSortInterceptor
26 | {
27 | public IQueryable InterceptNoSort(IQueryCriteria criteria, IQueryable queryable)
28 | {
29 | return queryable.OrderBy(t => t.Customer.LastName);
30 | }
31 |
32 | public IQueryable InterceptNoSort(IQueryCriteria criteria, IQueryable queryable)
33 | {
34 | // should not go in here.
35 | throw new NotImplementedException();
36 | }
37 | }
38 |
39 | [Fact]
40 | public void NonGeneric()
41 | {
42 | MockContextFactory.SeedAndTestContextFor("NoSortTests_NonGeneric", TestSeeders.SimpleSeedScenario, ctx =>
43 | {
44 | var criteria = new QueryCriteria();
45 | var interceptor = new MockNoSortInterceptor();
46 |
47 | // queryable of orders.
48 | var queryable = ctx.Orders.AsQueryable();
49 |
50 | // pass into the interceptor.
51 | queryable = (IQueryable)interceptor.InterceptNoSort(criteria, queryable);
52 |
53 | // query handler should pass by the same interceptor.
54 | var queryHandler = new QueryHandler(Enumerable.Empty());
55 | queryHandler.AddInterceptor(interceptor);
56 | var result = queryHandler.Execute(ctx.Orders, criteria);
57 |
58 | // compare results.
59 | var expected = queryable.ToList();
60 | Assert.Equal(expected, result.Data);
61 | });
62 | }
63 |
64 | [Fact]
65 | public void Generic()
66 | {
67 | MockContextFactory.SeedAndTestContextFor("NoSortTests_Generic", TestSeeders.SimpleSeedScenario, ctx =>
68 | {
69 | var criteria = new QueryCriteria();
70 | var interceptor = new MockNoSortGenericInterceptor();
71 |
72 | // queryable of orders.
73 | var queryable = ctx.Orders.AsQueryable();
74 |
75 | // pass into the interceptor.
76 | queryable = interceptor.InterceptNoSort(criteria, queryable);
77 |
78 | // query handler should pass by the same interceptor.
79 | var queryHandler = new QueryHandler(Enumerable.Empty());
80 | queryHandler.AddInterceptor(interceptor);
81 | var result = queryHandler.Execute(ctx.Orders, criteria);
82 |
83 | // compare results.
84 | var expected = queryable.ToList();
85 | Assert.Equal(expected, result.Data);
86 | });
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/PoweredSoft.DynamicQuery.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net5.0
5 |
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | all
20 | runtime; build; native; contentfiles; analyzers
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/QueryProviderTests.cs:
--------------------------------------------------------------------------------
1 | using PoweredSoft.DynamicQuery.Core;
2 | using PoweredSoft.DynamicQuery.Test.Mock;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using Xunit;
8 |
9 | namespace PoweredSoft.DynamicQuery.Test
10 | {
11 | public class QueryProviderTests
12 | {
13 | private class FakeInterceptor : IQueryInterceptor
14 | {
15 |
16 | }
17 |
18 | private class QueryInterceptorProvider : IQueryInterceptorProvider
19 | {
20 | public IEnumerable GetInterceptors(IQueryCriteria queryCriteria, IQueryable queryable)
21 | {
22 | yield return new FakeInterceptor();
23 | yield return new FakeInterceptor();
24 | }
25 | }
26 |
27 | [Fact]
28 | public void Simple()
29 | {
30 | MockContextFactory.SeedAndTestContextFor("QueryProviderTests_Simple", TestSeeders.SimpleSeedScenario, ctx =>
31 | {
32 | // criteria
33 | var criteria = new QueryCriteria();
34 | var queryHandler = new QueryHandler(new List{
35 | new QueryInterceptorProvider()
36 | });
37 | queryHandler.AddInterceptor(new FakeInterceptor());
38 | var interceptors = queryHandler.ResolveInterceptors(criteria, ctx.Orders);
39 | Assert.Equal(1, interceptors.Count);
40 | Assert.True(interceptors[0].GetType() == typeof(FakeInterceptor));
41 | });
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/SortInterceptorTests.cs:
--------------------------------------------------------------------------------
1 | using PoweredSoft.DynamicQuery.Core;
2 | using PoweredSoft.DynamicQuery.Test.Mock;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using Xunit;
8 |
9 | namespace PoweredSoft.DynamicQuery.Test
10 | {
11 | public class SortInterceptorTests
12 | {
13 | private class MockSortInterceptor : ISortInterceptor
14 | {
15 | public IEnumerable InterceptSort(IEnumerable sort)
16 | {
17 | return new Sort[]
18 | {
19 | new Sort("Customer.FirstName"),
20 | new Sort("Customer.LastName")
21 | };
22 | }
23 | }
24 |
25 | [Fact]
26 | public void Simple()
27 | {
28 | MockContextFactory.SeedAndTestContextFor("SortInterceptorTests_Simple", TestSeeders.SimpleSeedScenario, ctx =>
29 | {
30 | // expected
31 | var expected = ctx.Orders.OrderBy(t => t.Customer.FirstName).ThenBy(t => t.Customer.LastName).ToList();
32 |
33 | // criteria
34 | var criteria = new QueryCriteria();
35 | criteria.Sorts.Add(new Sort("CustomerFullName"));
36 | var queryHandler = new QueryHandler(Enumerable.Empty());
37 | queryHandler.AddInterceptor(new MockSortInterceptor());
38 | var result = queryHandler.Execute(ctx.Orders, criteria);
39 | Assert.Equal(expected, result.Data);
40 | });
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery.Test/SortTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using PoweredSoft.DynamicLinq;
6 | using PoweredSoft.DynamicQuery.Core;
7 | using PoweredSoft.DynamicQuery.Test.Mock;
8 | using Xunit;
9 |
10 | namespace PoweredSoft.DynamicQuery.Test
11 | {
12 | public class SortTests
13 | {
14 | [Fact]
15 | public void Simple()
16 | {
17 | MockContextFactory.SeedAndTestContextFor("SortTests_Simple", TestSeeders.SimpleSeedScenario, ctx =>
18 | {
19 | var shouldResult = ctx.Orders.OrderBy(t => t.OrderNum).ToList();
20 |
21 | // query handler that is empty should be the same as running to list.
22 | var criteria = new QueryCriteria()
23 | {
24 | Sorts = new List()
25 | {
26 | new Sort("OrderNum")
27 | }
28 | };
29 |
30 | var queryHandler = new QueryHandler(Enumerable.Empty());
31 | var result = queryHandler.Execute(ctx.Orders, criteria);
32 | Assert.Equal(shouldResult, result.Data);
33 | });
34 | }
35 |
36 | [Fact]
37 | public void MultiSort()
38 | {
39 | MockContextFactory.SeedAndTestContextFor("SortTests_MultiSort", TestSeeders.SimpleSeedScenario, ctx =>
40 | {
41 | var shouldResult = ctx.Orders.OrderBy(t => t.Customer.FirstName).ThenBy(t => t.OrderNum).ToList();
42 |
43 | // query handler that is empty should be the same as running to list.
44 | var criteria = new QueryCriteria()
45 | {
46 | Sorts = new List()
47 | {
48 | new Sort("Customer.FirstName"),
49 | new Sort("OrderNum")
50 | }
51 | };
52 |
53 | var queryHandler = new QueryHandler(Enumerable.Empty());
54 | var result = queryHandler.Execute(ctx.Orders, criteria);
55 | Assert.Equal(shouldResult, result.Data);
56 | });
57 | }
58 |
59 | private class MockSortInterceptor : ISortInterceptor
60 | {
61 | public IEnumerable InterceptSort(IEnumerable sort)
62 | {
63 | if (sort.Count() == 1 && sort.First().Path == "OrderNumStr")
64 | return new ISort[] { new Sort("OrderNum", false) };
65 |
66 | return sort;
67 | }
68 | }
69 |
70 | [Fact]
71 | public void SortInterceptor()
72 | {
73 | MockContextFactory.SeedAndTestContextFor("SortTests_SortInterceptor", TestSeeders.SimpleSeedScenario, ctx =>
74 | {
75 | var shouldResult = ctx.Orders.OrderByDescending(t => t.OrderNum).ToList();
76 |
77 | // query handler that is empty should be the same as running to list.
78 | var criteria = new QueryCriteria()
79 | {
80 | Sorts = new List()
81 | {
82 | new Sort("OrderNumStr", false)
83 | }
84 | };
85 |
86 | var queryHandler = new QueryHandler(Enumerable.Empty());
87 | queryHandler.AddInterceptor(new MockSortInterceptor());
88 | var result = queryHandler.Execute(ctx.Orders, criteria);
89 | Assert.Equal(shouldResult, result.Data);
90 | });
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery/Aggregate.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using PoweredSoft.DynamicQuery.Core;
5 |
6 | namespace PoweredSoft.DynamicQuery
7 | {
8 | public class Aggregate : IAggregate
9 | {
10 | public string Path { get; set; }
11 | public AggregateType Type { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery/Extensions/FilterExtensions.cs:
--------------------------------------------------------------------------------
1 | using PoweredSoft.DynamicQuery.Core;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Linq.Expressions;
6 | using System.Reflection;
7 | using System.Text;
8 |
9 | namespace PoweredSoft.DynamicQuery.Extensions
10 | {
11 | public static class FilterExtensions
12 | {
13 | public static bool IsSimpleFilter(this IFilter filter) => filter is ISimpleFilter;
14 | public static bool IsCompositeFilter(this IFilter filter) => filter is ICompositeFilter;
15 |
16 | public static bool IsSimpleFilterOn(this IFilter filter, string path)
17 | {
18 | var simpleFilter = filter as ISimpleFilter;
19 | if (simpleFilter == null)
20 | return false;
21 |
22 | var result = simpleFilter.Path?.Equals(path, StringComparison.InvariantCultureIgnoreCase) == true;
23 | return result;
24 | }
25 |
26 | public static bool IsSimpleFilterOn(this IFilter filter, Expression> expr)
27 | {
28 | var resolved = GetPropertySymbol(expr);
29 | return filter.IsSimpleFilterOn(resolved);
30 | }
31 |
32 | public static ISimpleFilter ReplaceByOn(this IFilter filter, string path)
33 | {
34 | var simpleFilter = filter as ISimpleFilter;
35 | if (simpleFilter == null)
36 | throw new Exception("Must be a simple filter");
37 |
38 | var ret = new SimpleFilter();
39 | ret.And = filter.And;
40 | ret.Type = filter.Type;
41 | ret.Value = simpleFilter.Value;
42 | ret.Path = path;
43 | return ret;
44 | }
45 |
46 | public static ISimpleFilter ReplaceByOn(this IFilter filter, Expression> expr)
47 | {
48 | var resolved = GetPropertySymbol(expr);
49 | return filter.ReplaceByOn(resolved);
50 | }
51 |
52 | public static ICompositeFilter ReplaceByCompositeOn(this IFilter filter, params string[] paths)
53 | {
54 | var simpleFilter = filter as ISimpleFilter;
55 | if (simpleFilter == null)
56 | throw new Exception("Must be a simple filter");
57 |
58 | var compositeFilter = new CompositeFilter();
59 | compositeFilter.And = filter.And;
60 | compositeFilter.Type = FilterType.Composite;
61 | compositeFilter.Filters = paths
62 | .Select(t => new SimpleFilter
63 | {
64 | Type = filter.Type,
65 | Path = t,
66 | And = false,
67 | Value = simpleFilter.Value
68 | })
69 | .AsEnumerable()
70 | .ToList();
71 | return compositeFilter;
72 | }
73 |
74 | public static ICompositeFilter ReplaceByCompositeOn(this IFilter filter, params Expression>[] exprs)
75 | {
76 | var paths = exprs.Select(expr => GetPropertySymbol(expr)).ToArray();
77 | return ReplaceByCompositeOn(filter, paths);
78 | }
79 |
80 | internal static string GetPropertySymbol(Expression> expression)
81 | {
82 | return string.Join(".",
83 | GetMembersOnPath(expression.Body as MemberExpression)
84 | .Select(m => m.Member.Name)
85 | .Reverse());
86 | }
87 |
88 | internal static IEnumerable GetMembersOnPath(MemberExpression expression)
89 | {
90 | while (expression != null)
91 | {
92 | yield return expression;
93 | expression = expression.Expression as MemberExpression;
94 | }
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery/Extensions/FilterTypeExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using PoweredSoft.DynamicLinq;
5 | using PoweredSoft.DynamicQuery.Core;
6 |
7 | namespace PoweredSoft.DynamicQuery.Extensions
8 | {
9 | public static class FilterTypeExtensions
10 | {
11 | public static ConditionOperators? ConditionOperator(this FilterType filterType)
12 | {
13 | if (filterType == FilterType.Equal)
14 | return ConditionOperators.Equal;
15 | if (filterType == FilterType.NotEqual)
16 | return ConditionOperators.NotEqual;
17 | if (filterType == FilterType.GreaterThan)
18 | return ConditionOperators.GreaterThan;
19 | if (filterType == FilterType.GreaterThanOrEqual)
20 | return ConditionOperators.GreaterThanOrEqual;
21 | if (filterType == FilterType.LessThan)
22 | return ConditionOperators.LessThan;
23 | if (filterType == FilterType.LessThanOrEqual)
24 | return ConditionOperators.LessThanOrEqual;
25 | if (filterType == FilterType.StartsWith)
26 | return ConditionOperators.StartsWith;
27 | if (filterType == FilterType.EndsWith)
28 | return ConditionOperators.EndsWith;
29 | if (filterType == FilterType.Contains)
30 | return ConditionOperators.Contains;
31 | if (filterType == FilterType.In)
32 | return ConditionOperators.In;
33 | if (filterType == FilterType.NotIn)
34 | return ConditionOperators.NotIn;
35 |
36 | return null;
37 | }
38 |
39 | public static SelectTypes? SelectType(this AggregateType aggregateType)
40 | {
41 | if (aggregateType == AggregateType.Avg)
42 | return SelectTypes.Average;
43 | if (aggregateType == AggregateType.Count)
44 | return SelectTypes.Count;
45 | if (aggregateType == AggregateType.LongCount)
46 | return SelectTypes.LongCount;
47 | if (aggregateType == AggregateType.Sum)
48 | return SelectTypes.Sum;
49 | else if (aggregateType == AggregateType.Min)
50 | return SelectTypes.Min;
51 | else if (aggregateType == AggregateType.Max)
52 | return SelectTypes.Max;
53 | else if (aggregateType == AggregateType.First)
54 | return SelectTypes.First;
55 | else if (aggregateType == AggregateType.FirstOrDefault)
56 | return SelectTypes.FirstOrDefault;
57 | else if (aggregateType == AggregateType.Last)
58 | return SelectTypes.Last;
59 | else if (aggregateType == AggregateType.LastOrDefault)
60 | return SelectTypes.LastOrDefault;
61 |
62 |
63 | return null;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery/Extensions/GroupResultExtensions.cs:
--------------------------------------------------------------------------------
1 | using PoweredSoft.DynamicQuery.Core;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace PoweredSoft.DynamicQuery.Extensions
7 | {
8 | public static class GroupResultExtensions
9 | {
10 | public static IQueryExecutionGroupResult GroupedResult(this IQueryExecutionResult source)
11 | {
12 | if (source is IQueryExecutionGroupResult ret)
13 | return ret;
14 |
15 | throw new Exception("this result is not a grouped result");
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery/Filter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using PoweredSoft.DynamicQuery.Core;
5 |
6 | namespace PoweredSoft.DynamicQuery
7 | {
8 | public abstract class Filter : IFilter
9 | {
10 | public bool? And { get; set; }
11 | public FilterType Type { get; set; }
12 | }
13 |
14 | public class SimpleFilter : ISimpleFilter
15 | {
16 | public bool? And { get; set; }
17 | public bool? Not { get; set; }
18 | public FilterType Type { get; set; }
19 | public string Path { get; set; }
20 | public object Value { get; set; }
21 | public bool? CaseInsensitive { get; set; }
22 | }
23 |
24 | public class CompositeFilter : ICompositeFilter
25 | {
26 | public bool? And { get; set; }
27 | public FilterType Type { get; set; } = FilterType.Composite;
28 | public List Filters { get; set; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery/Group.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using PoweredSoft.DynamicQuery.Core;
5 |
6 | namespace PoweredSoft.DynamicQuery
7 | {
8 | public class Group : IGroup
9 | {
10 | public string Path { get; set; }
11 | public bool? Ascending { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery/PoweredSoft.DynamicQuery.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | Powered Softwares Inc.
6 | MIT
7 | https://github.com/PoweredSoft/DynamicQuery
8 | https://github.com/PoweredSoft/DynamicQuery
9 | github
10 | powered,soft,dynamic,criteria,query,builder
11 | 3.0.0$(VersionSuffix)
12 | https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&r=g&d=retro
13 | PoweredSoft.DynamicQuery
14 | dynamic query based on string path very usefull for network requests.
15 | PoweredSoft.DynamicQuery
16 | False
17 | Powered Soft
18 | David Lebee
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery/QueryCriteria.cs:
--------------------------------------------------------------------------------
1 | using PoweredSoft.DynamicQuery.Core;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Text;
5 |
6 | namespace PoweredSoft.DynamicQuery
7 | {
8 | public class QueryCriteria : IQueryCriteria
9 | {
10 | public int? Page { get; set; }
11 | public int? PageSize { get; set; }
12 | public List Sorts { get; set; } = new List();
13 | public List Filters { get; set; } = new List();
14 | public List Groups { get; set; } = new List();
15 | public List Aggregates { get; set; } = new List();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/PoweredSoft.DynamicQuery/QueryHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Text;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 | using PoweredSoft.DynamicLinq;
10 | using PoweredSoft.DynamicLinq.Fluent;
11 | using PoweredSoft.DynamicQuery.Core;
12 |
13 | namespace PoweredSoft.DynamicQuery
14 | {
15 | public class QueryHandler : QueryHandlerBase, IQueryHandler
16 | {
17 | public QueryHandler(IEnumerable queryableInterceptorProviders) : base(queryableInterceptorProviders)
18 | {
19 | }
20 |
21 | protected virtual IQueryExecutionResult FinalExecute()
22 | {
23 | CommonBeforeExecute();
24 | return HasGrouping ? ExecuteGrouping() : ExecuteNoGrouping