├── .gitattributes ├── .gitignore ├── .paket ├── Paket.Restore.targets ├── paket.bootstrapper.exe └── paket.targets ├── LICENSE ├── README.md ├── Xrm.Oss.FluentQuery.sln ├── build.cmd ├── build.fsx ├── coverage.xml ├── paket.dependencies ├── paket.lock ├── paket.template ├── reports ├── Xrm.Oss.FluentQuery_FluentConditionExpression.htm ├── Xrm.Oss.FluentQuery_FluentFilterExpression.htm ├── Xrm.Oss.FluentQuery_FluentLinkEntity.htm ├── Xrm.Oss.FluentQuery_FluentOrderExpression.htm ├── Xrm.Oss.FluentQuery_FluentPagingInfo.htm ├── Xrm.Oss.FluentQuery_FluentQuery_1.htm ├── Xrm.Oss.FluentQuery_IOrganizationServiceFluentQuery.htm ├── badge_branchcoverage.png ├── badge_branchcoverage.svg ├── badge_combined.svg ├── badge_linecoverage.png ├── badge_linecoverage.svg ├── combined.js ├── icon_cube.svg ├── icon_down-dir_active.svg ├── icon_fork.svg ├── icon_info-circled.svg ├── icon_minus.svg ├── icon_plus.svg ├── icon_search-minus.svg ├── icon_search-plus.svg ├── icon_up-dir.svg ├── icon_up-dir_active.svg ├── icon_wrench.svg ├── index.htm └── report.css └── src ├── lib └── Xrm.Oss.FluentQuery │ ├── .vs │ └── Xrm.Oss.FluentQuery │ │ └── v15 │ │ └── Server │ │ └── sqlite3 │ │ ├── db.lock │ │ └── storage.ide │ ├── FluentQuery.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── Xrm.Oss.FluentQuery.csproj │ └── paket.references └── test └── Xrm.Oss.FluentQuery.Tests ├── CacheTests.cs ├── FluentFilterExpressionTests.cs ├── FluentLinkEntityTests.cs ├── FluentOrderExpressionTests.cs ├── FluentPagingInfoTests.cs ├── FluentQueryTests.cs ├── Properties └── AssemblyInfo.cs ├── Xrm.Oss.FluentQuery.Tests.csproj └── paket.references /.gitattributes: -------------------------------------------------------------------------------- 1 | # These files are text and should be normalized (convert crlf => lf) 2 | *.cmd text 3 | *.config text 4 | *.Config text 5 | *.cs text diff=csharp 6 | *.csproj text 7 | *.datasource text 8 | *.disco text 9 | *.edmx text 10 | *.map text 11 | *.md text 12 | *.msbuild text 13 | *.ps1 text 14 | *.settings text 15 | *.sln text 16 | *.svcinfo text 17 | *.svcmap text 18 | *.t4properties text 19 | *.tt text 20 | *.txt text 21 | *.vspscc text 22 | *.wsdl text 23 | *.xaml text 24 | *.xsd text 25 | 26 | # Images should be treated as binary 27 | # (binary is a macro for -text -diff) 28 | *.ico binary 29 | *.jepg binary 30 | *.jpg binary 31 | *.sdf binary 32 | *.pdf binary 33 | *.png binary 34 | 35 | # Exclude report coverage from linguist 36 | reports/* linguist-documentation 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | #ignore thumbnails created by windows 3 | Thumbs.db 4 | #Ignore files build by Visual Studio 5 | *.obj 6 | *.exe 7 | *.pdb 8 | *.user 9 | *.aps 10 | *.pch 11 | *.vspscc 12 | *_i.c 13 | *_p.c 14 | *.ncb 15 | *.suo 16 | *.tlb 17 | *.tlh 18 | *.bak 19 | *.cache 20 | *.ilk 21 | *.log 22 | [Bb]in 23 | [Dd]ebug*/ 24 | *.lib 25 | *.sbr 26 | obj/ 27 | [Rr]elease*/ 28 | _ReSharper*/ 29 | [Tt]est[Rr]esult* 30 | packages/* 31 | tools/* 32 | build/* 33 | *.sln.DotSettings 34 | *.sln.ide/* 35 | nuget/* 36 | Publish/* 37 | test/* 38 | .fake/* 39 | temp/* 40 | paket-files/* 41 | .vs/* -------------------------------------------------------------------------------- /.paket/Paket.Restore.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 8 | 9 | $(MSBuildVersion) 10 | 15.0.0 11 | false 12 | true 13 | 14 | true 15 | $(MSBuildThisFileDirectory) 16 | $(MSBuildThisFileDirectory)..\ 17 | $(PaketRootPath)paket-files\paket.restore.cached 18 | $(PaketRootPath)paket.lock 19 | classic 20 | proj 21 | assembly 22 | native 23 | /Library/Frameworks/Mono.framework/Commands/mono 24 | mono 25 | 26 | 27 | $(PaketRootPath)paket.bootstrapper.exe 28 | $(PaketToolsPath)paket.bootstrapper.exe 29 | $([System.IO.Path]::GetDirectoryName("$(PaketBootStrapperExePath)"))\ 30 | 31 | "$(PaketBootStrapperExePath)" 32 | $(MonoPath) --runtime=v4.0.30319 "$(PaketBootStrapperExePath)" 33 | 34 | 35 | 36 | 37 | true 38 | true 39 | 40 | 41 | True 42 | 43 | 44 | False 45 | 46 | $(BaseIntermediateOutputPath.TrimEnd('\').TrimEnd('\/')) 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | $(PaketRootPath)paket 56 | $(PaketToolsPath)paket 57 | 58 | 59 | 60 | 61 | 62 | $(PaketRootPath)paket.exe 63 | $(PaketToolsPath)paket.exe 64 | 65 | 66 | 67 | 68 | 69 | <_DotnetToolsJson Condition="Exists('$(PaketRootPath)/.config/dotnet-tools.json')">$([System.IO.File]::ReadAllText("$(PaketRootPath)/.config/dotnet-tools.json")) 70 | <_ConfigContainsPaket Condition=" '$(_DotnetToolsJson)' != ''">$(_DotnetToolsJson.Contains('"paket"')) 71 | <_ConfigContainsPaket Condition=" '$(_ConfigContainsPaket)' == ''">false 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | <_PaketCommand>dotnet paket 83 | 84 | 85 | 86 | 87 | 88 | $(PaketToolsPath)paket 89 | $(PaketBootStrapperExeDir)paket 90 | 91 | 92 | paket 93 | 94 | 95 | 96 | 97 | <_PaketExeExtension>$([System.IO.Path]::GetExtension("$(PaketExePath)")) 98 | <_PaketCommand Condition=" '$(_PaketCommand)' == '' AND '$(_PaketExeExtension)' == '.dll' ">dotnet "$(PaketExePath)" 99 | <_PaketCommand Condition=" '$(_PaketCommand)' == '' AND '$(OS)' != 'Windows_NT' AND '$(_PaketExeExtension)' == '.exe' ">$(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)" 100 | <_PaketCommand Condition=" '$(_PaketCommand)' == '' ">"$(PaketExePath)" 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | true 122 | $(NoWarn);NU1603;NU1604;NU1605;NU1608 123 | false 124 | true 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | $([System.IO.File]::ReadAllText('$(PaketRestoreCacheFile)')) 134 | 135 | 136 | 137 | 138 | 139 | 141 | $([System.Text.RegularExpressions.Regex]::Split(`%(Identity)`, `": "`)[0].Replace(`"`, ``).Replace(` `, ``)) 142 | $([System.Text.RegularExpressions.Regex]::Split(`%(Identity)`, `": "`)[1].Replace(`"`, ``).Replace(` `, ``)) 143 | 144 | 145 | 146 | 147 | %(PaketRestoreCachedKeyValue.Value) 148 | %(PaketRestoreCachedKeyValue.Value) 149 | 150 | 151 | 152 | 153 | true 154 | false 155 | true 156 | 157 | 158 | 162 | 163 | true 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | $(PaketIntermediateOutputPath)\$(MSBuildProjectFile).paket.references.cached 183 | 184 | $(MSBuildProjectFullPath).paket.references 185 | 186 | $(MSBuildProjectDirectory)\$(MSBuildProjectName).paket.references 187 | 188 | $(MSBuildProjectDirectory)\paket.references 189 | 190 | false 191 | true 192 | true 193 | references-file-or-cache-not-found 194 | 195 | 196 | 197 | 198 | $([System.IO.File]::ReadAllText('$(PaketReferencesCachedFilePath)')) 199 | $([System.IO.File]::ReadAllText('$(PaketOriginalReferencesFilePath)')) 200 | references-file 201 | false 202 | 203 | 204 | 205 | 206 | false 207 | 208 | 209 | 210 | 211 | true 212 | target-framework '$(TargetFramework)' or '$(TargetFrameworks)' files @(PaketResolvedFilePaths) 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | false 224 | true 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',').Length) 236 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[0]) 237 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[1]) 238 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[4]) 239 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[5]) 240 | 241 | 242 | %(PaketReferencesFileLinesInfo.PackageVersion) 243 | All 244 | runtime 245 | runtime 246 | true 247 | true 248 | 249 | 250 | 251 | 252 | $(PaketIntermediateOutputPath)/$(MSBuildProjectFile).paket.clitools 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | $([System.String]::Copy('%(PaketCliToolFileLines.Identity)').Split(',')[0]) 262 | $([System.String]::Copy('%(PaketCliToolFileLines.Identity)').Split(',')[1]) 263 | 264 | 265 | %(PaketCliToolFileLinesInfo.PackageVersion) 266 | 267 | 268 | 269 | 273 | 274 | 275 | 276 | 277 | 278 | false 279 | 280 | 281 | 282 | 283 | 284 | <_NuspecFilesNewLocation Include="$(PaketIntermediateOutputPath)\$(Configuration)\*.nuspec"/> 285 | 286 | 287 | 288 | 289 | 290 | $(MSBuildProjectDirectory)/$(MSBuildProjectFile) 291 | true 292 | false 293 | true 294 | false 295 | true 296 | false 297 | true 298 | false 299 | true 300 | $(PaketIntermediateOutputPath)\$(Configuration) 301 | $(PaketIntermediateOutputPath) 302 | 303 | 304 | 305 | <_NuspecFiles Include="$(AdjustedNuspecOutputPath)\*.$(PackageVersion.Split(`+`)[0]).nuspec"/> 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 363 | 364 | 407 | 408 | 450 | 451 | 492 | 493 | 494 | 495 | -------------------------------------------------------------------------------- /.paket/paket.bootstrapper.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XRM-OSS/Xrm-Fluent-Query/21d69462ecdd91b45f6e8a3611bfcfb12c8643a5/.paket/paket.bootstrapper.exe -------------------------------------------------------------------------------- /.paket/paket.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | true 7 | $(MSBuildThisFileDirectory) 8 | $(MSBuildThisFileDirectory)..\ 9 | /Library/Frameworks/Mono.framework/Commands/mono 10 | mono 11 | 12 | 13 | 14 | 15 | $(PaketRootPath)paket.exe 16 | $(PaketToolsPath)paket.exe 17 | "$(PaketExePath)" 18 | $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)" 19 | 20 | 21 | 22 | 23 | 24 | $(MSBuildProjectFullPath).paket.references 25 | 26 | 27 | 28 | 29 | $(MSBuildProjectDirectory)\$(MSBuildProjectName).paket.references 30 | 31 | 32 | 33 | 34 | $(MSBuildProjectDirectory)\paket.references 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | $(MSBuildProjectDirectory)\$(MSBuildProjectName).paket.references 47 | $(MSBuildProjectDirectory)\paket.references 48 | $(MSBuildStartupDirectory)\paket.references 49 | $(MSBuildProjectFullPath).paket.references 50 | $(PaketCommand) restore --references-files "$(PaketReferences)" 51 | 52 | RestorePackages; $(BuildDependsOn); 53 | 54 | 55 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Florian Krönert 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XRM Fluent Query [![Build status](https://ci.appveyor.com/api/projects/status/x0o7dqnhnwi2i8bk?svg=true)](https://ci.appveyor.com/project/DigitalFlow/xrm-fluent-query) [![NuGet Badge](https://buildstats.info/nuget/Xrm.Oss.FluentQuery.Sources)](https://www.nuget.org/packages/Xrm.Oss.FluentQuery.Sources) 2 | 3 | |Line Coverage|Branch Coverage| 4 | |-----|-----------------| 5 | |[![Line coverage](https://cdn.rawgit.com/digitalflow/xrm-fluent-query/master/reports/badge_linecoverage.svg)](https://cdn.rawgit.com/digitalflow/xrm-fluent-query/master/reports/index.htm)|[![Branch coverage](https://cdn.rawgit.com/digitalflow/xrm-fluent-query/master/reports/badge_branchcoverage.svg)](https://cdn.rawgit.com/digitalflow/xrm-fluent-query/master/reports/index.htm)| 6 | 7 | This is a library for fluent query operations in Dynamics CRM / Dynamics365. 8 | 9 | # Requirements 10 | This library can be used in CRM Plugins / Workflow Activities and in code of external applications. It is distributed as source file, so you don't need to merge DLLs. 11 | It does not include references / dependencies to any CRM SDK, so you can just install it and choose the CRM SDK that you need yourself. 12 | All CRM versions from 2011 to 365 are supported, just include the one you need to your project. 13 | 14 | # Remarks 15 | This library sets the NoLock parameter on your QueryExpressions to true by default (plain QueryExpressions don't). 16 | This is a recommended best practice, as otherwise the records would be locked in the database before retrieval, which might result in decreased performance. 17 | Not locking the data before retrieval might result in dirty reads however, if the record has an update transaction executing at the same time. 18 | There's also a limit of how much concurrent locked queries can run concurrently. Find out more about NoLock [here](https://msdn.microsoft.com/en-us/library/microsoft.xrm.sdk.query.queryexpression.nolock.aspx). 19 | 20 | If you choose to lock the data for retrieval, set `service.With.DataBaseLock()`. 21 | 22 | # Purpose 23 | QueryExpressions add nice IntelliSense, however they tend to be quite verbose, which leads to poor readability. 24 | This fluent interface aims to make queries as short and readable as possible while preserving IntelliSense. 25 | 26 | This could look something like this (When not developing early bound, you can simply leave out the generic parameter): 27 | ```C# 28 | var records = service.Query() 29 | .IncludeColumns("name", "address1_line1") 30 | .Where(e => e 31 | .Attribute(a => a 32 | .Named("name") 33 | .Is(ConditionOperator.Equal) 34 | .To("Adventure Works") 35 | ) 36 | ) 37 | .Link(l => l 38 | .FromEntity("account") 39 | .FromAttribute("primarycontactid") 40 | .ToEntity("contact") 41 | .ToAttribute("contactid") 42 | .With.LinkType(JoinOperator.LeftOuter) 43 | ) 44 | .RetrieveAll(); 45 | ``` 46 | 47 | The equivalent QueryExpression for above fluent query would be: 48 | ```C# 49 | var query = new QueryExpression 50 | { 51 | EntityName = "account", 52 | ColumnSet = new ColumnSet("name", "address1_line1"), 53 | NoLock = true, 54 | Criteria = new FilterExpression 55 | { 56 | Conditions = 57 | { 58 | new ConditionExpression("name", ConditionOperator.Equal, "Adventure Works") 59 | } 60 | }, 61 | LinkEntities = 62 | { 63 | new LinkEntity 64 | { 65 | LinkFromEntityName = "account", 66 | LinkFromAttributeName = "primarycontactid", 67 | LinkToEntityName = "contact", 68 | LinkToAttributeName = "contactid" 69 | } 70 | } 71 | }; 72 | 73 | var records = service.RetrieveMultiple(query).Entities.Select(e => e.ToEntity()).ToList(); 74 | ``` 75 | 76 | I believe the fluent syntax to be much easier to understand at first glance. 77 | 78 | In addition to that, it automatically casts your entity objects if you call it with the generic parameter and implements automatic retrieval of all pages. 79 | 80 | # How to build it 81 | If you want to build this library yourself, just call 82 | 83 | ```PowerShell 84 | .\build.cmd 85 | ``` 86 | -------------------------------------------------------------------------------- /Xrm.Oss.FluentQuery.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2036 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xrm.Oss.FluentQuery", "src\lib\Xrm.Oss.FluentQuery\Xrm.Oss.FluentQuery.csproj", "{4058FA56-2332-44A5-AED4-DA20526434AC}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xrm.Oss.FluentQuery.Tests", "src\test\Xrm.Oss.FluentQuery.Tests\Xrm.Oss.FluentQuery.Tests.csproj", "{332626A0-8AD8-410B-AA4E-13D9DB9A2860}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{7E727FD8-3B56-4205-8EFD-99882AA46664}" 11 | ProjectSection(SolutionItems) = preProject 12 | build.fsx = build.fsx 13 | paket.template = paket.template 14 | EndProjectSection 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "meta", "meta", "{C554C7D0-5E3D-41A9-8A5B-7B5E344C3007}" 17 | ProjectSection(SolutionItems) = preProject 18 | README.md = README.md 19 | EndProjectSection 20 | EndProject 21 | Global 22 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 23 | Debug|Any CPU = Debug|Any CPU 24 | Release|Any CPU = Release|Any CPU 25 | EndGlobalSection 26 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 27 | {4058FA56-2332-44A5-AED4-DA20526434AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {4058FA56-2332-44A5-AED4-DA20526434AC}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {4058FA56-2332-44A5-AED4-DA20526434AC}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {4058FA56-2332-44A5-AED4-DA20526434AC}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {332626A0-8AD8-410B-AA4E-13D9DB9A2860}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {332626A0-8AD8-410B-AA4E-13D9DB9A2860}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {332626A0-8AD8-410B-AA4E-13D9DB9A2860}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {332626A0-8AD8-410B-AA4E-13D9DB9A2860}.Release|Any CPU.Build.0 = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(SolutionProperties) = preSolution 37 | HideSolutionNode = FALSE 38 | EndGlobalSection 39 | GlobalSection(ExtensibilityGlobals) = postSolution 40 | SolutionGuid = {252D671D-91C5-4B60-88FC-789EBC1C7034} 41 | EndGlobalSection 42 | EndGlobal 43 | -------------------------------------------------------------------------------- /build.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | .paket\paket.bootstrapper.exe 4 | 5 | .paket\paket.exe restore 6 | if errorlevel 1 ( 7 | exit /b %errorlevel% 8 | ) 9 | 10 | packages\FAKE\tools\FAKE.exe build.fsx %* -------------------------------------------------------------------------------- /build.fsx: -------------------------------------------------------------------------------- 1 | // include Fake libs 2 | #I @"packages\FAKE\tools\" 3 | #r @"packages\FAKE\tools\FakeLib.dll" 4 | 5 | open Fake 6 | open Fake.AssemblyInfoFile 7 | open System.IO 8 | open Fake.Paket 9 | open Fake.OpenCoverHelper 10 | open Fake.ReportGeneratorHelper 11 | open Fake.FileHelper 12 | open Fake.Testing.NUnit3 13 | 14 | //Project config 15 | let projectName = "Xrm.Oss.FluentQuery" 16 | let projectDescription = "A Dynamics CRM / Dynamics365 library for fluent queries" 17 | let authors = ["Florian Kroenert"] 18 | 19 | // Directories 20 | let buildDir = @".\build\" 21 | let libbuildDir = buildDir + @"lib\" 22 | 23 | let testDir = @".\test\" 24 | 25 | let nUnitPath = "packages" @@ "nunit.consolerunner" @@ "tools" @@ "nunit3-console.exe" 26 | let deployDir = @".\Publish\" 27 | let libdeployDir = deployDir + @"lib\" 28 | let nugetDir = @".\nuget\" 29 | let packagesDir = @".\packages\" 30 | 31 | // version info 32 | let mutable majorversion = "2" 33 | let mutable minorversion = "1" 34 | let mutable patch = "3" 35 | let mutable nugetVersion = "" 36 | let mutable asmVersion = "" 37 | let mutable asmInfoVersion = "" 38 | 39 | let sha = "" //Git.Information.getCurrentHash() 40 | 41 | // Targets 42 | Target "Clean" (fun _ -> 43 | 44 | CleanDirs [buildDir; testDir; deployDir; nugetDir] 45 | ) 46 | 47 | Target "BuildVersions" (fun _ -> 48 | asmVersion <- majorversion + "." + minorversion + "." + patch 49 | asmInfoVersion <- asmVersion + " - " + sha 50 | 51 | let nugetBuildNumber = if not isLocalBuild then patch else "0" 52 | 53 | nugetVersion <- majorversion + "." + minorversion + "." + nugetBuildNumber 54 | 55 | SetBuildNumber nugetVersion // Publish version to TeamCity 56 | ) 57 | 58 | Target "AssemblyInfo" (fun _ -> 59 | BulkReplaceAssemblyInfoVersions "src" (fun f -> 60 | {f with 61 | AssemblyVersion = asmVersion 62 | AssemblyInformationalVersion = asmInfoVersion 63 | AssemblyFileVersion = asmVersion}) 64 | ) 65 | 66 | Target "BuildLib" (fun _ -> 67 | !! @"src\lib\**\*.csproj" 68 | |> MSBuildRelease libbuildDir "Build" 69 | |> Log "Build-Output: " 70 | ) 71 | 72 | Target "BuildTest" (fun _ -> 73 | !! @"src\test\**\*.csproj" 74 | |> MSBuildDebug testDir "Build" 75 | |> Log "Build Log: " 76 | ) 77 | 78 | Target "NUnit" (fun _ -> 79 | let testFiles = !!(testDir @@ @"\**\*.Tests.dll") 80 | 81 | if testFiles.Includes.Length <> 0 then 82 | testFiles 83 | |> NUnit3 (fun test -> 84 | {test with 85 | ShadowCopy = false; 86 | ToolPath = nUnitPath;}) 87 | ) 88 | 89 | Target "CodeCoverage" (fun _ -> 90 | OpenCover (fun p -> { p with 91 | TestRunnerExePath = nUnitPath 92 | ExePath ="packages" @@ "OpenCover" @@ "tools" @@ "OpenCover.Console.exe" 93 | Register = RegisterType.RegisterUser 94 | WorkingDir = (testDir) 95 | Filter = "+[Xrm.Oss*]* -[*.Tests*]*" 96 | Output = "../coverage.xml" 97 | }) "Xrm.Oss.FluentQuery.Tests.dll" 98 | ) 99 | 100 | Target "ReportCodeCoverage" (fun _ -> 101 | ReportGenerator (fun p -> { p with 102 | ExePath = "packages" @@ "ReportGenerator" @@ "tools" @@ "ReportGenerator.exe" 103 | WorkingDir = (testDir) 104 | TargetDir = "../reports" 105 | ReportTypes = [ReportGeneratorReportType.Html; ReportGeneratorReportType.Badges ] 106 | }) [ "..\coverage.xml" ] 107 | 108 | ) 109 | 110 | Target "Publish" (fun _ -> 111 | CreateDir libdeployDir 112 | 113 | !! (libbuildDir @@ @"*.*") 114 | |> CopyTo libdeployDir 115 | ) 116 | 117 | Target "CreateNuget" (fun _ -> 118 | Pack (fun p -> 119 | {p with 120 | Version = nugetVersion 121 | }) 122 | ) 123 | 124 | // Dependencies 125 | "Clean" 126 | ==> "BuildVersions" 127 | =?> ("AssemblyInfo", not isLocalBuild ) 128 | ==> "BuildLib" 129 | ==> "BuildTest" 130 | ==> "NUnit" 131 | ==> "CodeCoverage" 132 | ==> "ReportCodeCoverage" 133 | ==> "Publish" 134 | ==> "CreateNuget" 135 | 136 | // start build 137 | RunTargetOrDefault "Publish" 138 | -------------------------------------------------------------------------------- /paket.dependencies: -------------------------------------------------------------------------------- 1 | source https://www.nuget.org/api/v2 2 | nuget FAKE 3 | nuget FakeItEasy 1.25.3 4 | nuget FakeXrmEasy.365 5 | nuget Microsoft.CrmSdk.CoreAssemblies 6 | nuget NUnit 7 | nuget nunit.consolerunner 8 | nuget NUnit3TestAdapter 9 | nuget OpenCover 10 | nuget ReportGenerator -------------------------------------------------------------------------------- /paket.template: -------------------------------------------------------------------------------- 1 | type file 2 | id Xrm.Oss.FluentQuery.Sources 3 | version 1.0 4 | authors Florian Krönert 5 | title 6 | Xrm-Fluent-Query 7 | tags 8 | Xrm Dynamics CRM 365 QueryExpression Query DSL OrganizationService 9 | projectUrl 10 | https://github.com/DigitalFlow/Xrm-Fluent-Query 11 | licenseUrl 12 | https://github.com/DigitalFlow/Xrm-Fluent-Query/blob/master/LICENSE 13 | description 14 | A Dynamics CRM / Dynamics 365 library for fluent IOrganizationService queries 15 | frameworkAssemblies 16 | System.Runtime.Caching 17 | files 18 | src\lib\Xrm.Oss.FluentQuery\FluentQuery.cs ==> contentFiles/cs/any/lib 19 | src\lib\Xrm.Oss.FluentQuery\FluentQuery.cs ==> content 20 | -------------------------------------------------------------------------------- /reports/badge_branchcoverage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XRM-OSS/Xrm-Fluent-Query/21d69462ecdd91b45f6e8a3611bfcfb12c8643a5/reports/badge_branchcoverage.png -------------------------------------------------------------------------------- /reports/badge_branchcoverage.svg: -------------------------------------------------------------------------------- 1 | 2 | 30 | Code coverage 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Generated by: ReportGenerator 3.1.2.0 75 | 76 | 77 | 78 | Coverage 79 | Coverage 80 | 81 | 90%90% 82 | 83 | 84 | 85 | 86 | Branch coverage 87 | 88 | -------------------------------------------------------------------------------- /reports/badge_combined.svg: -------------------------------------------------------------------------------- 1 | 2 | 30 | Code coverage 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Generated by: ReportGenerator 3.1.2.0 75 | 76 | 77 | 78 | Coverage 79 | Coverage 80 | 97.8%97.8% 81 | 90%90% 82 | 83 | 84 | 85 | Line coverage 86 | Branch coverage 87 | 88 | -------------------------------------------------------------------------------- /reports/badge_linecoverage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XRM-OSS/Xrm-Fluent-Query/21d69462ecdd91b45f6e8a3611bfcfb12c8643a5/reports/badge_linecoverage.png -------------------------------------------------------------------------------- /reports/badge_linecoverage.svg: -------------------------------------------------------------------------------- 1 | 2 | 30 | Code coverage 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | Generated by: ReportGenerator 3.1.2.0 75 | 76 | 77 | 78 | Coverage 79 | Coverage 80 | 97.8%97.8% 81 | 82 | 83 | 84 | 85 | Line coverage 86 | 87 | 88 | -------------------------------------------------------------------------------- /reports/icon_cube.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reports/icon_down-dir_active.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reports/icon_fork.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reports/icon_info-circled.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reports/icon_minus.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reports/icon_plus.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reports/icon_search-minus.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reports/icon_search-plus.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reports/icon_up-dir.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reports/icon_up-dir_active.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reports/icon_wrench.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reports/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Summary - Coverage Report 8 | 9 |
10 |

Summary

11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
Generated on:06.05.2020 - 16:16:44
Parser:OpenCoverParser
Assemblies:1
Classes:7
Files:1
Covered lines:312
Uncovered lines:7
Coverable lines:319
Total lines:1030
Line coverage:97.8%
Branch coverage:90%
30 |

Coverage

31 |
32 |
33 |
34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
NameCoveredUncoveredCoverableTotalLine coverageBranch coverage
Xrm.Oss.FluentQuery3127319721097.8%
  
90%
  
Xrm.Oss.FluentQuery.FluentConditionExpression360361030100%
 
 
Xrm.Oss.FluentQuery.FluentFilterExpression260261030100%
 
 
Xrm.Oss.FluentQuery.FluentLinkEntity530531030100%
 
 
Xrm.Oss.FluentQuery.FluentOrderExpression190191030100%
 
 
Xrm.Oss.FluentQuery.FluentPagingInfo260261030100%
 
 
Xrm.Oss.FluentQuery.FluentQuery`11437150103095.3%
  
90%
  
Xrm.Oss.FluentQuery.IOrganizationServiceFluentQuery9091030100%
 
 
59 |
60 | 61 | -------------------------------------------------------------------------------- /reports/report.css: -------------------------------------------------------------------------------- 1 | html { font-family: sans-serif; margin: 0; padding: 0; font-size: 0.9em; background-color: #d6d6d6; height: 100%; } 2 | body { margin: 0; padding: 0; height: 100%; } 3 | h1 { font-family: 'Century Gothic', sans-serif; font-size: 1.2em; font-weight: normal; color: #fff; background-color: #6f6f6f; padding: 10px; margin: 20px -20px 20px -20px; } 4 | h1:first-of-type { margin-top: 0; } 5 | h2 { font-size: 1.0em; font-weight: bold; margin: 10px 0 15px 0; padding: 0; } 6 | h3 { font-size: 1.0em; font-weight: bold; margin: 0 0 10px 0; padding: 0; display: inline-block; } 7 | a { color: #c00; text-decoration: none; } 8 | a:hover { color: #000; text-decoration: none; } 9 | 10 | .container { margin: auto; max-width: 1500px; width: 90%; background-color: #fff; display: table; box-shadow: 0 0 60px #7d7d7d; height: 100%; } 11 | .containerleft { display: table-cell; padding: 0 20px 20px 20px; } 12 | .containerright { border-left: solid 1px #6f6f6f; display: table-cell; width: 340px; min-width: 340px; background-color: #e5e5e5; height: 100%; } 13 | .containerrightfixed { position: fixed; padding: 0 20px 20px 20px; width: 300px; overflow-y: auto; height: 100%; top: 0; bottom: 0; } 14 | .containerrightfixed h1 { background-color: #c00; } 15 | .containerrightfixed label, .containerright a { white-space: nowrap; overflow: hidden; display: inline-block; max-width: 300px; text-overflow: ellipsis; } 16 | .containerright a { margin-bottom: 3px; } 17 | 18 | @media screen and (max-width:1200px){ 19 | .container { box-shadow: none; width: 100%; } 20 | .containerright { display: none; } 21 | } 22 | 23 | .footer { font-size: 0.7em; text-align: center; margin-top: 35px; } 24 | 25 | th { text-align: left; } 26 | .table-fixed { table-layout: fixed; } 27 | .overview { border: solid 1px #c1c1c1; border-collapse: collapse; width: 100%; word-wrap: break-word; } 28 | .overview th { border: solid 1px #c1c1c1; border-collapse: collapse; padding: 2px 4px 2px 4px; background-color: #ddd; } 29 | .overview tr.namespace th { background-color: #dcdcdc; } 30 | .overview thead th { background-color: #d1d1d1; } 31 | .overview th a { color: #000; } 32 | .overview tr.namespace a { margin-left: 15px; } 33 | .overview td { border: solid 1px #c1c1c1; border-collapse: collapse; padding: 2px 5px 2px 5px; } 34 | .coverage { border: solid 1px #c1c1c1; border-collapse: collapse; font-size: 5px; height: 10px; } 35 | .coverage td { padding: 0; border: none; } 36 | .stripped tr:nth-child(2n+1) { background-color: #F3F3F3; } 37 | 38 | .customizebox { font-size: 0.75em; margin-bottom: 7px; } 39 | .customizebox div { width: 33.33%; display: inline-block; } 40 | .customizebox input { font-size: 0.8em; width: 150px; } 41 | #namespaceslider { width: 200px; display: inline-block; margin-left: 8px; } 42 | 43 | .percentagebarundefined { 44 | border-left: 2px solid #fff; 45 | padding-left: 3px; 46 | } 47 | .percentagebar0 { 48 | border-left: 2px solid #f00; 49 | padding-left: 3px; 50 | } 51 | .percentagebar10 { 52 | border-left: 2px solid; 53 | border-image: linear-gradient(to bottom, #f00 90%, #00ff21 90%, #00ff21 100%) 1; 54 | padding-left: 3px; 55 | } 56 | .percentagebar20 { 57 | border-left: 2px solid; 58 | border-image: linear-gradient(to bottom, #f00 80%, #00ff21 80%, #00ff21 100%) 1; 59 | padding-left: 3px; 60 | } 61 | .percentagebar30 { 62 | border-left: 2px solid; 63 | border-image: linear-gradient(to bottom, #f00 70%, #00ff21 70%, #00ff21 100%) 1; 64 | padding-left: 3px; 65 | } 66 | .percentagebar40 { 67 | border-left: 2px solid; 68 | border-image: linear-gradient(to bottom, #f00 60%, #00ff21 60%, #00ff21 100%) 1; 69 | padding-left: 3px; 70 | } 71 | .percentagebar50 { 72 | border-left: 2px solid; 73 | border-image: linear-gradient(to bottom, #f00 50%, #00ff21 50%, #00ff21 100%) 1; 74 | padding-left: 3px; 75 | } 76 | .percentagebar60 { 77 | border-left: 2px solid; 78 | border-image: linear-gradient(to bottom, #f00 40%, #00ff21 40%, #00ff21 100%) 1; 79 | padding-left: 3px; 80 | } 81 | .percentagebar70 { 82 | border-left: 2px solid; 83 | border-image: linear-gradient(to bottom, #f00 30%, #00ff21 30%, #00ff21 100%) 1; 84 | padding-left: 3px; 85 | } 86 | .percentagebar80 { 87 | border-left: 2px solid; 88 | border-image: linear-gradient(to bottom, #f00 20%, #00ff21 20%, #00ff21 100%) 1; 89 | padding-left: 3px; 90 | } 91 | .percentagebar90 { 92 | border-left: 2px solid; 93 | border-image: linear-gradient(to bottom, #f00 10%, #00ff21 10%, #00ff21 100%) 1; 94 | padding-left: 3px; 95 | } 96 | .percentagebar100 { 97 | border-left: 2px solid #00ff21; 98 | padding-left: 3px; 99 | } 100 | 101 | .hidden, .ng-hide { display: none; } 102 | .right { text-align: right; } 103 | .center { text-align: center; } 104 | .rightmargin { padding-right: 8px; } 105 | .leftmargin { padding-left: 5px; } 106 | .green { background-color: #00ff21; } 107 | .lightgreen { background-color: #dcf4dc; } 108 | .red { background-color: #f00; } 109 | .lightred { background-color: #f7dede; } 110 | .orange { background-color: #FFA500; } 111 | .lightorange { background-color: #FFEFD5; } 112 | .gray { background-color: #dcdcdc; } 113 | .lightgray { color: #888888; } 114 | 115 | .toggleZoom { text-align:right; } 116 | 117 | .ct-chart { position: relative; } 118 | .ct-chart .ct-line { stroke-width: 2px !important; } 119 | .ct-chart .ct-point { stroke-width: 6px !important; transition: stroke-width .2s; } 120 | .ct-chart .ct-point:hover { stroke-width: 10px !important; } 121 | .ct-chart .ct-series.ct-series-a .ct-line, .ct-chart .ct-series.ct-series-a .ct-point { stroke: #c00 !important;} 122 | .ct-chart .ct-series.ct-series-b .ct-line, .ct-chart .ct-series.ct-series-b .ct-point { stroke: #1c2298 !important;} 123 | 124 | .tinylinecoveragechart, .tinybranchcoveragechart { background-color: #fff; margin-left: -3px; float: left; border: solid 1px #c1c1c1; width: 30px; height: 18px; } 125 | 126 | .tinylinecoveragechart .ct-line, .tinybranchcoveragechart .ct-line { stroke-width: 1px !important; } 127 | .tinybranchcoveragechart .ct-series.ct-series-a .ct-line { stroke: #1c2298 !important; } 128 | 129 | .linecoverage { background-color: #c00; width: 10px; height: 8px; border: 1px solid #000; display: inline-block; } 130 | .branchcoverage { background-color: #1c2298; width: 10px; height: 8px; border: 1px solid #000; display: inline-block; } 131 | 132 | .tooltip { position: absolute; display: none; padding: 5px; background: #F4C63D;color: #453D3F; pointer-events: none; z-index: 1; } 133 | .tooltip:after { content: ""; position: absolute; top: 100%; left: 50%; width: 0; height: 0; margin-left: -15px; border: 15px solid transparent; border-top-color: #F4C63D; } 134 | 135 | .column1324 { max-width: 1324px; } 136 | .column674 { max-width: 674px; } 137 | .column60 { width: 60px; } 138 | .column70 { width: 70px; } 139 | .column90 { width: 90px; } 140 | .column98 { width: 98px; } 141 | .column100 { width: 100px; } 142 | .column105 { width: 105px; } 143 | .column112 { width: 112px; } 144 | .column135 { width: 135px; } 145 | 146 | .covered0 { width: 0px; } 147 | .covered1 { width: 1px; } 148 | .covered2 { width: 2px; } 149 | .covered3 { width: 3px; } 150 | .covered4 { width: 4px; } 151 | .covered5 { width: 5px; } 152 | .covered6 { width: 6px; } 153 | .covered7 { width: 7px; } 154 | .covered8 { width: 8px; } 155 | .covered9 { width: 9px; } 156 | .covered10 { width: 10px; } 157 | .covered11 { width: 11px; } 158 | .covered12 { width: 12px; } 159 | .covered13 { width: 13px; } 160 | .covered14 { width: 14px; } 161 | .covered15 { width: 15px; } 162 | .covered16 { width: 16px; } 163 | .covered17 { width: 17px; } 164 | .covered18 { width: 18px; } 165 | .covered19 { width: 19px; } 166 | .covered20 { width: 20px; } 167 | .covered21 { width: 21px; } 168 | .covered22 { width: 22px; } 169 | .covered23 { width: 23px; } 170 | .covered24 { width: 24px; } 171 | .covered25 { width: 25px; } 172 | .covered26 { width: 26px; } 173 | .covered27 { width: 27px; } 174 | .covered28 { width: 28px; } 175 | .covered29 { width: 29px; } 176 | .covered30 { width: 30px; } 177 | .covered31 { width: 31px; } 178 | .covered32 { width: 32px; } 179 | .covered33 { width: 33px; } 180 | .covered34 { width: 34px; } 181 | .covered35 { width: 35px; } 182 | .covered36 { width: 36px; } 183 | .covered37 { width: 37px; } 184 | .covered38 { width: 38px; } 185 | .covered39 { width: 39px; } 186 | .covered40 { width: 40px; } 187 | .covered41 { width: 41px; } 188 | .covered42 { width: 42px; } 189 | .covered43 { width: 43px; } 190 | .covered44 { width: 44px; } 191 | .covered45 { width: 45px; } 192 | .covered46 { width: 46px; } 193 | .covered47 { width: 47px; } 194 | .covered48 { width: 48px; } 195 | .covered49 { width: 49px; } 196 | .covered50 { width: 50px; } 197 | .covered51 { width: 51px; } 198 | .covered52 { width: 52px; } 199 | .covered53 { width: 53px; } 200 | .covered54 { width: 54px; } 201 | .covered55 { width: 55px; } 202 | .covered56 { width: 56px; } 203 | .covered57 { width: 57px; } 204 | .covered58 { width: 58px; } 205 | .covered59 { width: 59px; } 206 | .covered60 { width: 60px; } 207 | .covered61 { width: 61px; } 208 | .covered62 { width: 62px; } 209 | .covered63 { width: 63px; } 210 | .covered64 { width: 64px; } 211 | .covered65 { width: 65px; } 212 | .covered66 { width: 66px; } 213 | .covered67 { width: 67px; } 214 | .covered68 { width: 68px; } 215 | .covered69 { width: 69px; } 216 | .covered70 { width: 70px; } 217 | .covered71 { width: 71px; } 218 | .covered72 { width: 72px; } 219 | .covered73 { width: 73px; } 220 | .covered74 { width: 74px; } 221 | .covered75 { width: 75px; } 222 | .covered76 { width: 76px; } 223 | .covered77 { width: 77px; } 224 | .covered78 { width: 78px; } 225 | .covered79 { width: 79px; } 226 | .covered80 { width: 80px; } 227 | .covered81 { width: 81px; } 228 | .covered82 { width: 82px; } 229 | .covered83 { width: 83px; } 230 | .covered84 { width: 84px; } 231 | .covered85 { width: 85px; } 232 | .covered86 { width: 86px; } 233 | .covered87 { width: 87px; } 234 | .covered88 { width: 88px; } 235 | .covered89 { width: 89px; } 236 | .covered90 { width: 90px; } 237 | .covered91 { width: 91px; } 238 | .covered92 { width: 92px; } 239 | .covered93 { width: 93px; } 240 | .covered94 { width: 94px; } 241 | .covered95 { width: 95px; } 242 | .covered96 { width: 96px; } 243 | .covered97 { width: 97px; } 244 | .covered98 { width: 98px; } 245 | .covered99 { width: 99px; } 246 | .covered100 { width: 100px; } 247 | 248 | @media print { 249 | html, body { background-color: #fff; } 250 | .container { max-width: 100%; width: 100%; padding: 0; } 251 | .overview colgroup col:first-child { width: 300px; } 252 | } 253 | 254 | .icon-up-dir_active { 255 | background-image: url(icon_up-dir.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNDA4IDEyMTZxMCAyNi0xOSA0NXQtNDUgMTloLTg5NnEtMjYgMC00NS0xOXQtMTktNDUgMTktNDVsNDQ4LTQ0OHExOS0xOSA0NS0xOXQ0NSAxOWw0NDggNDQ4cTE5IDE5IDE5IDQ1eiIvPjwvc3ZnPg==); 256 | background-repeat: no-repeat; 257 | background-size: contain; 258 | padding-left: 15px; 259 | height: 1em; 260 | display: inline-block; 261 | position: relative; 262 | top: 3px; 263 | } 264 | .icon-down-dir_active { 265 | background-image: url(icon_up-dir_active.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNDA4IDcwNHEwIDI2LTE5IDQ1bC00NDggNDQ4cS0xOSAxOS00NSAxOXQtNDUtMTlsLTQ0OC00NDhxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5aDg5NnEyNiAwIDQ1IDE5dDE5IDQ1eiIvPjwvc3ZnPg==); 266 | background-repeat: no-repeat; 267 | background-size: contain; 268 | padding-left: 15px; 269 | height: 1em; 270 | display: inline-block; 271 | position: relative; 272 | top: 3px; 273 | } 274 | .icon-down-dir { 275 | background-image: url(icon_down-dir_active.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNDA4IDcwNHEwIDI2LTE5IDQ1bC00NDggNDQ4cS0xOSAxOS00NSAxOXQtNDUtMTlsLTQ0OC00NDhxLTE5LTE5LTE5LTQ1dDE5LTQ1IDQ1LTE5aDg5NnEyNiAwIDQ1IDE5dDE5IDQ1eiIvPjwvc3ZnPg==); 276 | background-repeat: no-repeat; 277 | background-size: contain; 278 | padding-left: 15px; 279 | height: 1em; 280 | display: inline-block; 281 | position: relative; 282 | top: 3px; 283 | } 284 | .icon-info-circled { 285 | background-image: url(icon_info-circled.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxjaXJjbGUgY3g9Ijg5NiIgY3k9Ijg5NiIgcj0iNzUwIiBmaWxsPSIjZmZmIiAvPjxwYXRoIGZpbGw9IiMyOEE1RkYiIGQ9Ik0xMTUyIDEzNzZ2LTE2MHEwLTE0LTktMjN0LTIzLTloLTk2di01MTJxMC0xNC05LTIzdC0yMy05aC0zMjBxLTE0IDAtMjMgOXQtOSAyM3YxNjBxMCAxNCA5IDIzdDIzIDloOTZ2MzIwaC05NnEtMTQgMC0yMyA5dC05IDIzdjE2MHEwIDE0IDkgMjN0MjMgOWg0NDhxMTQgMCAyMy05dDktMjN6bS0xMjgtODk2di0xNjBxMC0xNC05LTIzdC0yMy05aC0xOTJxLTE0IDAtMjMgOXQtOSAyM3YxNjBxMCAxNCA5IDIzdDIzIDloMTkycTE0IDAgMjMtOXQ5LTIzem02NDAgNDE2cTAgMjA5LTEwMyAzODUuNXQtMjc5LjUgMjc5LjUtMzg1LjUgMTAzLTM4NS41LTEwMy0yNzkuNS0yNzkuNS0xMDMtMzg1LjUgMTAzLTM4NS41IDI3OS41LTI3OS41IDM4NS41LTEwMyAzODUuNSAxMDMgMjc5LjUgMjc5LjUgMTAzIDM4NS41eiIvPjwvc3ZnPg==); 286 | background-repeat: no-repeat; 287 | background-size: contain; 288 | padding-left: 15px; 289 | height: 1em; 290 | display: inline-block; 291 | } 292 | .icon-plus { 293 | background-image: url(icon_plus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0xNjAwIDczNnYxOTJxMCA0MC0yOCA2OHQtNjggMjhoLTQxNnY0MTZxMCA0MC0yOCA2OHQtNjggMjhoLTE5MnEtNDAgMC02OC0yOHQtMjgtNjh2LTQxNmgtNDE2cS00MCAwLTY4LTI4dC0yOC02OHYtMTkycTAtNDAgMjgtNjh0NjgtMjhoNDE2di00MTZxMC00MCAyOC02OHQ2OC0yOGgxOTJxNDAgMCA2OCAyOHQyOCA2OHY0MTZoNDE2cTQwIDAgNjggMjh0MjggNjh6Ii8+PC9zdmc+); 294 | background-repeat: no-repeat; 295 | background-size: contain; 296 | padding-left: 15px; 297 | height: 1em; 298 | display: inline-block; 299 | position: relative; 300 | top: 3px; 301 | } 302 | .icon-minus { 303 | background-image: url(icon_minus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiNjMDAiIGQ9Ik0xNjAwIDczNnYxOTJxMCA0MC0yOCA2OHQtNjggMjhoLTEyMTZxLTQwIDAtNjgtMjh0LTI4LTY4di0xOTJxMC00MCAyOC02OHQ2OC0yOGgxMjE2cTQwIDAgNjggMjh0MjggNjh6Ii8+PC9zdmc+); 304 | background-repeat: no-repeat; 305 | background-size: contain; 306 | padding-left: 15px; 307 | height: 1em; 308 | display: inline-block; 309 | position: relative; 310 | top: 3px; 311 | } 312 | .icon-wrench { 313 | background-image: url(icon_wrench.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHN0eWxlPSJmaWxsOiNlNWU1ZTUiIC8+PHBhdGggZD0iTTQ0OCAxNDcycTAtMjYtMTktNDV0LTQ1LTE5LTQ1IDE5LTE5IDQ1IDE5IDQ1IDQ1IDE5IDQ1LTE5IDE5LTQ1em02NDQtNDIwbC02ODIgNjgycS0zNyAzNy05MCAzNy01MiAwLTkxLTM3bC0xMDYtMTA4cS0zOC0zNi0zOC05MCAwLTUzIDM4LTkxbDY4MS02ODFxMzkgOTggMTE0LjUgMTczLjV0MTczLjUgMTE0LjV6bTYzNC00MzVxMCAzOS0yMyAxMDYtNDcgMTM0LTE2NC41IDIxNy41dC0yNTguNSA4My41cS0xODUgMC0zMTYuNS0xMzEuNXQtMTMxLjUtMzE2LjUgMTMxLjUtMzE2LjUgMzE2LjUtMTMxLjVxNTggMCAxMjEuNSAxNi41dDEwNy41IDQ2LjVxMTYgMTEgMTYgMjh0LTE2IDI4bC0yOTMgMTY5djIyNGwxOTMgMTA3cTUtMyA3OS00OC41dDEzNS41LTgxIDcwLjUtMzUuNXExNSAwIDIzLjUgMTB0OC41IDI1eiIvPjwvc3ZnPg==); 314 | background-repeat: no-repeat; 315 | background-size: contain; 316 | padding-left: 20px; 317 | height: 1em; 318 | display: inline-block; 319 | } 320 | .icon-fork { 321 | background-image: url(icon_fork.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHN0eWxlPSJmaWxsOiNmZmYiIC8+PHBhdGggZD0iTTY3MiAxNDcycTAtNDAtMjgtNjh0LTY4LTI4LTY4IDI4LTI4IDY4IDI4IDY4IDY4IDI4IDY4LTI4IDI4LTY4em0wLTExNTJxMC00MC0yOC02OHQtNjgtMjgtNjggMjgtMjggNjggMjggNjggNjggMjggNjgtMjggMjgtNjh6bTY0MCAxMjhxMC00MC0yOC02OHQtNjgtMjgtNjggMjgtMjggNjggMjggNjggNjggMjggNjgtMjggMjgtNjh6bTk2IDBxMCA1Mi0yNiA5Ni41dC03MCA2OS41cS0yIDI4Ny0yMjYgNDE0LTY3IDM4LTIwMyA4MS0xMjggNDAtMTY5LjUgNzF0LTQxLjUgMTAwdjI2cTQ0IDI1IDcwIDY5LjV0MjYgOTYuNXEwIDgwLTU2IDEzNnQtMTM2IDU2LTEzNi01Ni01Ni0xMzZxMC01MiAyNi05Ni41dDcwLTY5LjV2LTgyMHEtNDQtMjUtNzAtNjkuNXQtMjYtOTYuNXEwLTgwIDU2LTEzNnQxMzYtNTYgMTM2IDU2IDU2IDEzNnEwIDUyLTI2IDk2LjV0LTcwIDY5LjV2NDk3cTU0LTI2IDE1NC01NyA1NS0xNyA4Ny41LTI5LjV0NzAuNS0zMSA1OS0zOS41IDQwLjUtNTEgMjgtNjkuNSA4LjUtOTEuNXEtNDQtMjUtNzAtNjkuNXQtMjYtOTYuNXEwLTgwIDU2LTEzNnQxMzYtNTYgMTM2IDU2IDU2IDEzNnoiLz48L3N2Zz4=); 322 | background-repeat: no-repeat; 323 | background-size: contain; 324 | padding-left: 20px; 325 | height: 1em; 326 | display: inline-block; 327 | } 328 | .icon-cube { 329 | background-image: url(icon_cube.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxyZWN0IHdpZHRoPSIxNzkyIiBoZWlnaHQ9IjE3OTIiIHN0eWxlPSJmaWxsOiNlNWU1ZTUiIC8+PHBhdGggZD0iTTg5NiAxNjI5bDY0MC0zNDl2LTYzNmwtNjQwIDIzM3Y3NTJ6bS02NC04NjVsNjk4LTI1NC02OTgtMjU0LTY5OCAyNTR6bTgzMi0yNTJ2NzY4cTAgMzUtMTggNjV0LTQ5IDQ3bC03MDQgMzg0cS0yOCAxNi02MSAxNnQtNjEtMTZsLTcwNC0zODRxLTMxLTE3LTQ5LTQ3dC0xOC02NXYtNzY4cTAtNDAgMjMtNzN0NjEtNDdsNzA0LTI1NnEyMi04IDQ0LTh0NDQgOGw3MDQgMjU2cTM4IDE0IDYxIDQ3dDIzIDczeiIvPjwvc3ZnPg==); 330 | background-repeat: no-repeat; 331 | background-size: contain; 332 | padding-left: 20px; 333 | height: 1em; 334 | display: inline-block; 335 | } 336 | .icon-search-plus { 337 | background-image: url(icon_search-plus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiM2ZjZmNmYiIGQ9Ik0xMDg4IDgwMHY2NHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtMjI0djIyNHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtNjRxLTEzIDAtMjIuNS05LjV0LTkuNS0yMi41di0yMjRoLTIyNHEtMTMgMC0yMi41LTkuNXQtOS41LTIyLjV2LTY0cTAtMTMgOS41LTIyLjV0MjIuNS05LjVoMjI0di0yMjRxMC0xMyA5LjUtMjIuNXQyMi41LTkuNWg2NHExMyAwIDIyLjUgOS41dDkuNSAyMi41djIyNGgyMjRxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXptMTI4IDMycTAtMTg1LTEzMS41LTMxNi41dC0zMTYuNS0xMzEuNS0zMTYuNSAxMzEuNS0xMzEuNSAzMTYuNSAxMzEuNSAzMTYuNSAzMTYuNSAxMzEuNSAzMTYuNS0xMzEuNSAxMzEuNS0zMTYuNXptNTEyIDgzMnEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41cS01NCAwLTkwLTM4bC0zNDMtMzQycS0xNzkgMTI0LTM5OSAxMjQtMTQzIDAtMjczLjUtNTUuNXQtMjI1LTE1MC0xNTAtMjI1LTU1LjUtMjczLjUgNTUuNS0yNzMuNSAxNTAtMjI1IDIyNS0xNTAgMjczLjUtNTUuNSAyNzMuNSA1NS41IDIyNSAxNTAgMTUwIDIyNSA1NS41IDI3My41cTAgMjIwLTEyNCAzOTlsMzQzIDM0M3EzNyAzNyAzNyA5MHoiLz48L3N2Zz4=); 338 | background-repeat: no-repeat; 339 | background-size: contain; 340 | padding-left: 20px; 341 | height: 1em; 342 | display: inline-block; 343 | } 344 | .icon-search-minus { 345 | background-image: url(icon_search-minus.svg), url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB3aWR0aD0iMTc5MiIgaGVpZ2h0PSIxNzkyIiB2aWV3Qm94PSIwIDAgMTc5MiAxNzkyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGZpbGw9IiM2ZjZmNmYiIGQ9Ik0xMDg4IDgwMHY2NHEwIDEzLTkuNSAyMi41dC0yMi41IDkuNWgtNTc2cS0xMyAwLTIyLjUtOS41dC05LjUtMjIuNXYtNjRxMC0xMyA5LjUtMjIuNXQyMi41LTkuNWg1NzZxMTMgMCAyMi41IDkuNXQ5LjUgMjIuNXptMTI4IDMycTAtMTg1LTEzMS41LTMxNi41dC0zMTYuNS0xMzEuNS0zMTYuNSAxMzEuNS0xMzEuNSAzMTYuNSAxMzEuNSAzMTYuNSAzMTYuNSAxMzEuNSAzMTYuNS0xMzEuNSAxMzEuNS0zMTYuNXptNTEyIDgzMnEwIDUzLTM3LjUgOTAuNXQtOTAuNSAzNy41cS01NCAwLTkwLTM4bC0zNDMtMzQycS0xNzkgMTI0LTM5OSAxMjQtMTQzIDAtMjczLjUtNTUuNXQtMjI1LTE1MC0xNTAtMjI1LTU1LjUtMjczLjUgNTUuNS0yNzMuNSAxNTAtMjI1IDIyNS0xNTAgMjczLjUtNTUuNSAyNzMuNSA1NS41IDIyNSAxNTAgMTUwIDIyNSA1NS41IDI3My41cTAgMjIwLTEyNCAzOTlsMzQzIDM0M3EzNyAzNyAzNyA5MHoiLz48L3N2Zz4=); 346 | background-repeat: no-repeat; 347 | background-size: contain; 348 | padding-left: 20px; 349 | height: 1em; 350 | display: inline-block; 351 | } 352 | 353 | .ct-double-octave:after,.ct-major-eleventh:after,.ct-major-second:after,.ct-major-seventh:after,.ct-major-sixth:after,.ct-major-tenth:after,.ct-major-third:after,.ct-major-twelfth:after,.ct-minor-second:after,.ct-minor-seventh:after,.ct-minor-sixth:after,.ct-minor-third:after,.ct-octave:after,.ct-perfect-fifth:after,.ct-perfect-fourth:after,.ct-square:after{content:"";clear:both}.ct-label{fill:rgba(0,0,0,.4);color:rgba(0,0,0,.4);font-size:.75rem;line-height:1}.ct-grid-background,.ct-line{fill:none}.ct-chart-bar .ct-label,.ct-chart-line .ct-label{display:block;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}.ct-chart-donut .ct-label,.ct-chart-pie .ct-label{dominant-baseline:central}.ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-label.ct-vertical.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-label.ct-vertical.ct-end{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-start{-webkit-box-align:flex-end;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-horizontal.ct-end{-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:start}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-start{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-end;-webkit-justify-content:flex-end;-ms-flex-pack:flex-end;justify-content:flex-end;text-align:right;text-anchor:end}.ct-chart-bar.ct-horizontal-bars .ct-label.ct-vertical.ct-end{-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:flex-start;-webkit-justify-content:flex-start;-ms-flex-pack:flex-start;justify-content:flex-start;text-align:left;text-anchor:end}.ct-grid{stroke:rgba(0,0,0,.2);stroke-width:1px;stroke-dasharray:2px}.ct-point{stroke-width:10px;stroke-linecap:round}.ct-line{stroke-width:4px}.ct-area{stroke:none;fill-opacity:.1}.ct-bar{fill:none;stroke-width:10px}.ct-slice-donut{fill:none;stroke-width:60px}.ct-series-a .ct-bar,.ct-series-a .ct-line,.ct-series-a .ct-point,.ct-series-a .ct-slice-donut{stroke:#d70206}.ct-series-a .ct-area,.ct-series-a .ct-slice-donut-solid,.ct-series-a .ct-slice-pie{fill:#d70206}.ct-series-b .ct-bar,.ct-series-b .ct-line,.ct-series-b .ct-point,.ct-series-b .ct-slice-donut{stroke:#f05b4f}.ct-series-b .ct-area,.ct-series-b .ct-slice-donut-solid,.ct-series-b .ct-slice-pie{fill:#f05b4f}.ct-series-c .ct-bar,.ct-series-c .ct-line,.ct-series-c .ct-point,.ct-series-c .ct-slice-donut{stroke:#f4c63d}.ct-series-c .ct-area,.ct-series-c .ct-slice-donut-solid,.ct-series-c .ct-slice-pie{fill:#f4c63d}.ct-series-d .ct-bar,.ct-series-d .ct-line,.ct-series-d .ct-point,.ct-series-d .ct-slice-donut{stroke:#d17905}.ct-series-d .ct-area,.ct-series-d .ct-slice-donut-solid,.ct-series-d .ct-slice-pie{fill:#d17905}.ct-series-e .ct-bar,.ct-series-e .ct-line,.ct-series-e .ct-point,.ct-series-e .ct-slice-donut{stroke:#453d3f}.ct-series-e .ct-area,.ct-series-e .ct-slice-donut-solid,.ct-series-e .ct-slice-pie{fill:#453d3f}.ct-series-f .ct-bar,.ct-series-f .ct-line,.ct-series-f .ct-point,.ct-series-f .ct-slice-donut{stroke:#59922b}.ct-series-f .ct-area,.ct-series-f .ct-slice-donut-solid,.ct-series-f .ct-slice-pie{fill:#59922b}.ct-series-g .ct-bar,.ct-series-g .ct-line,.ct-series-g .ct-point,.ct-series-g .ct-slice-donut{stroke:#0544d3}.ct-series-g .ct-area,.ct-series-g .ct-slice-donut-solid,.ct-series-g .ct-slice-pie{fill:#0544d3}.ct-series-h .ct-bar,.ct-series-h .ct-line,.ct-series-h .ct-point,.ct-series-h .ct-slice-donut{stroke:#6b0392}.ct-series-h .ct-area,.ct-series-h .ct-slice-donut-solid,.ct-series-h .ct-slice-pie{fill:#6b0392}.ct-series-i .ct-bar,.ct-series-i .ct-line,.ct-series-i .ct-point,.ct-series-i .ct-slice-donut{stroke:#f05b4f}.ct-series-i .ct-area,.ct-series-i .ct-slice-donut-solid,.ct-series-i .ct-slice-pie{fill:#f05b4f}.ct-series-j .ct-bar,.ct-series-j .ct-line,.ct-series-j .ct-point,.ct-series-j .ct-slice-donut{stroke:#dda458}.ct-series-j .ct-area,.ct-series-j .ct-slice-donut-solid,.ct-series-j .ct-slice-pie{fill:#dda458}.ct-series-k .ct-bar,.ct-series-k .ct-line,.ct-series-k .ct-point,.ct-series-k .ct-slice-donut{stroke:#eacf7d}.ct-series-k .ct-area,.ct-series-k .ct-slice-donut-solid,.ct-series-k .ct-slice-pie{fill:#eacf7d}.ct-series-l .ct-bar,.ct-series-l .ct-line,.ct-series-l .ct-point,.ct-series-l .ct-slice-donut{stroke:#86797d}.ct-series-l .ct-area,.ct-series-l .ct-slice-donut-solid,.ct-series-l .ct-slice-pie{fill:#86797d}.ct-series-m .ct-bar,.ct-series-m .ct-line,.ct-series-m .ct-point,.ct-series-m .ct-slice-donut{stroke:#b2c326}.ct-series-m .ct-area,.ct-series-m .ct-slice-donut-solid,.ct-series-m .ct-slice-pie{fill:#b2c326}.ct-series-n .ct-bar,.ct-series-n .ct-line,.ct-series-n .ct-point,.ct-series-n .ct-slice-donut{stroke:#6188e2}.ct-series-n .ct-area,.ct-series-n .ct-slice-donut-solid,.ct-series-n .ct-slice-pie{fill:#6188e2}.ct-series-o .ct-bar,.ct-series-o .ct-line,.ct-series-o .ct-point,.ct-series-o .ct-slice-donut{stroke:#a748ca}.ct-series-o .ct-area,.ct-series-o .ct-slice-donut-solid,.ct-series-o .ct-slice-pie{fill:#a748ca}.ct-square{display:block;position:relative;width:100%}.ct-square:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:100%}.ct-square:after{display:table}.ct-square>svg{display:block;position:absolute;top:0;left:0}.ct-minor-second{display:block;position:relative;width:100%}.ct-minor-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:93.75%}.ct-minor-second:after{display:table}.ct-minor-second>svg{display:block;position:absolute;top:0;left:0}.ct-major-second{display:block;position:relative;width:100%}.ct-major-second:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:88.8888888889%}.ct-major-second:after{display:table}.ct-major-second>svg{display:block;position:absolute;top:0;left:0}.ct-minor-third{display:block;position:relative;width:100%}.ct-minor-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:83.3333333333%}.ct-minor-third:after{display:table}.ct-minor-third>svg{display:block;position:absolute;top:0;left:0}.ct-major-third{display:block;position:relative;width:100%}.ct-major-third:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:80%}.ct-major-third:after{display:table}.ct-major-third>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fourth{display:block;position:relative;width:100%}.ct-perfect-fourth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:75%}.ct-perfect-fourth:after{display:table}.ct-perfect-fourth>svg{display:block;position:absolute;top:0;left:0}.ct-perfect-fifth{display:block;position:relative;width:100%}.ct-perfect-fifth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:66.6666666667%}.ct-perfect-fifth:after{display:table}.ct-perfect-fifth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-sixth{display:block;position:relative;width:100%}.ct-minor-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:62.5%}.ct-minor-sixth:after{display:table}.ct-minor-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-golden-section{display:block;position:relative;width:100%}.ct-golden-section:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:61.804697157%}.ct-golden-section:after{content:"";display:table;clear:both}.ct-golden-section>svg{display:block;position:absolute;top:0;left:0}.ct-major-sixth{display:block;position:relative;width:100%}.ct-major-sixth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:60%}.ct-major-sixth:after{display:table}.ct-major-sixth>svg{display:block;position:absolute;top:0;left:0}.ct-minor-seventh{display:block;position:relative;width:100%}.ct-minor-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:56.25%}.ct-minor-seventh:after{display:table}.ct-minor-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-seventh{display:block;position:relative;width:100%}.ct-major-seventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:53.3333333333%}.ct-major-seventh:after{display:table}.ct-major-seventh>svg{display:block;position:absolute;top:0;left:0}.ct-octave{display:block;position:relative;width:100%}.ct-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:50%}.ct-octave:after{display:table}.ct-octave>svg{display:block;position:absolute;top:0;left:0}.ct-major-tenth{display:block;position:relative;width:100%}.ct-major-tenth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:40%}.ct-major-tenth:after{display:table}.ct-major-tenth>svg{display:block;position:absolute;top:0;left:0}.ct-major-eleventh{display:block;position:relative;width:100%}.ct-major-eleventh:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:37.5%}.ct-major-eleventh:after{display:table}.ct-major-eleventh>svg{display:block;position:absolute;top:0;left:0}.ct-major-twelfth{display:block;position:relative;width:100%}.ct-major-twelfth:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:33.3333333333%}.ct-major-twelfth:after{display:table}.ct-major-twelfth>svg{display:block;position:absolute;top:0;left:0}.ct-double-octave{display:block;position:relative;width:100%}.ct-double-octave:before{display:block;float:left;content:"";width:0;height:0;padding-bottom:25%}.ct-double-octave:after{display:table}.ct-double-octave>svg{display:block;position:absolute;top:0;left:0} -------------------------------------------------------------------------------- /src/lib/Xrm.Oss.FluentQuery/.vs/Xrm.Oss.FluentQuery/v15/Server/sqlite3/db.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XRM-OSS/Xrm-Fluent-Query/21d69462ecdd91b45f6e8a3611bfcfb12c8643a5/src/lib/Xrm.Oss.FluentQuery/.vs/Xrm.Oss.FluentQuery/v15/Server/sqlite3/db.lock -------------------------------------------------------------------------------- /src/lib/Xrm.Oss.FluentQuery/.vs/Xrm.Oss.FluentQuery/v15/Server/sqlite3/storage.ide: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XRM-OSS/Xrm-Fluent-Query/21d69462ecdd91b45f6e8a3611bfcfb12c8643a5/src/lib/Xrm.Oss.FluentQuery/.vs/Xrm.Oss.FluentQuery/v15/Server/sqlite3/storage.ide -------------------------------------------------------------------------------- /src/lib/Xrm.Oss.FluentQuery/FluentQuery.cs: -------------------------------------------------------------------------------- 1 | /** 2 | MIT License 3 | 4 | Copyright (c) 2018 Florian Krönert 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | using Microsoft.Xrm.Sdk; 26 | using Microsoft.Xrm.Sdk.Query; 27 | using System; 28 | using System.Collections.Generic; 29 | using System.Linq; 30 | using System.Runtime.Caching; 31 | using System.Runtime.Serialization.Json; 32 | using System.IO; 33 | using System.Text; 34 | 35 | namespace Xrm.Oss.FluentQuery 36 | { 37 | public static class IOrganizationServiceFluentQuery 38 | { 39 | /** 40 | * 41 | * Creates a new fluent query for your early bound entity of type T, while automatically using the entity name of type T. Results will also be of type T. 42 | * If developing late bound, use the non-generic Query function. 43 | * 44 | * Fluent Query object. Use Retrieve or RetrieveAll for getting the results. 45 | */ 46 | public static IFluentQuery Query(this IOrganizationService service) where T : Entity, new() 47 | { 48 | return new FluentQuery(new T().LogicalName, service); 49 | } 50 | 51 | /** 52 | * 53 | * Creates a new fluent query for your early bound entity of type T, while automatically using the entity name of type T. Results will also be of type T. 54 | * If developing late bound, use the non-generic Query function. 55 | * 56 | * The logical name of the entity you want to query. 57 | * Fluent Query object. Use Retrieve or RetrieveAll for getting the results. 58 | */ 59 | [Obsolete("Entity name is no longer needed for early bound queries, use the parameterless overload. For late bound queries, use the non generic overload.")] 60 | public static IFluentQuery Query(this IOrganizationService service, string entityName) where T : Entity, new() 61 | { 62 | return new FluentQuery(entityName, service); 63 | } 64 | 65 | /** 66 | * 67 | * Creates a new fluent query in late binding style. Results will be of type Entity. 68 | * If developing early bound, use the generic Query function. 69 | * 70 | * The logical name of the entity you want to query. 71 | * Fluent Query object. Use Retrieve or RetrieveAll for getting the results. 72 | */ 73 | public static IFluentQuery Query(this IOrganizationService service, string entityName) 74 | { 75 | return new FluentQuery(entityName, service); 76 | } 77 | } 78 | 79 | public interface IFluentQuery where T : Entity 80 | { 81 | /** 82 | * 83 | * Adds the given columns to your query. Multiple calls will just add to the existing columns. 84 | * 85 | * Params array of your columns. 86 | */ 87 | IFluentQuery IncludeColumns(params string[] columns); 88 | 89 | /** 90 | * 91 | * Adds all columns to the query. This is disadvised, specify the columns you need if possible. 92 | * 93 | */ 94 | IFluentQuery IncludeAllColumns(); 95 | 96 | /** 97 | * 98 | * Returns the Query Expression that represents the current fluent query. 99 | * 100 | */ 101 | QueryExpression Expression { get; } 102 | 103 | /** 104 | * 105 | * Retrieves the first page for your query. 106 | * 107 | * Records retrieved from your query. 108 | */ 109 | List Retrieve(); 110 | 111 | /** 112 | * 113 | * Retrieves all pages for your query. 114 | * 115 | * Records retrieved from your query. 116 | */ 117 | List RetrieveAll(); 118 | 119 | /** 120 | * 121 | * Use this for setting further options in your query. 122 | * 123 | */ 124 | IFluentQuerySetting With { get; } 125 | 126 | /** 127 | * 128 | * Adds a link to a connected entity. 129 | * 130 | * Action for setting the link properties. Use a lambda for readability. 131 | */ 132 | IFluentQuery Link(Action definition); 133 | 134 | /** 135 | * 136 | * Adds filter conditions to your query. 137 | * 138 | * Multiple calls to this method currently override the existing filter. 139 | * Action for setting the filter properties. Use a lambda for readability. 140 | */ 141 | IFluentQuery Where(Action definition); 142 | 143 | /** 144 | * 145 | * Instructs to use the supplied cache. 146 | * Cache keys will automatically be generated by the used query expression. 147 | * Retrieving and setting of the cache items is done automatically when executing retrieve. 148 | * 149 | * The memory cache to use for getting and setting results 150 | * Expiration date for cached results 151 | */ 152 | IFluentQuery UseCache(MemoryCache cache, DateTimeOffset absoluteExpiration); 153 | 154 | /** 155 | * 156 | * Adds another condition to the top level filter expression. 157 | * 158 | * The condition expression to add. 159 | */ 160 | void AddCondition(Action definition); 161 | 162 | /** 163 | * 164 | * Adds a child filter to your top level filter. 165 | * 166 | * Action for setting the filter properties. Use a lambda for readability. 167 | */ 168 | void AddFilter(Action definition); 169 | 170 | /** 171 | * 172 | * Adds an order expression to your query. 173 | * 174 | * Action for setting the order properties. Use a lambda for readability. 175 | */ 176 | IFluentQuery Order(Action definition); 177 | } 178 | 179 | public interface IFluentQuerySetting where T : Entity 180 | { 181 | /** 182 | * 183 | * Set this for defining how many records you want to retrieve. The first top X records will be retrieved. 184 | * 185 | * Top X count of records to retrieve 186 | */ 187 | IFluentQuery RecordCount(int? topCount); 188 | 189 | /** 190 | * 191 | * Defines whether the record should be locked for retrieval. Not locking is a recommended best practice, but might lead to dirty reads. 192 | * 193 | * Default of true is recommended as best practice. Dirty reads might occur if data is written to this record simultaneously. Turn off if you know what you're doing. 194 | * True for locking the database record for retrieval, false otherwise. 195 | */ 196 | IFluentQuery DatabaseLock(bool useLock = true); 197 | 198 | /** 199 | * 200 | * Specifies whether duplicate records in your query will be filtered out or not. 201 | * 202 | * True for only returning unique records, false otherwise 203 | */ 204 | IFluentQuery UniqueRecords(bool unique = true); 205 | 206 | /** 207 | * 208 | * Adds paging info to your query, such as page size, page number or paging cookie. 209 | * 210 | * Use retrieve all for automatic retrieval of all records using paging. 211 | * Action for setting the paging info properties. Use a lambda for readability. 212 | */ 213 | IFluentQuery PagingInfo(Action definition); 214 | 215 | /** 216 | * 217 | * Determines whether to use a paging cookie when retrieving all records with paging. Speeds up retrieval, but may loose to result loss if you're adding link entities. 218 | * More on this topic: https://truenorthit.co.uk/2014/07/19/dynamics-crm-paging-cookies-some-gotchas/ 219 | * 220 | * Paging Cookies are not used by default 221 | * True for using cookie, false otherwise 222 | */ 223 | IFluentQuery PagingCookie(bool useCookie = true); 224 | 225 | /** 226 | * 227 | * Specifies whether the total record count of your query results should be retrieved. 228 | * 229 | * True for returning total record count, false otherwise. 230 | */ 231 | IFluentQuery TotalRecordCount(bool returnTotalRecordCount = true); 232 | } 233 | 234 | public class FluentQuery : IFluentQuery, IFluentQuerySetting where T : Entity 235 | { 236 | private QueryExpression _query; 237 | private bool _usePagingCookie; 238 | 239 | private IOrganizationService _service; 240 | private MemoryCache _cache; 241 | private DateTimeOffset _absoluteExpiration; 242 | 243 | public FluentQuery(string entityName, IOrganizationService service) 244 | { 245 | _query = new QueryExpression 246 | { 247 | EntityName = entityName, 248 | NoLock = true 249 | }; 250 | 251 | _service = service; 252 | _usePagingCookie = false; 253 | } 254 | 255 | public IFluentQuery IncludeColumns(params string[] columns) 256 | { 257 | _query.ColumnSet.AllColumns = false; 258 | _query.ColumnSet.AddColumns(columns); 259 | 260 | return this; 261 | } 262 | 263 | public IFluentQuery IncludeAllColumns() 264 | { 265 | _query.ColumnSet.AllColumns = true; 266 | 267 | return this; 268 | } 269 | 270 | public IFluentQuerySetting With 271 | { 272 | get 273 | { 274 | return this; 275 | } 276 | } 277 | 278 | public IFluentQuery RecordCount(int? topCount) 279 | { 280 | _query.TopCount = topCount; 281 | 282 | return this; 283 | } 284 | 285 | public IFluentQuery DatabaseLock(bool useLock = true) 286 | { 287 | _query.NoLock = !useLock; 288 | 289 | return this; 290 | } 291 | 292 | public IFluentQuery PagingCookie(bool useCookie = true) 293 | { 294 | _usePagingCookie = useCookie; 295 | 296 | return this; 297 | } 298 | 299 | private string GenerateQueryCacheKey(QueryExpression query) 300 | { 301 | using (var memoryStream = new MemoryStream()) 302 | { 303 | var serializer = new DataContractJsonSerializer(typeof(QueryExpression)); 304 | serializer.WriteObject(memoryStream, query); 305 | 306 | var serialized = Encoding.UTF8.GetString(memoryStream.ToArray()); 307 | return serialized; 308 | } 309 | } 310 | 311 | public List ReturnFromCache(string key) 312 | { 313 | if (_cache != null) 314 | { 315 | if (_cache.Contains(key)) 316 | { 317 | return _cache.Get(key) as List; 318 | } 319 | } 320 | 321 | return null; 322 | } 323 | 324 | public void SetCacheResult(string key, List result) 325 | { 326 | if (_cache != null) 327 | { 328 | _cache.Set(key, result, _absoluteExpiration); 329 | } 330 | } 331 | 332 | public List Retrieve() 333 | { 334 | var cacheKey = _cache != null ? GenerateQueryCacheKey(_query) : null; 335 | var cacheResult = ReturnFromCache(cacheKey); 336 | 337 | if (cacheResult != null) 338 | { 339 | return cacheResult; 340 | } 341 | 342 | var result = _service.RetrieveMultiple(_query).Entities.Select(e => e.ToEntity()) 343 | .ToList(); 344 | 345 | SetCacheResult(cacheKey, result); 346 | 347 | return result; 348 | } 349 | 350 | public List RetrieveAll() 351 | { 352 | var cacheKey = _cache != null ? GenerateQueryCacheKey(_query) : null; 353 | var cacheResult = ReturnFromCache(cacheKey); 354 | 355 | if (cacheResult != null) 356 | { 357 | return cacheResult; 358 | } 359 | 360 | var records = new List(); 361 | 362 | // Default to 1 as first page number, otherwise the first page is retrieved twice (once while supplying 0 as page number and again when supplying 1) 363 | var previousPageNumber = _query.PageInfo.PageNumber > 0 ? _query.PageInfo.PageNumber : 1; 364 | var previousPagingCookie = _query.PageInfo.PagingCookie; 365 | 366 | var moreRecords = false; 367 | var pageNumber = previousPageNumber; 368 | string pagingCookie = previousPagingCookie; 369 | 370 | do 371 | { 372 | _query.PageInfo.PageNumber = pageNumber; 373 | 374 | if (_usePagingCookie) 375 | { 376 | _query.PageInfo.PagingCookie = pagingCookie; 377 | } 378 | 379 | var response = _service.RetrieveMultiple(_query); 380 | var result = response.Entities.Select(e => e.ToEntity()) 381 | .ToList(); 382 | 383 | records.AddRange(result); 384 | 385 | moreRecords = response.MoreRecords; 386 | pagingCookie = response.PagingCookie; 387 | 388 | pageNumber++; 389 | } 390 | while (moreRecords); 391 | 392 | _query.PageInfo.PageNumber = previousPageNumber; 393 | _query.PageInfo.PagingCookie = previousPagingCookie; 394 | 395 | SetCacheResult(cacheKey, records); 396 | 397 | return records; 398 | } 399 | 400 | public IFluentQuery UniqueRecords(bool unique = true) 401 | { 402 | _query.Distinct = true; 403 | 404 | return this; 405 | } 406 | 407 | public IFluentQuery Link(Action definition) 408 | { 409 | var link = new FluentLinkEntity(); 410 | 411 | definition(link); 412 | 413 | _query.LinkEntities.Add(link.GetLinkEntity()); 414 | 415 | return this; 416 | } 417 | 418 | public IFluentQuery Where(Action definition) 419 | { 420 | var filter = new FluentFilterExpression(); 421 | 422 | definition(filter); 423 | 424 | _query.Criteria = filter.GetFilter(); 425 | 426 | return this; 427 | } 428 | 429 | public void AddCondition(Action definition) 430 | { 431 | var condition = new FluentConditionExpression(); 432 | 433 | definition(condition); 434 | 435 | _query.Criteria.AddCondition(condition.GetCondition()); 436 | } 437 | 438 | public void AddFilter(Action definition) 439 | { 440 | var filter = new FluentFilterExpression(); 441 | 442 | definition(filter); 443 | 444 | _query.Criteria.AddFilter(filter.GetFilter()); 445 | } 446 | 447 | public IFluentQuery Order(Action definition) 448 | { 449 | var order = new FluentOrderExpression(); 450 | 451 | definition(order); 452 | 453 | _query.Orders.Add(order.GetOrder()); 454 | 455 | return this; 456 | } 457 | 458 | public IFluentQuery PagingInfo(Action definition) 459 | { 460 | var PagingInfo = new FluentPagingInfo(); 461 | 462 | definition(PagingInfo); 463 | 464 | _query.PageInfo = PagingInfo.GetPagingInfo(); 465 | 466 | return this; 467 | } 468 | 469 | public IFluentQuery TotalRecordCount(bool returnTotalRecordCount = true) 470 | { 471 | _query.PageInfo.ReturnTotalRecordCount = true; 472 | 473 | return this; 474 | } 475 | 476 | public IFluentQuery UseCache(MemoryCache cache, DateTimeOffset absoluteExpiration) 477 | { 478 | _cache = cache; 479 | _absoluteExpiration = absoluteExpiration; 480 | 481 | return this; 482 | } 483 | 484 | public QueryExpression Expression 485 | { 486 | get 487 | { 488 | return _query; 489 | } 490 | } 491 | } 492 | 493 | public interface IFluentLinkEntity 494 | { 495 | /** 496 | * 497 | * Logical name of entity that the link is created from. 498 | * 499 | * Entity Logical Name 500 | */ 501 | IFluentLinkEntity FromEntity(string entityName); 502 | 503 | /** 504 | * 505 | * Logical name of attribute that the link is created from. 506 | * 507 | * Attribute Logical Name 508 | */ 509 | IFluentLinkEntity FromAttribute(string attributeName); 510 | 511 | /** 512 | * 513 | * Logical name of entity that the link is created to. 514 | * 515 | * Entity Logical Name 516 | */ 517 | IFluentLinkEntity ToEntity(string entityName); 518 | 519 | /** 520 | * 521 | * Logical name of attribute that the link is created to. 522 | * 523 | * Attribute Logical Name 524 | */ 525 | IFluentLinkEntity ToAttribute(string attributeName); 526 | 527 | /** 528 | * 529 | * Adds the given columns to the link entity. Multiple calls will just add to the existing columns. 530 | * 531 | * Params array of your columns. 532 | */ 533 | IFluentLinkEntity IncludeColumns(params string[] columns); 534 | 535 | /** 536 | * 537 | * Use this for setting further options of your link. 538 | * 539 | */ 540 | IFluentLinkEntitySetting With { get; } 541 | 542 | /** 543 | * 544 | * Adds a nested link to this link. 545 | * 546 | * Action for setting the link properties. Use a lambda for readability. 547 | */ 548 | IFluentLinkEntity Link(Action definition); 549 | 550 | /** 551 | * 552 | * Adds filter conditions to your link. 553 | * 554 | * Multiple calls to this method currently override the existing filter. 555 | * Action for setting the filter properties. Use a lambda for readability. 556 | */ 557 | IFluentLinkEntity Where(Action definition); 558 | } 559 | 560 | public interface IFluentLinkEntitySetting 561 | { 562 | /** 563 | * 564 | * Sets an alias for the results of this link entity. 565 | * 566 | * Alias to set in results. 567 | */ 568 | IFluentLinkEntity Alias(string name); 569 | 570 | /** 571 | * Join type of this link. 572 | * Join type to use. 573 | */ 574 | IFluentLinkEntity LinkType(JoinOperator joinOperator); 575 | } 576 | 577 | public class FluentLinkEntity : IFluentLinkEntity, IFluentLinkEntitySetting 578 | { 579 | private LinkEntity _linkEntity; 580 | 581 | public FluentLinkEntity() 582 | { 583 | _linkEntity = new LinkEntity 584 | { 585 | Columns = new ColumnSet() 586 | }; 587 | } 588 | 589 | public IFluentLinkEntitySetting With 590 | { 591 | get 592 | { 593 | return this; 594 | } 595 | } 596 | 597 | public IFluentLinkEntity Alias(string name) 598 | { 599 | _linkEntity.EntityAlias = name; 600 | 601 | return this; 602 | } 603 | 604 | public IFluentLinkEntity FromAttribute(string attributeName) 605 | { 606 | _linkEntity.LinkFromAttributeName = attributeName; 607 | 608 | return this; 609 | } 610 | 611 | public IFluentLinkEntity FromEntity(string entityName) 612 | { 613 | _linkEntity.LinkFromEntityName = entityName; 614 | 615 | return this; 616 | } 617 | 618 | public IFluentLinkEntity IncludeColumns(params string[] columns) 619 | { 620 | _linkEntity.Columns.AddColumns(columns); 621 | 622 | return this; 623 | } 624 | 625 | public IFluentLinkEntity Where(Action definition) 626 | { 627 | var filter = new FluentFilterExpression(); 628 | 629 | definition(filter); 630 | 631 | _linkEntity.LinkCriteria = filter.GetFilter(); 632 | 633 | return this; 634 | } 635 | 636 | public IFluentLinkEntity Link(Action definition) 637 | { 638 | var link = new FluentLinkEntity(); 639 | 640 | definition(link); 641 | 642 | _linkEntity.LinkEntities.Add(link.GetLinkEntity()); 643 | 644 | return this; 645 | } 646 | 647 | public IFluentLinkEntity LinkType(JoinOperator joinOperator) 648 | { 649 | _linkEntity.JoinOperator = joinOperator; 650 | 651 | return this; 652 | } 653 | 654 | public IFluentLinkEntity ToAttribute(string attributeName) 655 | { 656 | _linkEntity.LinkToAttributeName = attributeName; 657 | 658 | return this; 659 | } 660 | 661 | public IFluentLinkEntity ToEntity(string entityName) 662 | { 663 | _linkEntity.LinkToEntityName = entityName; 664 | 665 | return this; 666 | } 667 | 668 | internal LinkEntity GetLinkEntity() 669 | { 670 | return _linkEntity; 671 | } 672 | } 673 | 674 | public interface IFluentFilterExpression 675 | { 676 | /** 677 | * 678 | * Use this for setting further options of your filter. 679 | * 680 | */ 681 | IFluentFilterExpressionSetting With { get; } 682 | 683 | /** 684 | * 685 | * Use this for adding a condition on an attribute to your filter. 686 | * 687 | * Multiple calls to Attribute will add to the existing ones. 688 | * Action for setting the attribute properties. Use a lambda for readability. 689 | */ 690 | IFluentFilterExpression Attribute(Action definition); 691 | 692 | /** 693 | * 694 | * Adds nested filter conditions to your filter. 695 | * 696 | * Multiple calls to this method add to the existing filter conditions. 697 | * Action for setting the filter properties. Use a lambda for readability. 698 | */ 699 | IFluentFilterExpression Where(Action definition); 700 | } 701 | 702 | public interface IFluentFilterExpressionSetting 703 | { 704 | /** 705 | * 706 | * Sets the logical operator for chaining multiple conditions in this filter. 707 | * 708 | */ 709 | IFluentFilterExpression Operator(LogicalOperator filterOperator); 710 | } 711 | 712 | public class FluentFilterExpression : IFluentFilterExpression, IFluentFilterExpressionSetting 713 | { 714 | private FilterExpression _filter; 715 | 716 | public FluentFilterExpression() 717 | { 718 | _filter = new FilterExpression(); 719 | } 720 | 721 | public IFluentFilterExpressionSetting With 722 | { 723 | get 724 | { 725 | return this; 726 | } 727 | } 728 | 729 | public IFluentFilterExpression Operator(LogicalOperator filterOperator) 730 | { 731 | _filter.FilterOperator = filterOperator; 732 | 733 | return this; 734 | } 735 | 736 | public IFluentFilterExpression Where(Action definition) 737 | { 738 | var filter = new FluentFilterExpression(); 739 | 740 | definition(filter); 741 | 742 | _filter.Filters.Add(filter.GetFilter()); 743 | 744 | return this; 745 | } 746 | 747 | public IFluentFilterExpression Attribute(Action definition) 748 | { 749 | var condition = new FluentConditionExpression(); 750 | 751 | definition(condition); 752 | 753 | _filter.Conditions.Add(condition.GetCondition()); 754 | 755 | return this; 756 | } 757 | 758 | internal FilterExpression GetFilter() 759 | { 760 | return _filter; 761 | } 762 | } 763 | 764 | public interface IFluentConditionExpression 765 | { 766 | /** 767 | * 768 | * Set the entity name that your condition attribute targets, if it is not the main entity. 769 | * 770 | * Entity Logical Name 771 | */ 772 | IFluentConditionExpression Of(string entityName); 773 | 774 | /** 775 | * 776 | * Set the logical name of the attribute that your condition targets. 777 | * 778 | * Attribute logical name 779 | */ 780 | IFluentConditionExpression Named(string attributeName); 781 | 782 | /** 783 | * 784 | * Set the condition operator for your condition. 785 | * 786 | * Condition Operator Enum 787 | */ 788 | IFluentConditionExpression Is(ConditionOperator conditionOperator); 789 | 790 | /** 791 | * 792 | * Sets the value for the condition. 793 | * 794 | * Single object, use object array overload if necessary. 795 | */ 796 | IFluentConditionExpression Value(T value); 797 | 798 | /** 799 | * 800 | * Sets the values for the condition. 801 | * 802 | * Object enumeration, use object overload if necessary. 803 | */ 804 | IFluentConditionExpression Values(IEnumerable values); 805 | 806 | /** 807 | * 808 | * Alias for Value, provides better readability on Equal conditions. 809 | * Sets the value for the condition. 810 | * 811 | * Single object, use object array overload if necessary. 812 | */ 813 | IFluentConditionExpression To(T value); 814 | 815 | /** 816 | * 817 | * Alias for Value, provides better readability on Equal conditions. 818 | * Sets the values for the condition. 819 | * 820 | * Object enumeration, use object overload if necessary. 821 | */ 822 | IFluentConditionExpression ToMany(IEnumerable values); 823 | } 824 | 825 | public class FluentConditionExpression : IFluentConditionExpression 826 | { 827 | private ConditionExpression _condition; 828 | 829 | public FluentConditionExpression() 830 | { 831 | _condition = new ConditionExpression(); 832 | } 833 | 834 | public IFluentConditionExpression Is(ConditionOperator conditionOperator) 835 | { 836 | _condition.Operator = conditionOperator; 837 | 838 | return this; 839 | } 840 | 841 | public IFluentConditionExpression Named(string attributeName) 842 | { 843 | _condition.AttributeName = attributeName; 844 | 845 | return this; 846 | } 847 | 848 | public IFluentConditionExpression Of(string entityName) 849 | { 850 | _condition.EntityName = entityName; 851 | 852 | return this; 853 | } 854 | 855 | public IFluentConditionExpression To(T value) 856 | { 857 | return Value(value); 858 | } 859 | 860 | public IFluentConditionExpression ToMany(IEnumerable values) 861 | { 862 | return Values(values); 863 | } 864 | 865 | public IFluentConditionExpression Value(T value) 866 | { 867 | _condition.Values.Add(value); 868 | 869 | return this; 870 | } 871 | 872 | public IFluentConditionExpression Values(IEnumerable values) 873 | { 874 | foreach (var value in values) 875 | { 876 | _condition.Values.Add(value); 877 | } 878 | 879 | return this; 880 | } 881 | 882 | internal ConditionExpression GetCondition () 883 | { 884 | return _condition; 885 | } 886 | } 887 | 888 | public interface IFluentOrderExpression 889 | { 890 | /** 891 | * 892 | * Set the attribute name to order by. 893 | * 894 | * Attribute logical name 895 | */ 896 | IFluentOrderExpression By(string attributeName); 897 | 898 | /** 899 | * 900 | * Sets the sort order to be ascending. 901 | * 902 | */ 903 | IFluentOrderExpression Ascending(); 904 | 905 | /** 906 | * 907 | * Sets the sort order to be descending. 908 | * 909 | */ 910 | IFluentOrderExpression Descending(); 911 | } 912 | 913 | public class FluentOrderExpression : IFluentOrderExpression 914 | { 915 | private OrderExpression _order; 916 | 917 | public FluentOrderExpression () 918 | { 919 | _order = new OrderExpression(); 920 | } 921 | 922 | public IFluentOrderExpression By(string attributeName) 923 | { 924 | _order.AttributeName = attributeName; 925 | 926 | return this; 927 | } 928 | 929 | public IFluentOrderExpression Ascending() 930 | { 931 | _order.OrderType = OrderType.Ascending; 932 | 933 | return this; 934 | } 935 | 936 | public IFluentOrderExpression Descending() 937 | { 938 | _order.OrderType = OrderType.Descending; 939 | 940 | return this; 941 | } 942 | 943 | internal OrderExpression GetOrder() 944 | { 945 | return _order; 946 | } 947 | } 948 | 949 | public interface IFluentPagingInfo 950 | { 951 | /** 952 | * 953 | * Set the page number to retrieve. Is set to 1 by default. 954 | * 955 | * Number of the page, starts at 1. 956 | */ 957 | IFluentPagingInfo PageNumber(int number); 958 | 959 | /** 960 | * 961 | * Set the paging cookie for retrieving records from pages after the first. 962 | * 963 | * Use retrieve all for automatic retrieval of all records using paging. 964 | * Paging cookie retrieved during last query response. 965 | */ 966 | IFluentPagingInfo PagingCookie(string pagingCookie); 967 | 968 | /** 969 | * 970 | * Set the size of each page. 971 | * 972 | * Number of records to return per page. 973 | */ 974 | IFluentPagingInfo PageSize(int number); 975 | 976 | /** 977 | * 978 | * Specifies whether the total record count of your query results should be retrieved. 979 | * 980 | * True for returning total record count, false otherwise. 981 | */ 982 | IFluentPagingInfo ReturnTotalRecordCount(bool returnTotal = true); 983 | } 984 | 985 | public class FluentPagingInfo : IFluentPagingInfo 986 | { 987 | private PagingInfo _pagingInfo; 988 | 989 | public FluentPagingInfo() 990 | { 991 | _pagingInfo = new PagingInfo 992 | { 993 | PageNumber = 1 994 | }; 995 | } 996 | 997 | public IFluentPagingInfo PageNumber(int number) 998 | { 999 | _pagingInfo.PageNumber = number; 1000 | 1001 | return this; 1002 | } 1003 | 1004 | public IFluentPagingInfo PageSize(int number) 1005 | { 1006 | _pagingInfo.Count = number; 1007 | 1008 | return this; 1009 | } 1010 | 1011 | public IFluentPagingInfo PagingCookie(string pagingCookie) 1012 | { 1013 | _pagingInfo.PagingCookie = pagingCookie; 1014 | 1015 | return this; 1016 | } 1017 | 1018 | public IFluentPagingInfo ReturnTotalRecordCount(bool returnTotal = true) 1019 | { 1020 | _pagingInfo.ReturnTotalRecordCount = returnTotal; 1021 | 1022 | return this; 1023 | } 1024 | 1025 | public PagingInfo GetPagingInfo() 1026 | { 1027 | return _pagingInfo; 1028 | } 1029 | } 1030 | } 1031 | -------------------------------------------------------------------------------- /src/lib/Xrm.Oss.FluentQuery/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Allgemeine Informationen über eine Assembly werden über die folgenden 6 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, 7 | // die einer Assembly zugeordnet sind. 8 | [assembly: AssemblyTitle("Xrm.Oss.FluentQuery")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Xrm.Oss.FluentQuery")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly 18 | // für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von 19 | // COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. 20 | [assembly: ComVisible(false)] 21 | 22 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird 23 | [assembly: Guid("4058fa56-2332-44a5-aed4-da20526434ac")] 24 | 25 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: 26 | // 27 | // Hauptversion 28 | // Nebenversion 29 | // Buildnummer 30 | // Revision 31 | // 32 | // Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, 33 | // indem Sie "*" wie unten gezeigt eingeben: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/lib/Xrm.Oss.FluentQuery/Xrm.Oss.FluentQuery.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {4058FA56-2332-44A5-AED4-DA20526434AC} 8 | Library 9 | Properties 10 | Xrm.Oss.FluentQuery 11 | Xrm.Oss.FluentQuery 12 | v4.6.2 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | True 54 | 55 | 56 | True 57 | 58 | 59 | True 60 | 61 | 62 | True 63 | 64 | 65 | True 66 | 67 | 68 | True 69 | 70 | 71 | True 72 | 73 | 74 | True 75 | 76 | 77 | ..\..\..\packages\Microsoft.CrmSdk.CoreAssemblies\lib\net452\Microsoft.Crm.Sdk.Proxy.dll 78 | True 79 | True 80 | 81 | 82 | ..\..\..\packages\Microsoft.CrmSdk.CoreAssemblies\lib\net452\Microsoft.Xrm.Sdk.dll 83 | True 84 | True 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | ..\..\..\packages\Microsoft.IdentityModel\lib\net35\Microsoft.IdentityModel.dll 94 | True 95 | True 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/lib/Xrm.Oss.FluentQuery/paket.references: -------------------------------------------------------------------------------- 1 | Microsoft.CrmSdk.CoreAssemblies -------------------------------------------------------------------------------- /src/test/Xrm.Oss.FluentQuery.Tests/CacheTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.Caching; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using FakeItEasy; 8 | using FakeXrmEasy; 9 | using Microsoft.Xrm.Sdk; 10 | using Microsoft.Xrm.Sdk.Query; 11 | using NUnit.Framework; 12 | 13 | namespace Xrm.Oss.FluentQuery.Tests 14 | { 15 | [TestFixture] 16 | public class CacheTests 17 | { 18 | private List ExecuteQuery(MemoryCache memoryCache, IOrganizationService service) 19 | { 20 | return service.Query("account") 21 | .IncludeAllColumns() 22 | .UseCache(memoryCache, new DateTimeOffset(DateTime.UtcNow.AddHours(1))) 23 | .Retrieve(); 24 | } 25 | 26 | private List ExecuteQueryWithExplicitColumns(MemoryCache memoryCache, IOrganizationService service) 27 | { 28 | return service.Query("account") 29 | .IncludeColumns("name") 30 | .UseCache(memoryCache, new DateTimeOffset(DateTime.UtcNow.AddHours(1))) 31 | .Retrieve(); 32 | } 33 | 34 | private List ExecuteQueryAll(MemoryCache memoryCache, IOrganizationService service) 35 | { 36 | return service.Query("account") 37 | .IncludeAllColumns() 38 | .UseCache(memoryCache, new DateTimeOffset(DateTime.UtcNow.AddHours(1))) 39 | .RetrieveAll(); 40 | } 41 | 42 | private List ExecuteQueryWithExplicitColumnsAll(MemoryCache memoryCache, IOrganizationService service) 43 | { 44 | return service.Query("account") 45 | .IncludeColumns("name") 46 | .UseCache(memoryCache, new DateTimeOffset(DateTime.UtcNow.AddHours(1))) 47 | .RetrieveAll(); 48 | } 49 | 50 | [Test] 51 | public void It_Should_Use_Result_From_Cache_If_Found() 52 | { 53 | var memoryCache = new MemoryCache("test"); 54 | 55 | var context = new XrmFakedContext(); 56 | 57 | var account = new Entity 58 | { 59 | Id = Guid.NewGuid(), 60 | LogicalName = "account", 61 | Attributes = 62 | { 63 | { "name", "Adventure Works" } 64 | } 65 | }; 66 | context.Initialize(new[] { account }); 67 | 68 | var service = context.GetFakedOrganizationService(); 69 | var results = ExecuteQuery(memoryCache, service); 70 | 71 | Assert.That(results.Count, Is.EqualTo(1)); 72 | Assert.That(results[0].GetAttributeValue("name"), Is.EqualTo("Adventure Works")); 73 | 74 | results = ExecuteQuery(memoryCache, service); 75 | 76 | Assert.That(results.Count, Is.EqualTo(1)); 77 | Assert.That(results[0].GetAttributeValue("name"), Is.EqualTo("Adventure Works")); 78 | 79 | A.CallTo(() => service.RetrieveMultiple(A.Ignored)).MustHaveHappened(Repeated.Exactly.Once); 80 | } 81 | 82 | [Test] 83 | public void It_Should_Differentiate_Queries() 84 | { 85 | var memoryCache = new MemoryCache("test"); 86 | 87 | var context = new XrmFakedContext(); 88 | 89 | var account = new Entity 90 | { 91 | Id = Guid.NewGuid(), 92 | LogicalName = "account", 93 | Attributes = 94 | { 95 | { "name", "Adventure Works" } 96 | } 97 | }; 98 | context.Initialize(new[] { account }); 99 | 100 | var service = context.GetFakedOrganizationService(); 101 | var results = ExecuteQuery(memoryCache, service); 102 | 103 | Assert.That(results.Count, Is.EqualTo(1)); 104 | Assert.That(results[0].GetAttributeValue("name"), Is.EqualTo("Adventure Works")); 105 | 106 | results = ExecuteQueryWithExplicitColumns(memoryCache, service); 107 | 108 | Assert.That(results.Count, Is.EqualTo(1)); 109 | Assert.That(results[0].GetAttributeValue("name"), Is.EqualTo("Adventure Works")); 110 | 111 | A.CallTo(() => service.RetrieveMultiple(A.Ignored)).MustHaveHappened(Repeated.Exactly.Twice); 112 | } 113 | 114 | [Test] 115 | public void It_Should_Use_Result_From_Cache_If_Found_Retrieve_All() 116 | { 117 | var memoryCache = new MemoryCache("test"); 118 | 119 | var context = new XrmFakedContext(); 120 | 121 | var account = new Entity 122 | { 123 | Id = Guid.NewGuid(), 124 | LogicalName = "account", 125 | Attributes = 126 | { 127 | { "name", "Adventure Works" } 128 | } 129 | }; 130 | context.Initialize(new[] { account }); 131 | 132 | var service = context.GetFakedOrganizationService(); 133 | var results = ExecuteQueryAll(memoryCache, service); 134 | 135 | Assert.That(results.Count, Is.EqualTo(1)); 136 | Assert.That(results[0].GetAttributeValue("name"), Is.EqualTo("Adventure Works")); 137 | 138 | results = ExecuteQueryAll(memoryCache, service); 139 | 140 | Assert.That(results.Count, Is.EqualTo(1)); 141 | Assert.That(results[0].GetAttributeValue("name"), Is.EqualTo("Adventure Works")); 142 | 143 | A.CallTo(() => service.RetrieveMultiple(A.Ignored)).MustHaveHappened(Repeated.Exactly.Once); 144 | } 145 | 146 | [Test] 147 | public void It_Should_Differentiate_Queries_Retrieve_All() 148 | { 149 | var memoryCache = new MemoryCache("test"); 150 | 151 | var context = new XrmFakedContext(); 152 | 153 | var account = new Entity 154 | { 155 | Id = Guid.NewGuid(), 156 | LogicalName = "account", 157 | Attributes = 158 | { 159 | { "name", "Adventure Works" } 160 | } 161 | }; 162 | context.Initialize(new[] { account }); 163 | 164 | var service = context.GetFakedOrganizationService(); 165 | var results = ExecuteQueryAll(memoryCache, service); 166 | 167 | Assert.That(results.Count, Is.EqualTo(1)); 168 | Assert.That(results[0].GetAttributeValue("name"), Is.EqualTo("Adventure Works")); 169 | 170 | results = ExecuteQueryWithExplicitColumnsAll(memoryCache, service); 171 | 172 | Assert.That(results.Count, Is.EqualTo(1)); 173 | Assert.That(results[0].GetAttributeValue("name"), Is.EqualTo("Adventure Works")); 174 | 175 | A.CallTo(() => service.RetrieveMultiple(A.Ignored)).MustHaveHappened(Repeated.Exactly.Twice); 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/test/Xrm.Oss.FluentQuery.Tests/FluentFilterExpressionTests.cs: -------------------------------------------------------------------------------- 1 | using FakeXrmEasy; 2 | using Microsoft.Xrm.Sdk.Query; 3 | using NUnit.Framework; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Xrm.Oss.FluentQuery.Tests 11 | { 12 | [TestFixture] 13 | public class FluentFilterExpressionTests 14 | { 15 | [Test] 16 | public void It_Should_Properly_Set_Simple_Filter() 17 | { 18 | var context = new XrmFakedContext(); 19 | var service = context.GetFakedOrganizationService(); 20 | 21 | var query = service.Query("account") 22 | .Where(e => e 23 | .Attribute(a => a 24 | .Of("contact") 25 | .Named("name") 26 | .Is(ConditionOperator.Equal) 27 | .Value("Test") 28 | ) 29 | .With.Operator(LogicalOperator.And) 30 | ) 31 | .Expression; 32 | 33 | Assert.That(query.Criteria.FilterOperator, Is.EqualTo(LogicalOperator.And)); 34 | Assert.That(query.Criteria.Conditions[0].EntityName, Is.EqualTo("contact")); 35 | Assert.That(query.Criteria.Conditions[0].AttributeName, Is.EqualTo("name")); 36 | Assert.That(query.Criteria.Conditions[0].Operator, Is.EqualTo(ConditionOperator.Equal)); 37 | Assert.That(query.Criteria.Conditions[0].Values, Is.EqualTo(new[] { "Test" })); 38 | } 39 | 40 | [Test] 41 | public void It_Should_Properly_Set_Value_Arrays() 42 | { 43 | var context = new XrmFakedContext(); 44 | var service = context.GetFakedOrganizationService(); 45 | 46 | var values = new[] { "name1", "name2" }; 47 | 48 | var query = service.Query("account") 49 | .Where(e => e 50 | .Attribute(a => a 51 | .Of("contact") 52 | .Named("name") 53 | .Is(ConditionOperator.In) 54 | .Values(values) 55 | ) 56 | .Attribute(a => a 57 | .Of("contact") 58 | .Named("name") 59 | .Is(ConditionOperator.In) 60 | .ToMany(values) 61 | ) 62 | .With.Operator(LogicalOperator.And) 63 | ) 64 | .Expression; 65 | 66 | Assert.That(query.Criteria.FilterOperator, Is.EqualTo(LogicalOperator.And)); 67 | 68 | Assert.That(query.Criteria.Conditions[0].EntityName, Is.EqualTo("contact")); 69 | Assert.That(query.Criteria.Conditions[0].AttributeName, Is.EqualTo("name")); 70 | Assert.That(query.Criteria.Conditions[0].Operator, Is.EqualTo(ConditionOperator.In)); 71 | Assert.That(query.Criteria.Conditions[0].Values, Is.EqualTo(values)); 72 | 73 | Assert.That(query.Criteria.Conditions[1].EntityName, Is.EqualTo("contact")); 74 | Assert.That(query.Criteria.Conditions[1].AttributeName, Is.EqualTo("name")); 75 | Assert.That(query.Criteria.Conditions[1].Operator, Is.EqualTo(ConditionOperator.In)); 76 | Assert.That(query.Criteria.Conditions[1].Values, Is.EqualTo(values)); 77 | } 78 | 79 | [Test] 80 | public void It_Should_Properly_Set_Nested_Filter() 81 | { 82 | var context = new XrmFakedContext(); 83 | var service = context.GetFakedOrganizationService(); 84 | 85 | var query = service.Query("account") 86 | .Where(e => e 87 | .Attribute(a => a 88 | .Of("contact") 89 | .Named("name") 90 | .Is(ConditionOperator.Equal) 91 | .Value("Test") 92 | ) 93 | .With.Operator(LogicalOperator.And) 94 | .Where(e2 => e2 95 | .Attribute(a => a 96 | .Of("contact2") 97 | .Named("name2") 98 | .Is(ConditionOperator.NotEqual) 99 | .Value("Test2") 100 | ) 101 | .With.Operator(LogicalOperator.Or) 102 | ) 103 | ) 104 | .Expression; 105 | 106 | Assert.That(query.Criteria.FilterOperator, Is.EqualTo(LogicalOperator.And)); 107 | 108 | Assert.That(query.Criteria.Conditions[0].EntityName, Is.EqualTo("contact")); 109 | Assert.That(query.Criteria.Conditions[0].AttributeName, Is.EqualTo("name")); 110 | Assert.That(query.Criteria.Conditions[0].Operator, Is.EqualTo(ConditionOperator.Equal)); 111 | Assert.That(query.Criteria.Conditions[0].Values, Is.EqualTo(new[] { "Test" })); 112 | 113 | var nestedFilter = query.Criteria.Filters[0]; 114 | Assert.That(nestedFilter.FilterOperator, Is.EqualTo(LogicalOperator.Or)); 115 | 116 | Assert.That(nestedFilter.Conditions[0].EntityName, Is.EqualTo("contact2")); 117 | Assert.That(nestedFilter.Conditions[0].AttributeName, Is.EqualTo("name2")); 118 | Assert.That(nestedFilter.Conditions[0].Operator, Is.EqualTo(ConditionOperator.NotEqual)); 119 | Assert.That(nestedFilter.Conditions[0].Values, Is.EqualTo(new[] { "Test2" })); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/test/Xrm.Oss.FluentQuery.Tests/FluentLinkEntityTests.cs: -------------------------------------------------------------------------------- 1 | using FakeXrmEasy; 2 | using Microsoft.Xrm.Sdk.Query; 3 | using NUnit.Framework; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Xrm.Oss.FluentQuery.Tests 11 | { 12 | [TestFixture] 13 | public class FluentLinkEntityTests 14 | { 15 | [Test] 16 | public void It_Should_Add_Link_Entity() 17 | { 18 | var context = new XrmFakedContext(); 19 | var service = context.GetFakedOrganizationService(); 20 | 21 | var entityAlias = "accountContacts"; 22 | var fromEntity = "account"; 23 | var fromAttribute = "accountid"; 24 | var toEntity = "contact"; 25 | var toAttribute = "contactid"; 26 | 27 | var query = service.Query("account") 28 | .Link(l => l 29 | .With.Alias(entityAlias) 30 | .FromEntity(fromEntity) 31 | .FromAttribute(fromAttribute) 32 | .ToEntity(toEntity) 33 | .ToAttribute(toAttribute) 34 | ) 35 | .Expression; 36 | 37 | Assert.That(query.LinkEntities.Count, Is.EqualTo(1)); 38 | 39 | Assert.That(query.LinkEntities.Single().EntityAlias, Is.EqualTo(entityAlias)); 40 | Assert.That(query.LinkEntities.Single().LinkFromEntityName, Is.EqualTo(fromEntity)); 41 | Assert.That(query.LinkEntities.Single().LinkFromAttributeName, Is.EqualTo(fromAttribute)); 42 | Assert.That(query.LinkEntities.Single().LinkToEntityName, Is.EqualTo(toEntity)); 43 | Assert.That(query.LinkEntities.Single().LinkToAttributeName, Is.EqualTo(toAttribute)); 44 | } 45 | 46 | [Test] 47 | public void It_Should_Add_Nested_Link_Entity() 48 | { 49 | var context = new XrmFakedContext(); 50 | var service = context.GetFakedOrganizationService(); 51 | 52 | var entityAlias = "accountContacts"; 53 | var fromEntity = "account"; 54 | var fromAttribute = "accountid"; 55 | var toEntity = "contact"; 56 | var toAttribute = "contactid"; 57 | 58 | var entityAlias2 = "accountContacts2"; 59 | var fromEntity2 = "account2"; 60 | var fromAttribute2 = "accountid2"; 61 | var toEntity2 = "contact2"; 62 | var toAttribute2 = "contactid2"; 63 | 64 | var query = service.Query("account") 65 | .Link(l => l 66 | .With.Alias(entityAlias) 67 | .FromEntity(fromEntity) 68 | .FromAttribute(fromAttribute) 69 | .ToEntity(toEntity) 70 | .ToAttribute(toAttribute) 71 | .Link(l2 => l2 72 | .With.Alias(entityAlias2) 73 | .FromEntity(fromEntity2) 74 | .FromAttribute(fromAttribute2) 75 | .ToEntity(toEntity2) 76 | .ToAttribute(toAttribute2) 77 | ) 78 | ) 79 | .Expression; 80 | 81 | Assert.That(query.LinkEntities.Count, Is.EqualTo(1)); 82 | 83 | Assert.That(query.LinkEntities.Single().EntityAlias, Is.EqualTo(entityAlias)); 84 | Assert.That(query.LinkEntities.Single().LinkFromEntityName, Is.EqualTo(fromEntity)); 85 | Assert.That(query.LinkEntities.Single().LinkFromAttributeName, Is.EqualTo(fromAttribute)); 86 | Assert.That(query.LinkEntities.Single().LinkToEntityName, Is.EqualTo(toEntity)); 87 | Assert.That(query.LinkEntities.Single().LinkToAttributeName, Is.EqualTo(toAttribute)); 88 | 89 | var innerLink = query.LinkEntities.Single().LinkEntities.Single(); 90 | 91 | Assert.That(innerLink.EntityAlias, Is.EqualTo(entityAlias2)); 92 | Assert.That(innerLink.LinkFromEntityName, Is.EqualTo(fromEntity2)); 93 | Assert.That(innerLink.LinkFromAttributeName, Is.EqualTo(fromAttribute2)); 94 | Assert.That(innerLink.LinkToEntityName, Is.EqualTo(toEntity2)); 95 | Assert.That(innerLink.LinkToAttributeName, Is.EqualTo(toAttribute2)); 96 | } 97 | 98 | [Test] 99 | public void It_Should_Set_Link_Type() 100 | { 101 | var context = new XrmFakedContext(); 102 | var service = context.GetFakedOrganizationService(); 103 | 104 | var query = service.Query("account") 105 | .Link(l => l 106 | .With.LinkType(JoinOperator.Inner) 107 | ) 108 | .Expression; 109 | 110 | Assert.That(query.LinkEntities.Count, Is.EqualTo(1)); 111 | 112 | Assert.That(query.LinkEntities.Single().JoinOperator, Is.EqualTo(JoinOperator.Inner)); 113 | } 114 | 115 | [Test] 116 | public void It_Should_Set_Columns() 117 | { 118 | var context = new XrmFakedContext(); 119 | var service = context.GetFakedOrganizationService(); 120 | 121 | var query = service.Query("account") 122 | .Link(l => l 123 | .IncludeColumns("name", "address1_line1") 124 | ) 125 | .Expression; 126 | 127 | Assert.That(query.LinkEntities.Count, Is.EqualTo(1)); 128 | 129 | Assert.That(query.LinkEntities.Single().Columns.Columns.ToArray, Is.EquivalentTo(new[] { "name", "address1_line1" })); 130 | } 131 | 132 | [Test] 133 | public void It_Should_Set_Filter() 134 | { 135 | var context = new XrmFakedContext(); 136 | var service = context.GetFakedOrganizationService(); 137 | 138 | var entityAlias = "accountContacts"; 139 | var fromEntity = "account"; 140 | var fromAttribute = "accountid"; 141 | var toEntity = "contact"; 142 | var toAttribute = "contactid"; 143 | 144 | var query = service.Query("account") 145 | .Link(l => l 146 | .With.Alias(entityAlias) 147 | .FromEntity(fromEntity) 148 | .FromAttribute(fromAttribute) 149 | .ToEntity(toEntity) 150 | .ToAttribute(toAttribute) 151 | .Where(e => e 152 | .Attribute(a => a 153 | .Named("emailaddress1") 154 | .Is(ConditionOperator.NotNull) 155 | ) 156 | ) 157 | ) 158 | .Expression; 159 | 160 | Assert.That(query.LinkEntities.Count, Is.EqualTo(1)); 161 | 162 | Assert.That(query.LinkEntities.Single().LinkCriteria.Conditions.Count, Is.EqualTo(1)); 163 | 164 | var link = query.LinkEntities.Single(); 165 | Assert.That(link.LinkCriteria.Conditions[0].AttributeName, Is.EqualTo("emailaddress1")); 166 | Assert.That(link.LinkCriteria.Conditions[0].Operator, Is.EqualTo(ConditionOperator.NotNull)); 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/test/Xrm.Oss.FluentQuery.Tests/FluentOrderExpressionTests.cs: -------------------------------------------------------------------------------- 1 | using FakeXrmEasy; 2 | using Microsoft.Xrm.Sdk.Query; 3 | using NUnit.Framework; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Xrm.Oss.FluentQuery.Tests 11 | { 12 | [TestFixture] 13 | public class FluentOrderExpressionTests 14 | { 15 | [Test] 16 | public void It_Should_Add_Simple_Order_Expression() 17 | { 18 | var context = new XrmFakedContext(); 19 | var service = context.GetFakedOrganizationService(); 20 | 21 | var query = service.Query("account") 22 | .Order(o => o 23 | .By("name") 24 | ) 25 | .Expression; 26 | 27 | Assert.That(query.Orders.Count, Is.EqualTo(1)); 28 | 29 | Assert.That(query.Orders.Single().AttributeName, Is.EqualTo("name")); 30 | } 31 | 32 | [Test] 33 | public void It_Should_Add_Multiple_Order_Expressions() 34 | { 35 | var context = new XrmFakedContext(); 36 | var service = context.GetFakedOrganizationService(); 37 | 38 | var query = service.Query("account") 39 | .Order(o => o 40 | .By("name") 41 | ) 42 | .Order(o => o 43 | .By("address1_line1") 44 | ) 45 | .Expression; 46 | 47 | Assert.That(query.Orders.Count, Is.EqualTo(2)); 48 | 49 | Assert.That(query.Orders[0].AttributeName, Is.EqualTo("name")); 50 | Assert.That(query.Orders[1].AttributeName, Is.EqualTo("address1_line1")); 51 | } 52 | 53 | [Test] 54 | public void It_Should_Set_Direction() 55 | { 56 | var context = new XrmFakedContext(); 57 | var service = context.GetFakedOrganizationService(); 58 | 59 | var query = service.Query("account") 60 | .Order(o => o 61 | .By("name") 62 | .Ascending() 63 | ) 64 | .Order(o => o 65 | .By("address1_line1") 66 | .Descending() 67 | ) 68 | .Expression; 69 | 70 | Assert.That(query.Orders.Count, Is.EqualTo(2)); 71 | 72 | Assert.That(query.Orders[0].AttributeName, Is.EqualTo("name")); 73 | Assert.That(query.Orders[0].OrderType, Is.EqualTo(OrderType.Ascending)); 74 | 75 | Assert.That(query.Orders[1].AttributeName, Is.EqualTo("address1_line1")); 76 | Assert.That(query.Orders[1].OrderType, Is.EqualTo(OrderType.Descending)); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/Xrm.Oss.FluentQuery.Tests/FluentPagingInfoTests.cs: -------------------------------------------------------------------------------- 1 | using FakeXrmEasy; 2 | using NUnit.Framework; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Xrm.Oss.FluentQuery.Tests 10 | { 11 | [TestFixture] 12 | public class FluentPagingInfoTests 13 | { 14 | [Test] 15 | public void It_Should_Apply_Paging_Info() 16 | { 17 | var context = new XrmFakedContext(); 18 | var service = context.GetFakedOrganizationService(); 19 | 20 | var query = service.Query("account") 21 | .With.PagingInfo(p => p 22 | .PageNumber(2) 23 | .PageSize(100) 24 | .PagingCookie("asdf") 25 | .ReturnTotalRecordCount() 26 | ) 27 | .Expression; 28 | 29 | Assert.That(query.PageInfo, Is.Not.Null); 30 | 31 | Assert.That(query.PageInfo.PageNumber, Is.EqualTo(2)); 32 | Assert.That(query.PageInfo.Count, Is.EqualTo(100)); 33 | Assert.That(query.PageInfo.PagingCookie, Is.EqualTo("asdf")); 34 | Assert.That(query.PageInfo.ReturnTotalRecordCount, Is.True); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/test/Xrm.Oss.FluentQuery.Tests/FluentQueryTests.cs: -------------------------------------------------------------------------------- 1 | using FakeItEasy; 2 | using FakeXrmEasy; 3 | using Microsoft.Xrm.Sdk; 4 | using Microsoft.Xrm.Sdk.Query; 5 | using NUnit.Framework; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace Xrm.Oss.FluentQuery.Tests 13 | { 14 | [TestFixture] 15 | public class FluentQueryTests 16 | { 17 | private class Account : Entity 18 | { 19 | public Account() : base("account") 20 | { 21 | } 22 | } 23 | 24 | [Test] 25 | public void It_Should_Set_Entity_Name() 26 | { 27 | var context = new XrmFakedContext(); 28 | var service = context.GetFakedOrganizationService(); 29 | 30 | var query = service.Query("account") 31 | .IncludeColumns("name", "address1_line1") 32 | .Expression; 33 | 34 | Assert.That(query.EntityName, Is.EqualTo("account")); 35 | } 36 | 37 | [Test] 38 | public void It_Should_Set_Entity_Name_On_Generic_With_Parameter() 39 | { 40 | var context = new XrmFakedContext(); 41 | var service = context.GetFakedOrganizationService(); 42 | 43 | var query = service.Query("account") 44 | .IncludeColumns("name", "address1_line1") 45 | .Expression; 46 | 47 | Assert.That(query.EntityName, Is.EqualTo("account")); 48 | } 49 | 50 | [Test] 51 | public void It_Should_Set_Entity_Name_On_Early_Binding_Automatically() 52 | { 53 | var context = new XrmFakedContext(); 54 | var service = context.GetFakedOrganizationService(); 55 | 56 | var query = service.Query() 57 | .IncludeColumns("name", "address1_line1") 58 | .Expression; 59 | 60 | Assert.That(query.EntityName, Is.EqualTo("account")); 61 | } 62 | 63 | [Test] 64 | public void It_Should_Execute_Simple_Query() 65 | { 66 | var context = new XrmFakedContext(); 67 | 68 | var testName = "Adventure Works"; 69 | var testAddress = "Somewhere over the rainbow"; 70 | 71 | var account = new Entity 72 | { 73 | Id = Guid.NewGuid(), 74 | LogicalName = "account", 75 | Attributes = 76 | { 77 | { "name", testName }, 78 | { "address1_line1", testAddress } 79 | } 80 | }; 81 | context.Initialize(new[] { account }); 82 | 83 | var service = context.GetFakedOrganizationService(); 84 | 85 | var records = service.Query("account") 86 | .IncludeColumns("name", "address1_line1") 87 | .Retrieve(); 88 | 89 | Assert.That(records.Count, Is.EqualTo(1)); 90 | 91 | var record = records.Single(); 92 | Assert.That(record.GetAttributeValue("name"), Is.EqualTo(testName)); 93 | Assert.That(record.GetAttributeValue("address1_line1"), Is.EqualTo(testAddress)); 94 | } 95 | 96 | [Test] 97 | public void It_Should_Add_All_Columns() 98 | { 99 | var context = new XrmFakedContext(); 100 | 101 | var testName = "Adventure Works"; 102 | var testAddress = "Somewhere over the rainbow"; 103 | 104 | var account = new Entity 105 | { 106 | Id = Guid.NewGuid(), 107 | LogicalName = "account", 108 | Attributes = 109 | { 110 | { "name", testName }, 111 | { "address1_line1", testAddress } 112 | } 113 | }; 114 | context.Initialize(new[] { account }); 115 | 116 | var service = context.GetFakedOrganizationService(); 117 | 118 | var records = service.Query("account") 119 | .IncludeAllColumns() 120 | .Retrieve(); 121 | 122 | Assert.That(records.Count, Is.EqualTo(1)); 123 | 124 | var record = records.Single(); 125 | Assert.That(record.GetAttributeValue("name"), Is.EqualTo(testName)); 126 | Assert.That(record.GetAttributeValue("address1_line1"), Is.EqualTo(testAddress)); 127 | } 128 | 129 | [Test] 130 | public void It_Should_Retrieve_All() 131 | { 132 | var context = new XrmFakedContext(); 133 | 134 | var testName = "Adventure Works"; 135 | var testAddress = "Somewhere over the rainbow"; 136 | 137 | var account = new Entity 138 | { 139 | Id = Guid.NewGuid(), 140 | LogicalName = "account", 141 | Attributes = 142 | { 143 | { "name", testName }, 144 | { "address1_line1", testAddress } 145 | } 146 | }; 147 | 148 | var account2 = new Entity 149 | { 150 | Id = Guid.NewGuid(), 151 | LogicalName = "account", 152 | Attributes = 153 | { 154 | { "name", testName }, 155 | { "address1_line1", testAddress } 156 | } 157 | }; 158 | 159 | context.Initialize(new[] { account, account2 }); 160 | 161 | var service = context.GetFakedOrganizationService(); 162 | 163 | var records = service.Query("account") 164 | .IncludeColumns("name", "address1_line1") 165 | .With.PagingInfo(p => p 166 | .PageNumber(1) 167 | .PageSize(1) 168 | ) 169 | .RetrieveAll(); 170 | 171 | Assert.That(records.Count, Is.EqualTo(2)); 172 | 173 | Assert.That(records.Any(r => r.Id == account.Id), Is.True); 174 | Assert.That(records.Any(r => r.Id == account2.Id), Is.True); 175 | 176 | // FakeXrmEasy Bug? 177 | // A.CallTo(() => service.RetrieveMultiple(A._)).MustHaveHappened(Repeated.Exactly.Twice); 178 | } 179 | 180 | [Test] 181 | public void It_Should_Include_Columns() 182 | { 183 | var context = new XrmFakedContext(); 184 | var service = context.GetFakedOrganizationService(); 185 | var columns = new[] { "name", "address1_line1" }; 186 | 187 | var query = service.Query("account") 188 | .IncludeColumns(columns) 189 | .Expression; 190 | 191 | Assert.That(query.ColumnSet.Columns.ToArray(), Is.EquivalentTo(columns)); 192 | } 193 | 194 | [Test] 195 | public void It_Should_Concat_Columns() 196 | { 197 | var context = new XrmFakedContext(); 198 | var service = context.GetFakedOrganizationService(); 199 | var columns = new[] { "name", "address1_line1" }; 200 | 201 | var query = service.Query("account") 202 | .IncludeColumns("name") 203 | .IncludeColumns("address1_line1") 204 | .Expression; 205 | 206 | Assert.That(query.ColumnSet.Columns.ToArray(), Is.EquivalentTo(columns)); 207 | } 208 | 209 | [Test] 210 | public void It_Should_Set_Record_Count() 211 | { 212 | var context = new XrmFakedContext(); 213 | var service = context.GetFakedOrganizationService(); 214 | 215 | var query = service.Query("account") 216 | .With.RecordCount(10) 217 | .Expression; 218 | 219 | Assert.That(query.TopCount, Is.EqualTo(10)); 220 | } 221 | 222 | [Test] 223 | public void It_Should_Set_Database_Lock() 224 | { 225 | var context = new XrmFakedContext(); 226 | var service = context.GetFakedOrganizationService(); 227 | 228 | var query = service.Query("account") 229 | .With.DatabaseLock() 230 | .Expression; 231 | 232 | Assert.That(query.NoLock, Is.EqualTo(false)); 233 | } 234 | 235 | [Test] 236 | public void It_Should_Set_Database_Unique_Records() 237 | { 238 | var context = new XrmFakedContext(); 239 | var service = context.GetFakedOrganizationService(); 240 | 241 | var query = service.Query("account") 242 | .With.UniqueRecords() 243 | .Expression; 244 | 245 | Assert.That(query.Distinct, Is.EqualTo(true)); 246 | } 247 | 248 | [Test] 249 | public void It_Should_Set_Total_Record_Count() 250 | { 251 | var context = new XrmFakedContext(); 252 | var service = context.GetFakedOrganizationService(); 253 | 254 | var query = service.Query("account") 255 | .With.TotalRecordCount() 256 | .Expression; 257 | 258 | Assert.That(query.PageInfo.ReturnTotalRecordCount, Is.EqualTo(true)); 259 | } 260 | 261 | [Test] 262 | public void It_Should_Set_Paging_Info() 263 | { 264 | var context = new XrmFakedContext(); 265 | var service = context.GetFakedOrganizationService(); 266 | 267 | var query = service.Query("account") 268 | .With.PagingInfo(p => p 269 | .PageNumber(1) 270 | ) 271 | .Expression; 272 | 273 | Assert.That(query.PageInfo.PageNumber, Is.EqualTo(1)); 274 | } 275 | 276 | [Test] 277 | public void It_Should_Properly_Execute_Query() 278 | { 279 | var context = new XrmFakedContext(); 280 | var service = context.GetFakedOrganizationService(); 281 | 282 | var account = new Entity 283 | { 284 | Id = Guid.NewGuid(), 285 | LogicalName = "account", 286 | Attributes = 287 | { 288 | { "name", "Adventure Works" } 289 | } 290 | }; 291 | 292 | var account2 = new Entity 293 | { 294 | Id = Guid.NewGuid(), 295 | LogicalName = "account", 296 | Attributes = 297 | { 298 | { "name", "Contoso" }, 299 | } 300 | }; 301 | 302 | context.Initialize(new[] { account, account2 }); 303 | 304 | var result = service.Query("account") 305 | .IncludeColumns("name") 306 | .Where(e => e 307 | .Attribute(a => a 308 | .Named("name") 309 | .Is(ConditionOperator.Equal) 310 | .To("Adventure Works") 311 | ) 312 | ) 313 | .Link(l => l 314 | .FromEntity("account") 315 | .FromAttribute("primarycontactid") 316 | .ToEntity("contact") 317 | .ToAttribute("contactid") 318 | .With.LinkType(JoinOperator.LeftOuter) 319 | ) 320 | .Retrieve(); 321 | 322 | Assert.That(result.Count, Is.EqualTo(1)); 323 | Assert.That(result[0].Id, Is.EqualTo(account.Id)); 324 | Assert.That(result[0].GetAttributeValue("name"), Is.EqualTo("Adventure Works")); 325 | } 326 | 327 | [Test] 328 | public void It_Should_Add_Condition_Post_Creation() 329 | { 330 | var context = new XrmFakedContext(); 331 | var service = context.GetFakedOrganizationService(); 332 | 333 | var query = service.Query("account"); 334 | 335 | query.AddCondition(c => c 336 | .Named("emailaddress1") 337 | .Is(ConditionOperator.NotNull) 338 | ); 339 | 340 | var expression = query.Expression; 341 | 342 | Assert.That(expression.Criteria.Conditions[0].AttributeName, Is.EqualTo("emailaddress1")); 343 | Assert.That(expression.Criteria.Conditions[0].Operator, Is.EqualTo(ConditionOperator.NotNull)); 344 | } 345 | 346 | [Test] 347 | public void It_Should_Add_Filter_Post_Creation() 348 | { 349 | var context = new XrmFakedContext(); 350 | var service = context.GetFakedOrganizationService(); 351 | 352 | var query = service.Query("account") 353 | .Where(e => e 354 | .Attribute(a => a 355 | .Of("contact") 356 | .Named("name") 357 | .Is(ConditionOperator.Equal) 358 | .Value("Test") 359 | ) 360 | .With.Operator(LogicalOperator.And) 361 | ); 362 | 363 | query.AddFilter(f => f 364 | .Attribute(a => a 365 | .Named("emailaddress1") 366 | .Is(ConditionOperator.NotNull) 367 | ) 368 | ); 369 | 370 | var expression = query.Expression; 371 | 372 | Assert.That(expression.Criteria.FilterOperator, Is.EqualTo(LogicalOperator.And)); 373 | Assert.That(expression.Criteria.Conditions[0].EntityName, Is.EqualTo("contact")); 374 | Assert.That(expression.Criteria.Conditions[0].AttributeName, Is.EqualTo("name")); 375 | Assert.That(expression.Criteria.Conditions[0].Operator, Is.EqualTo(ConditionOperator.Equal)); 376 | Assert.That(expression.Criteria.Conditions[0].Values, Is.EqualTo(new[] { "Test" })); 377 | 378 | Assert.That(expression.Criteria.Filters[0].Conditions[0].AttributeName, Is.EqualTo("emailaddress1")); 379 | Assert.That(expression.Criteria.Filters[0].Conditions[0].Operator, Is.EqualTo(ConditionOperator.NotNull)); 380 | } 381 | 382 | [Test] 383 | public void It_Should_Add_Top_Level_Filter_Post_Creation() 384 | { 385 | var context = new XrmFakedContext(); 386 | var service = context.GetFakedOrganizationService(); 387 | 388 | var query = service.Query("account") 389 | .Where(e => e 390 | .Attribute(a => a 391 | .Of("contact") 392 | .Named("name") 393 | .Is(ConditionOperator.Equal) 394 | .Value("Test") 395 | ) 396 | .With.Operator(LogicalOperator.And) 397 | ); 398 | 399 | query.AddFilter(f => f 400 | .Attribute(a => a 401 | .Named("emailaddress1") 402 | .Is(ConditionOperator.NotNull) 403 | ) 404 | ); 405 | 406 | var expression = query.Expression; 407 | 408 | Assert.That(expression.Criteria.FilterOperator, Is.EqualTo(LogicalOperator.And)); 409 | Assert.That(expression.Criteria.Conditions[0].EntityName, Is.EqualTo("contact")); 410 | Assert.That(expression.Criteria.Conditions[0].AttributeName, Is.EqualTo("name")); 411 | Assert.That(expression.Criteria.Conditions[0].Operator, Is.EqualTo(ConditionOperator.Equal)); 412 | Assert.That(expression.Criteria.Conditions[0].Values, Is.EqualTo(new[] { "Test" })); 413 | 414 | Assert.That(expression.Criteria.Filters[0].Conditions[0].AttributeName, Is.EqualTo("emailaddress1")); 415 | Assert.That(expression.Criteria.Filters[0].Conditions[0].Operator, Is.EqualTo(ConditionOperator.NotNull)); 416 | } 417 | } 418 | } 419 | -------------------------------------------------------------------------------- /src/test/Xrm.Oss.FluentQuery.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Allgemeine Informationen über eine Assembly werden über die folgenden 6 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, 7 | // die einer Assembly zugeordnet sind. 8 | [assembly: AssemblyTitle("Xrm.Oss.FluentQuery.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Xrm.Oss.FluentQuery.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly 18 | // für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von 19 | // COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. 20 | [assembly: ComVisible(false)] 21 | 22 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird 23 | [assembly: Guid("332626a0-8ad8-410b-aa4e-13d9db9a2860")] 24 | 25 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: 26 | // 27 | // Hauptversion 28 | // Nebenversion 29 | // Buildnummer 30 | // Revision 31 | // 32 | // Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, 33 | // indem Sie "*" wie unten gezeigt eingeben: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/test/Xrm.Oss.FluentQuery.Tests/paket.references: -------------------------------------------------------------------------------- 1 | FakeXrmEasy.365 2 | NUnit 3 | NUnit3TestAdapter --------------------------------------------------------------------------------