master 1de038417ba2 cached
107 files
396.0 KB
86.6k tokens
398 symbols
1 requests
Download .txt
Showing preview only (430K chars total). Download the full file or copy to clipboard to get everything.
Repository: VahidN/EFSecondLevelCache.Core
Branch: master
Commit: 1de038417ba2
Files: 107
Total size: 396.0 KB

Directory structure:
gitextract_jukctmgq/

├── .gitattributes
├── .github/
│   ├── issue_template.md
│   ├── lock.yml
│   └── workflows/
│       └── build.yml
├── .gitignore
├── .vscode/
│   ├── launch.json
│   ├── settings.json
│   └── tasks.json
├── EFSecondLevelCache.Core.sln
├── LICENSE.md
├── README.md
├── global.json
├── src/
│   ├── EFSecondLevelCache.Core/
│   │   ├── Contracts/
│   │   │   ├── EFCacheDebugInfo.cs
│   │   │   ├── EFCacheKey.cs
│   │   │   ├── EFQueryDebugView.cs
│   │   │   ├── IEFCacheKeyHashProvider.cs
│   │   │   ├── IEFCacheKeyProvider.cs
│   │   │   └── IEFCacheServiceProvider.cs
│   │   ├── EFAsyncEnumerable.cs
│   │   ├── EFAsyncEnumerator.cs
│   │   ├── EFAsyncTaskEnumerable.cs
│   │   ├── EFAsyncTaskEnumerator.cs
│   │   ├── EFCacheKeyHashProvider.cs
│   │   ├── EFCacheKeyProvider3x.cs
│   │   ├── EFCachePolicy.cs
│   │   ├── EFCacheServiceProvider.cs
│   │   ├── EFCachedDbSet.cs
│   │   ├── EFCachedDbSetExtensions.cs
│   │   ├── EFCachedQueryExtensions.cs
│   │   ├── EFCachedQueryProvider.cs
│   │   ├── EFCachedQueryable.cs
│   │   ├── EFChangeTrackerExtensions.cs
│   │   ├── EFMaterializer.cs
│   │   ├── EFQueryExpressionVisitor.cs
│   │   ├── EFSecondLevelCache.Core.csproj
│   │   ├── EFServiceCollectionExtensions.cs
│   │   ├── EFStaticServiceProvider.cs
│   │   ├── ParallelExtensions.cs
│   │   ├── Properties/
│   │   │   └── AssemblyInfo.cs
│   │   ├── XxHashUnsafe.cs
│   │   ├── _0-restore.bat
│   │   └── _1-dotnet_pack.bat
│   └── Tests/
│       ├── EFSecondLevelCache.Core.AspNetCoreSample/
│       │   ├── App_Data/
│       │   │   └── .gitkeep.txt
│       │   ├── Controllers/
│       │   │   └── HomeController.cs
│       │   ├── DataLayer/
│       │   │   ├── Entities/
│       │   │   │   ├── Post.cs
│       │   │   │   ├── Product.cs
│       │   │   │   ├── Tag.cs
│       │   │   │   ├── TagProduct.cs
│       │   │   │   └── User.cs
│       │   │   ├── SampleContext.cs
│       │   │   └── Utils/
│       │   │       ├── ApplicationDbContextSeedData.cs
│       │   │       └── DBInitialization.cs
│       │   ├── EFSecondLevelCache.Core.AspNetCoreSample.csproj
│       │   ├── Migrations/
│       │   │   ├── 20191022095356_V2019_10_22_1323.Designer.cs
│       │   │   ├── 20191022095356_V2019_10_22_1323.cs
│       │   │   └── SampleContextModelSnapshot.cs
│       │   ├── Models/
│       │   │   └── PostDto.cs
│       │   ├── Others/
│       │   │   └── TestUtils.cs
│       │   ├── Profiles/
│       │   │   └── PostProfile.cs
│       │   ├── Program.cs
│       │   ├── Properties/
│       │   │   └── launchSettings.json
│       │   ├── Startup.cs
│       │   ├── _0-restore.bat
│       │   ├── _1-dotnet_run.bat
│       │   ├── _update_db.bat
│       │   ├── appsettings.json
│       │   ├── web.config
│       │   └── wwwroot/
│       │       └── App_Data/
│       │           └── .gitkeep
│       ├── EFSecondLevelCache.Core.NET46Sample/
│       │   └── EFSecondLevelCache.Core.NET46Sample/
│       │       ├── App.config
│       │       ├── DataLayer/
│       │       │   ├── ConfigureServices.cs
│       │       │   ├── Entities/
│       │       │   │   └── Post.cs
│       │       │   └── SampleContext.cs
│       │       ├── EFSecondLevelCache.Core.NET46Sample.csproj
│       │       ├── Program.cs
│       │       ├── Properties/
│       │       │   └── AssemblyInfo.cs
│       │       └── packages.config
│       ├── EFSecondLevelCache.Core.PerformanceTests/
│       │   ├── BenchmarkTests.cs
│       │   ├── EFSecondLevelCache.Core.PerformanceTests.csproj
│       │   ├── Program.cs
│       │   ├── SampleContext.cs
│       │   ├── TestsServiceProvider.cs
│       │   ├── _0-restore.bat
│       │   ├── _1-dotnet_run.bat
│       │   └── app_data/
│       │       └── .gitkeep
│       ├── EFSecondLevelCache.Core.Tests/
│       │   ├── EFCacheServiceProviderTests.cs
│       │   ├── EFCachedQueryProviderAsyncTests.cs
│       │   ├── EFCachedQueryProviderBasicTests.cs
│       │   ├── EFCachedQueryProviderInvalidationTests.cs
│       │   ├── EFSecondLevelCache.Core.Tests.csproj
│       │   ├── Properties/
│       │   │   └── AssemblyInfo.cs
│       │   ├── TestsBase.cs
│       │   ├── XxHashTests.cs
│       │   ├── _0-restore.bat
│       │   └── _1-dotnet_test.bat
│       └── Issues/
│           ├── Issue15/
│           │   ├── ConfigureServices.cs
│           │   ├── Issue15.csproj
│           │   ├── Payment.cs
│           │   ├── Program.cs
│           │   ├── SampleContext.cs
│           │   └── app_data/
│           │       └── .git.keep
│           └── Issue43/
│               ├── Issue43.csproj
│               ├── Program.cs
│               ├── _0-restore.bat
│               ├── _1-dotnet_run.bat
│               └── app_data/
│                   └── .git.keep
├── tag-it.bat
└── update-dependencies.bat

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs     diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following 
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln       merge=binary
#*.csproj    merge=binary
#*.vbproj    merge=binary
#*.vcxproj   merge=binary
#*.vcproj    merge=binary
#*.dbproj    merge=binary
#*.fsproj    merge=binary
#*.lsproj    merge=binary
#*.wixproj   merge=binary
#*.modelproj merge=binary
#*.sqlproj   merge=binary
#*.wwaproj   merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg   binary
#*.png   binary
#*.gif   binary

###############################################################################
# diff behavior for common document formats
# 
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the 
# entries below.
###############################################################################
#*.doc   diff=astextplain
#*.DOC   diff=astextplain
#*.docx  diff=astextplain
#*.DOCX  diff=astextplain
#*.dot   diff=astextplain
#*.DOT   diff=astextplain
#*.pdf   diff=astextplain
#*.PDF   diff=astextplain
#*.rtf   diff=astextplain
#*.RTF   diff=astextplain


================================================
FILE: .github/issue_template.md
================================================
# Summary of the issue



## Environment

```
.NET Core SDK version: 
Microsoft.EntityFrameworkCore version: 
EFSecondLevelCache.Core version: 
```

## Example code/Steps to reproduce:

```
paste your core code
```

## Output:

```
Exception message:
Full Stack trace:
```



================================================
FILE: .github/lock.yml
================================================
daysUntilLock: 90

skipCreatedBefore: false

exemptLabels: []

lockLabel: false

lockComment: >
  This thread has been automatically locked since there has not been
  any recent activity after it was closed. Please open a new issue for
  related problems.
  
setLockReason: true


================================================
FILE: .github/workflows/build.yml
================================================
name: .NET Core Build

on: [push, pull_request]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1
    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 3.1.101
    - name: Build DNTCaptcha.Core lib
      run: dotnet build ./src/EFSecondLevelCache.Core/EFSecondLevelCache.Core.csproj --configuration Release

================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
[Xx]64/
[Xx]86/
[Bb]uild/
bld/
[Bb]in/
[Oo]bj/

# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# DNX
project.lock.json
artifacts/

*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding add-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml

# TODO: Un-comment the next line if you do not want to checkin 
# your web deploy settings because they may include unencrypted
# passwords
#*.pubxml
*.publishproj

# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config

# Windows Store app package directory
AppPackages/
BundleArtifacts/

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/

# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm

# SQL Server files
*.mdf
*.ldf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# LightSwitch generated files
GeneratedArtifacts/
ModelManifest.xml

# Paket dependency manager
.paket/paket.exe

# FAKE - F# Make
.fake/
/.idea
/src/Tests/EFSecondLevelCache.Core.PerformanceTests/BenchmarkDotNet.Artifacts

================================================
FILE: .vscode/launch.json
================================================
{
    // Use IntelliSense to find out which attributes exist for C# debugging
    // Use hover for the description of the existing attributes
    // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
    "version": "0.2.0",
    "configurations": [
        {
            "name": ".NET Core Launch (console)-Issue43",
            "type": "coreclr",
            "request": "launch",
            "preLaunchTask": "build",
            "program": "${workspaceFolder}/src/Tests/Issues/Issue43/bin/Debug/netcoreapp3.1/Issue43.dll",
            "args": [],
            "cwd": "${workspaceFolder}",
            "stopAtEntry": false,
            "console": "internalConsole"
        },
        {
            "name": ".NET Core Launch (web)",
            "type": "coreclr",
            "request": "launch",
            "preLaunchTask": "build",
            // If you have changed target frameworks, make sure to update the program path.
            "program": "${workspaceRoot}/src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/bin/Debug/netcoreapp3.1/EFSecondLevelCache.Core.AspNetCoreSample.dll",
            "args": [],
            "cwd": "${workspaceRoot}/src/Tests/EFSecondLevelCache.Core.AspNetCoreSample",
            "stopAtEntry": false,
            "internalConsoleOptions": "openOnSessionStart",
            "launchBrowser": {
                "enabled": true,
                "args": "${auto-detect-url}",
                "windows": {
                    "command": "cmd.exe",
                    "args": "/C start ${auto-detect-url}"
                },
                "osx": {
                    "command": "open"
                },
                "linux": {
                    "command": "xdg-open"
                }
            },
            "env": {
                "ASPNETCORE_ENVIRONMENT": "Development"
            },
            "sourceFileMap": {
                "/Views": "${workspaceRoot}/Views"
            }
        },
        {
            "name": ".NET Core Attach",
            "type": "coreclr",
            "request": "attach",
            "processId": "${command:pickProcess}"
        }
    ]
}

================================================
FILE: .vscode/settings.json
================================================
{
    "workbench.colorCustomizations": {
        "activityBar.background": "#7295b1",
        "activityBar.activeBorder": "#e8d6e0",
        "activityBar.foreground": "#15202b",
        "activityBar.inactiveForeground": "#15202b99",
        "activityBarBadge.background": "#e8d6e0",
        "activityBarBadge.foreground": "#15202b",
        "titleBar.activeBackground": "#557c9b",
        "titleBar.inactiveBackground": "#557c9b99",
        "titleBar.activeForeground": "#e7e7e7",
        "titleBar.inactiveForeground": "#e7e7e799",
        "statusBar.background": "#557c9b",
        "statusBarItem.hoverBackground": "#7295b1",
        "statusBar.foreground": "#e7e7e7"
    },
    "peacock.color": "#557c9b"
}

================================================
FILE: .vscode/tasks.json
================================================
{
    "version": "0.1.0",
    "command": "dotnet",
    "isShellCommand": true,
    "args": [],
    "tasks": [
        {
            "taskName": "build",
            "args": [
                "${workspaceRoot}/src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/EFSecondLevelCache.Core.AspNetCoreSample.csproj"
            ],
            "isBuildCommand": true,
            "problemMatcher": "$msCompile"
        }
    ]
}

================================================
FILE: EFSecondLevelCache.Core.sln
================================================

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.4
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0C0EA45E-C765-4D41-A1DA-EDDC8F239A5B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{312DDEFA-6800-4297-9B38-0C458BB28FED}"
	ProjectSection(SolutionItems) = preProject
		LICENSE.md = LICENSE.md
		README.md = README.md
	EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFSecondLevelCache.Core", "src\EFSecondLevelCache.Core\EFSecondLevelCache.Core.csproj", "{755D6A18-E67A-4E14-8289-BD33A901C52B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFSecondLevelCache.Core.Tests", "src\Tests\EFSecondLevelCache.Core.Tests\EFSecondLevelCache.Core.Tests.csproj", "{5475D985-85D6-4DD2-899E-8E8B333B0070}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFSecondLevelCache.Core.AspNetCoreSample", "src\Tests\EFSecondLevelCache.Core.AspNetCoreSample\EFSecondLevelCache.Core.AspNetCoreSample.csproj", "{EB8B44EC-D677-47B3-8A72-3E340BFDF0DE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFSecondLevelCache.Core.NET46Sample", "src\Tests\EFSecondLevelCache.Core.NET46Sample\EFSecondLevelCache.Core.NET46Sample\EFSecondLevelCache.Core.NET46Sample.csproj", "{CEE7F26B-7A7E-4021-A37F-F9625EAF85CE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{4D31E9D7-B686-44FF-8BCD-C17BA26A0333}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Issues", "Issues", "{EDE35C41-BFB7-492F-A8FD-C13FB276486C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Issue15", "src\Tests\Issues\Issue15\Issue15.csproj", "{767215E4-AF19-4AD0-968C-532BE14AF75A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFSecondLevelCache.Core.PerformanceTests", "src\Tests\EFSecondLevelCache.Core.PerformanceTests\EFSecondLevelCache.Core.PerformanceTests.csproj", "{01DE3CF0-AA97-43B3-B95C-45184DF223CB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Issue43", "src\Tests\Issues\Issue43\Issue43.csproj", "{65D28CEA-604F-4283-BDEE-9D6B669A37E1}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
		Debug|x64 = Debug|x64
		Debug|x86 = Debug|x86
		Release|x64 = Release|x64
		Release|x86 = Release|x86
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{755D6A18-E67A-4E14-8289-BD33A901C52B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{755D6A18-E67A-4E14-8289-BD33A901C52B}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{755D6A18-E67A-4E14-8289-BD33A901C52B}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{755D6A18-E67A-4E14-8289-BD33A901C52B}.Release|Any CPU.Build.0 = Release|Any CPU
		{5475D985-85D6-4DD2-899E-8E8B333B0070}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{5475D985-85D6-4DD2-899E-8E8B333B0070}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{5475D985-85D6-4DD2-899E-8E8B333B0070}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{5475D985-85D6-4DD2-899E-8E8B333B0070}.Release|Any CPU.Build.0 = Release|Any CPU
		{EB8B44EC-D677-47B3-8A72-3E340BFDF0DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{EB8B44EC-D677-47B3-8A72-3E340BFDF0DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{EB8B44EC-D677-47B3-8A72-3E340BFDF0DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{EB8B44EC-D677-47B3-8A72-3E340BFDF0DE}.Release|Any CPU.Build.0 = Release|Any CPU
		{CEE7F26B-7A7E-4021-A37F-F9625EAF85CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{CEE7F26B-7A7E-4021-A37F-F9625EAF85CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{CEE7F26B-7A7E-4021-A37F-F9625EAF85CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{CEE7F26B-7A7E-4021-A37F-F9625EAF85CE}.Release|Any CPU.Build.0 = Release|Any CPU
		{767215E4-AF19-4AD0-968C-532BE14AF75A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{767215E4-AF19-4AD0-968C-532BE14AF75A}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{767215E4-AF19-4AD0-968C-532BE14AF75A}.Debug|x64.ActiveCfg = Debug|x64
		{767215E4-AF19-4AD0-968C-532BE14AF75A}.Debug|x64.Build.0 = Debug|x64
		{767215E4-AF19-4AD0-968C-532BE14AF75A}.Debug|x86.ActiveCfg = Debug|x86
		{767215E4-AF19-4AD0-968C-532BE14AF75A}.Debug|x86.Build.0 = Debug|x86
		{767215E4-AF19-4AD0-968C-532BE14AF75A}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{767215E4-AF19-4AD0-968C-532BE14AF75A}.Release|Any CPU.Build.0 = Release|Any CPU
		{767215E4-AF19-4AD0-968C-532BE14AF75A}.Release|x64.ActiveCfg = Release|x64
		{767215E4-AF19-4AD0-968C-532BE14AF75A}.Release|x64.Build.0 = Release|x64
		{767215E4-AF19-4AD0-968C-532BE14AF75A}.Release|x86.ActiveCfg = Release|x86
		{767215E4-AF19-4AD0-968C-532BE14AF75A}.Release|x86.Build.0 = Release|x86
		{01DE3CF0-AA97-43B3-B95C-45184DF223CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{01DE3CF0-AA97-43B3-B95C-45184DF223CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{01DE3CF0-AA97-43B3-B95C-45184DF223CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{01DE3CF0-AA97-43B3-B95C-45184DF223CB}.Release|Any CPU.Build.0 = Release|Any CPU
		{01DE3CF0-AA97-43B3-B95C-45184DF223CB}.Debug|x64.ActiveCfg = Debug|Any CPU
		{01DE3CF0-AA97-43B3-B95C-45184DF223CB}.Debug|x64.Build.0 = Debug|Any CPU
		{01DE3CF0-AA97-43B3-B95C-45184DF223CB}.Debug|x86.ActiveCfg = Debug|Any CPU
		{01DE3CF0-AA97-43B3-B95C-45184DF223CB}.Debug|x86.Build.0 = Debug|Any CPU
		{01DE3CF0-AA97-43B3-B95C-45184DF223CB}.Release|x64.ActiveCfg = Release|Any CPU
		{01DE3CF0-AA97-43B3-B95C-45184DF223CB}.Release|x64.Build.0 = Release|Any CPU
		{01DE3CF0-AA97-43B3-B95C-45184DF223CB}.Release|x86.ActiveCfg = Release|Any CPU
		{01DE3CF0-AA97-43B3-B95C-45184DF223CB}.Release|x86.Build.0 = Release|Any CPU
		{65D28CEA-604F-4283-BDEE-9D6B669A37E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{65D28CEA-604F-4283-BDEE-9D6B669A37E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{65D28CEA-604F-4283-BDEE-9D6B669A37E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{65D28CEA-604F-4283-BDEE-9D6B669A37E1}.Release|Any CPU.Build.0 = Release|Any CPU
		{65D28CEA-604F-4283-BDEE-9D6B669A37E1}.Debug|x64.ActiveCfg = Debug|Any CPU
		{65D28CEA-604F-4283-BDEE-9D6B669A37E1}.Debug|x64.Build.0 = Debug|Any CPU
		{65D28CEA-604F-4283-BDEE-9D6B669A37E1}.Debug|x86.ActiveCfg = Debug|Any CPU
		{65D28CEA-604F-4283-BDEE-9D6B669A37E1}.Debug|x86.Build.0 = Debug|Any CPU
		{65D28CEA-604F-4283-BDEE-9D6B669A37E1}.Release|x64.ActiveCfg = Release|Any CPU
		{65D28CEA-604F-4283-BDEE-9D6B669A37E1}.Release|x64.Build.0 = Release|Any CPU
		{65D28CEA-604F-4283-BDEE-9D6B669A37E1}.Release|x86.ActiveCfg = Release|Any CPU
		{65D28CEA-604F-4283-BDEE-9D6B669A37E1}.Release|x86.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(NestedProjects) = preSolution
		{755D6A18-E67A-4E14-8289-BD33A901C52B} = {0C0EA45E-C765-4D41-A1DA-EDDC8F239A5B}
		{5475D985-85D6-4DD2-899E-8E8B333B0070} = {0C0EA45E-C765-4D41-A1DA-EDDC8F239A5B}
		{EB8B44EC-D677-47B3-8A72-3E340BFDF0DE} = {0C0EA45E-C765-4D41-A1DA-EDDC8F239A5B}
		{CEE7F26B-7A7E-4021-A37F-F9625EAF85CE} = {0C0EA45E-C765-4D41-A1DA-EDDC8F239A5B}
		{4D31E9D7-B686-44FF-8BCD-C17BA26A0333} = {0C0EA45E-C765-4D41-A1DA-EDDC8F239A5B}
		{EDE35C41-BFB7-492F-A8FD-C13FB276486C} = {4D31E9D7-B686-44FF-8BCD-C17BA26A0333}
		{767215E4-AF19-4AD0-968C-532BE14AF75A} = {EDE35C41-BFB7-492F-A8FD-C13FB276486C}
		{01DE3CF0-AA97-43B3-B95C-45184DF223CB} = {4D31E9D7-B686-44FF-8BCD-C17BA26A0333}
		{65D28CEA-604F-4283-BDEE-9D6B669A37E1} = {EDE35C41-BFB7-492F-A8FD-C13FB276486C}
	EndGlobalSection
EndGlobal


================================================
FILE: LICENSE.md
================================================
Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "{}"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright {yyyy} {name of copyright owner}

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.



================================================
FILE: README.md
================================================
# [Announcing a better Second Level Caching Library!](https://github.com/VahidN/EFSecondLevelCache.Core/issues/67)

# EFSecondLevelCache.Core

<p align="left">
  <a href="https://github.com/VahidN/EFSecondLevelCache.Core">
     <img alt="GitHub Actions status" src="https://github.com/VahidN/EFSecondLevelCache.Core/workflows/.NET%20Core%20Build/badge.svg">
  </a>
</p>

Entity Framework Core Second Level Caching Library.

Second level caching is a query cache. The results of EF commands will be stored in the cache, so that the same EF commands will retrieve their data from the cache rather than executing them against the database again.

## Install via NuGet

To install EFSecondLevelCache.Core, run the following command in the Package Manager Console:

[![Nuget](https://img.shields.io/nuget/v/EFSecondLevelCache.Core)](https://github.com/VahidN/EFSecondLevelCache.Core)

```
PM> Install-Package EFSecondLevelCache.Core
```

You can also view the [package page](http://www.nuget.org/packages/EFSecondLevelCache.Core/) on NuGet.

This library also uses the [CacheManager.Core](https://github.com/MichaCo/CacheManager), as a highly configurable cache manager.
To use its in-memory caching mechanism, add these entries to the `.csproj` file:

```xml
  <ItemGroup>
    <PackageReference Include="EFSecondLevelCache.Core" Version="2.9.0" />
    <PackageReference Include="CacheManager.Core" Version="1.2.0" />
    <PackageReference Include="CacheManager.Microsoft.Extensions.Caching.Memory" Version="1.2.0" />
    <PackageReference Include="CacheManager.Serialization.Json" Version="1.2.0" />
  </ItemGroup>
```

And to get the latest versions of these libraries you can run the following command in the Package Manager Console:

```
PM> Update-Package
```

## Usage

1- [Register the required services](/src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Startup.cs) of `EFSecondLevelCache.Core` and also `CacheManager.Core`

```csharp
namespace EFSecondLevelCache.Core.AspNetCoreSample
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddEFSecondLevelCache();

            // Add an in-memory cache service provider
            services.AddSingleton(typeof(ICacheManager<>), typeof(BaseCacheManager<>));
            services.AddSingleton(typeof(ICacheManagerConfiguration),
                new CacheManager.Core.ConfigurationBuilder()
                        .WithJsonSerializer()
                        .WithMicrosoftMemoryCacheHandle(instanceName: "MemoryCache1")
                        .WithExpiration(ExpirationMode.Absolute, TimeSpan.FromMinutes(10))
                        .Build());
        }
    }
}
```

If you want to use the Redis as the preferred cache provider, first install the `CacheManager.StackExchange.Redis` package and then register its required services:

```csharp
// Add Redis cache service provider
var jss = new JsonSerializerSettings
{
    NullValueHandling = NullValueHandling.Ignore,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};

const string redisConfigurationKey = "redis";
services.AddSingleton(typeof(ICacheManagerConfiguration),
    new CacheManager.Core.ConfigurationBuilder()
        .WithJsonSerializer(serializationSettings: jss, deserializationSettings: jss)
        .WithUpdateMode(CacheUpdateMode.Up)
        .WithRedisConfiguration(redisConfigurationKey, config =>
        {
            config.WithAllowAdmin()
                .WithDatabase(0)
                .WithEndpoint("localhost", 6379)
                // Enables keyspace notifications to react on eviction/expiration of items.
                // Make sure that all servers are configured correctly and 'notify-keyspace-events' is at least set to 'Exe', otherwise CacheManager will not retrieve any events.
                // See https://redis.io/topics/notifications#configuration for configuration details.
                .EnableKeyspaceEvents();
        })
        .WithMaxRetries(100)
        .WithRetryTimeout(50)
        .WithRedisCacheHandle(redisConfigurationKey)
        .WithExpiration(ExpirationMode.Absolute, TimeSpan.FromMinutes(10))
        .Build());
services.AddSingleton(typeof(ICacheManager<>), typeof(BaseCacheManager<>));
```

2- [Setting up the cache invalidation](/src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/SampleContext.cs) by overriding the SaveChanges method to prevent stale reads:

```csharp
namespace EFSecondLevelCache.Core.AspNetCoreSample.DataLayer
{
    public class SampleContext : DbContext
    {
        public SampleContext(DbContextOptions<SampleContext> options) : base(options)
        { }

        public virtual DbSet<Post> Posts { get; set; }

        public override int SaveChanges()
        {
            var changedEntityNames = this.GetChangedEntityNames();

            this.ChangeTracker.AutoDetectChangesEnabled = false; // for performance reasons, to avoid calling DetectChanges() again.
            var result = base.SaveChanges();
            this.ChangeTracker.AutoDetectChangesEnabled = true;

            this.GetService<IEFCacheServiceProvider>().InvalidateCacheDependencies(changedEntityNames);

            return result;
        }
    }
}
```

3- Then to cache the results of the normal queries like:

```csharp
var products = context.Products.Include(x => x.Tags).FirstOrDefault();
```

We can use the new `Cacheable()` extension method:

```csharp
// If you don't specify the `EFCachePolicy`, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.
var products = context.Products.Include(x => x.Tags).Cacheable().FirstOrDefault(); // Async methods are supported too.

// Or you can specify the `EFCachePolicy` explicitly to override the global settings.
var post1 = context.Posts
                   .Where(x => x.Id > 0)
                   .OrderBy(x => x.Id)
                   .Cacheable(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(5))
                   .FirstOrDefault();

// NOTE: It's better to add the `Cacheable()` method before the materialization methods such as `ToList()` or `FirstOrDefault()` to cover the whole expression tree.
```

Also AutoMapper's `ProjectTo()` method is supported:

```csharp
var posts = context.Posts
                   .Where(x => x.Id > 0)
                   .OrderBy(x => x.Id)
                   .Cacheable()
                   .ProjectTo<PostDto>(configuration: _mapper.ConfigurationProvider)
                   .ToList();
```

## Guidance

### When to use

Good candidates for query caching are global site settings and public data, such as infrequently changing articles or comments. It can also be beneficial to cache data specific to a user so long as the cache expires frequently enough relative to the size of the user base that memory consumption remains acceptable. Small, per-user data that frequently exceeds the cache's lifetime, such as a user's photo path, is better held in user claims, which are stored in cookies, than in this cache.

### Scope

This cache is scoped to the application, not the current user. It does not use session variables. Accordingly, when retriveing cached per-user data, be sure queries in include code such as `.Where(x => .... && x.UserId == id)`.

### Invalidation

This cache is updated when an entity is changed (insert, update, or delete) via a DbContext that uses this library. If the database is updated through some other means, such as a stored procedure or trigger, the cache becomes stale.


================================================
FILE: global.json
================================================
{
  "sdk": {
    "version": "3.1.101"
  }
}

================================================
FILE: src/EFSecondLevelCache.Core/Contracts/EFCacheDebugInfo.cs
================================================
namespace EFSecondLevelCache.Core.Contracts
{
    /// <summary>
    /// Stores the debug information of the caching process.
    /// </summary>
    public class EFCacheDebugInfo
    {
        /// <summary>
        /// Stores information of the computed key of the input LINQ query.
        /// </summary>
        public EFCacheKey EFCacheKey { set; get; }

        /// <summary>
        /// Determines this query is using the 2nd level cache or not.
        /// </summary>
        public bool IsCacheHit { set; get; }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/Contracts/EFCacheKey.cs
================================================
using System.Collections.Generic;

namespace EFSecondLevelCache.Core.Contracts
{
    /// <summary>
    /// Stores information of the computed key of the input LINQ query.
    /// </summary>
    public class EFCacheKey
    {
        /// <summary>
        /// The computed key of the input LINQ query.
        /// </summary>
        public string Key { set; get; }

        /// <summary>
        /// Hash of the input LINQ query's computed key.
        /// </summary>
        public string KeyHash { set; get; }

        /// <summary>
        /// Determines which entities are used in this LINQ query.
        /// This array will be used to invalidate the related cache of all related queries automatically.
        /// </summary>
        public ISet<string> CacheDependencies { set; get; }

        /// <summary>
        /// Stores information of the computed key of the input LINQ query.
        /// </summary>
        public EFCacheKey()
        {
            CacheDependencies = new HashSet<string>();
        }

        /// <summary>
        /// Equals
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            var efCacheKey = obj as EFCacheKey;
            if (efCacheKey == null)
                return false;

            return this.KeyHash == efCacheKey.KeyHash;
        }

        /// <summary>
        /// GetHashCode
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            unchecked
            {
                var hash = 17;
                hash = hash * 23 + KeyHash.GetHashCode();
                return hash;
            }
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/Contracts/EFQueryDebugView.cs
================================================
using System.Collections.Generic;

namespace EFSecondLevelCache.Core.Contracts
{
    /// <summary>
    /// Expression and its Dependencies
    /// </summary>
    public class EFQueryDebugView
    {
        /// <summary>
        ///  Dependency items.
        /// </summary>
        public ISet<string> Types { set; get; }

        /// <summary>
        /// Expression to a readable string.
        /// </summary>
        public string DebugView { set; get; }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/Contracts/IEFCacheKeyHashProvider.cs
================================================
namespace EFSecondLevelCache.Core.Contracts
{
    /// <summary>
    /// The CacheKey Hash Provider Contract.
    /// </summary>
    public interface IEFCacheKeyHashProvider
    {
        /// <summary>
        /// Computes the unique hash of the input.
        /// </summary>
        /// <param name="data">the input data to hash</param>
        /// <returns>Hashed data</returns>
        string ComputeHash(string data);
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/Contracts/IEFCacheKeyProvider.cs
================================================
using System.Linq;
using System.Linq.Expressions;

namespace EFSecondLevelCache.Core.Contracts
{
    /// <summary>
    /// CacheKeyProvider Contract.
    /// </summary>
    public interface IEFCacheKeyProvider
    {
        /// <summary>
        /// Gets an EF query and returns its hash to store in the cache.
        /// </summary>
        /// <param name="query">The EF query.</param>
        /// <param name="expression">An expression tree that represents a LINQ query.</param>
        /// <param name="saltKey">If you think the computed hash of the query is not enough, set this value.</param>
        /// <returns>Information of the computed key of the input LINQ query.</returns>
        EFCacheKey GetEFCacheKey(IQueryable query, Expression expression, string saltKey = "");
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/Contracts/IEFCacheServiceProvider.cs
================================================
using System.Collections.Generic;

namespace EFSecondLevelCache.Core.Contracts
{
    /// <summary>
    /// Cache Service Provider Contract.
    /// </summary>
    public interface IEFCacheServiceProvider
    {
        /// <summary>
        /// Removes the cached entries added by this library.
        /// </summary>
        void ClearAllCachedEntries();

        /// <summary>
        /// Gets a cached entry by key.
        /// </summary>
        /// <param name="cacheKey">key to find</param>
        /// <returns>cached value</returns>
        object GetValue(string cacheKey);

        /// <summary>
        /// Adds a new item to the cache.
        /// </summary>
        /// <param name="cacheKey">key</param>
        /// <param name="value">value</param>
        /// <param name="rootCacheKeys">cache dependencies</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item. If you set it to null, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.</param>
        void InsertValue(string cacheKey, object value, ISet<string> rootCacheKeys, EFCachePolicy cachePolicy);

        /// <summary>
        /// Invalidates all of the cache entries which are dependent on any of the specified root keys.
        /// </summary>
        /// <param name="rootCacheKeys">cache dependencies</param>
        void InvalidateCacheDependencies(string[] rootCacheKeys);

        /// <summary>
        /// Some cache providers won't accept null values.
        /// So we need a custom Null object here. It should be defined `static readonly` in your code.
        /// </summary>
        object NullObject { get; }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFAsyncEnumerable.cs
================================================
using System.Collections.Generic;
using System.Threading;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Asynchronous version of the IEnumerable interface, allowing elements of the enumerable sequence to be retrieved asynchronously.
    /// </summary>
    public class EFAsyncEnumerable<T> : IAsyncEnumerable<T>
    {
        private readonly IEnumerator<T> _inner;

        /// <summary>
        /// Asynchronous version of the IEnumerable interface
        /// </summary>
        public EFAsyncEnumerable(IEnumerator<T> inner)
        {
            _inner = inner;
        }

        /// <summary>
        /// Gets an asynchronous enumerator over the sequence.
        /// </summary>
        public IAsyncEnumerator<T> GetEnumerator()
        {
            return new EFAsyncEnumerator<T>(_inner);
        }

        /// <summary>
        /// Gets an asynchronous enumerator over the sequence.
        /// </summary>
        public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken())
        {
            return new EFAsyncEnumerator<T>(_inner);
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFAsyncEnumerator.cs
================================================
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Asynchronous version of the IEnumerator of T interface that allows elements to be retrieved asynchronously.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class EFAsyncEnumerator<T> : IAsyncEnumerator<T>
    {
        private readonly IEnumerator<T> _inner;

        /// <summary>
        /// Asynchronous version of the IEnumerator of T interface that allows elements to be retrieved asynchronously.
        /// </summary>
        /// <param name="inner">The inner IEnumerator</param>
        public EFAsyncEnumerator(IEnumerator<T> inner)
        {
            _inner = inner;
        }

        /// <summary>
        /// Gets the current element in the iteration.
        /// </summary>
        public T Current => _inner.Current;

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            _inner.Dispose();
        }

        /// <summary>
        /// Advances the enumerator to the next element in the sequence, returning the result asynchronously.
        /// </summary>
        /// <param name="cancellationToken">A System.Threading.CancellationToken to observe while waiting for the task to complete.</param>
        /// <returns>A task that represents the asynchronous operation.  The task result contains true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the sequence.</returns>
        public Task<bool> MoveNext(CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            return Task.FromResult(_inner.MoveNext());
        }

        /// <summary>
        /// Advances the enumerator to the next element in the sequence, returning the result asynchronously.
        /// </summary>
        /// <returns>A task that represents the asynchronous operation.  The task result contains true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the sequence.</returns>
        public ValueTask<bool> MoveNextAsync()
        {
            return new ValueTask<bool>(_inner.MoveNext());
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public ValueTask DisposeAsync()
        {
            _inner.Dispose();
            return default;
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFAsyncTaskEnumerable.cs
================================================
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Asynchronous version of the IEnumerable interface, allowing elements of the enumerable sequence to be retrieved asynchronously.
    /// </summary>
    public class EFAsyncTaskEnumerable<T> : IAsyncEnumerable<T>
    {
        private readonly Task<T> _task;

        /// <summary>
        /// Asynchronous version of the IEnumerable interface.
        /// </summary>
        public EFAsyncTaskEnumerable(Task<T> task)
        {
            _task = task;
        }

        /// <summary>
        /// Gets an asynchronous enumerator over the sequence.
        /// </summary>
        public IAsyncEnumerator<T> GetEnumerator() => new EFAsyncTaskEnumerator<T>(_task);

        /// <summary>
        /// Gets an asynchronous enumerator over the sequence.
        /// </summary>
        public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken())
        {
            return new EFAsyncTaskEnumerator<T>(_task);
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFAsyncTaskEnumerator.cs
================================================
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Asynchronous version of the IEnumerator interface, allowing elements to be retrieved asynchronously.
    /// </summary>
    public sealed class EFAsyncTaskEnumerator<T> : IAsyncEnumerator<T>
    {
        private readonly Task<T> _task;
        private bool _moved;

        /// <summary>
        /// Asynchronous version of the IEnumerator interface
        /// </summary>
        public EFAsyncTaskEnumerator(Task<T> task)
        {
            _task = task;
        }

        /// <summary>
        /// Gets the current element in the iteration.
        /// </summary>
        public T Current => !_moved ? default(T) : _task.Result;

        /// <summary>
        /// Advances the enumerator to the next element in the sequence, returning the result asynchronously.
        /// </summary>
        public async Task<bool> MoveNext(CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            if (!_moved)
            {
                await _task.ConfigureAwait(false);

                _moved = true;

                return _moved;
            }

            return false;
        }

        /// <summary>
        /// Advances the enumerator to the next element in the sequence, returning the result asynchronously.
        /// </summary>
        public ValueTask<bool> MoveNextAsync()
        {
            return new ValueTask<bool>(MoveNext(new CancellationToken()));
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public ValueTask DisposeAsync()
        {
            return default;
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFCacheKeyHashProvider.cs
================================================
using System;
using EFSecondLevelCache.Core.Contracts;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Computes the unique hash of the input, using the xxHash algorithm.
    /// </summary>
    public class EFCacheKeyHashProvider : IEFCacheKeyHashProvider
    {
        /// <summary>
        /// Computes the unique hash of the input.
        /// </summary>
        /// <param name="data">the input data to hash</param>
        /// <returns>Hashed data using the xxHash algorithm</returns>
        public string ComputeHash(string data)
        {
            if(string.IsNullOrWhiteSpace(data))
                throw new ArgumentNullException(nameof(data));

            return $"{XxHashUnsafe.ComputeHash(data):X}";
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFCacheKeyProvider3x.cs
================================================
using System.Linq;
using System.Linq.Expressions;
using EFSecondLevelCache.Core.Contracts;
using System;
using CacheManager.Core;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// A custom cache key provider for EF queries.
    /// </summary>
    public class EFCacheKeyProvider : IEFCacheKeyProvider
    {
        private static readonly TypeInfo _queryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();

        private static readonly FieldInfo _queryCompilerField =
            typeof(EntityQueryProvider).GetTypeInfo().DeclaredFields.First(x => x.Name == "_queryCompiler");

        private static readonly FieldInfo _queryContextFactoryField =
            _queryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_queryContextFactory");

        private static readonly FieldInfo _loggerField =
            _queryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_logger");

        private static readonly TimeSpan _slidingExpirationTimeSpan = TimeSpan.FromMinutes(7);

        private static readonly ICacheManager<EFCacheKey> _keysCacheManager =
            EFStaticServiceProvider.Instance.GetRequiredService<ICacheManager<EFCacheKey>>();

        private readonly IEFCacheKeyHashProvider _cacheKeyHashProvider;

        /// <summary>
        /// A custom cache key provider for EF queries.
        /// </summary>
        /// <param name="cacheKeyHashProvider">Provides the custom hashing algorithm.</param>
        public EFCacheKeyProvider(IEFCacheKeyHashProvider cacheKeyHashProvider)
        {
            _cacheKeyHashProvider = cacheKeyHashProvider;
        }

        /// <summary>
        /// Gets an EF query and returns its hashed key to store in the cache.
        /// </summary>
        /// <param name="query">The EF query.</param>
        /// <param name="expression">An expression tree that represents a LINQ query.</param>
        /// <param name="saltKey">If you think the computed hash of the query is not enough, set this value.</param>
        /// <returns>Information of the computed key of the input LINQ query.</returns>
        public EFCacheKey GetEFCacheKey(IQueryable query, Expression expression, string saltKey = "")
        {
            var queryCompiler = (QueryCompiler)_queryCompilerField.GetValue(query.Provider);
            var (expressionKeyHash, modifiedExpression) = getExpressionKeyHash(queryCompiler, _cacheKeyHashProvider, expression);
            var cachedKey = _keysCacheManager.Get<EFCacheKey>(expressionKeyHash);
            if (cachedKey != null)
            {
                return cachedKey;
            }

            var expressionPrinter = new ExpressionPrinter();
            var sql = expressionPrinter.PrintDebug(modifiedExpression);

            var expressionVisitorResult = EFQueryExpressionVisitor.GetDebugView(expression);
            var key = $"{sql};{expressionVisitorResult.DebugView};{saltKey}";
            var keyHash = _cacheKeyHashProvider.ComputeHash(key);

            var cacheKey = new EFCacheKey
            {
                Key = key,
                KeyHash = keyHash,
                CacheDependencies = expressionVisitorResult.Types
            };
            setCache(expressionKeyHash, cacheKey);
            return cacheKey;
        }

        private static void setCache(string expressionKeyHash, EFCacheKey value)
        {
            _keysCacheManager.Add(
                new CacheItem<EFCacheKey>(expressionKeyHash, value, ExpirationMode.Sliding, _slidingExpirationTimeSpan));
        }

        private static (string ExpressionKeyHash, Expression ModifiedExpression) getExpressionKeyHash(
            QueryCompiler queryCompiler,
            IEFCacheKeyHashProvider cacheKeyHashProvider,
            Expression expression)
        {
            var queryContextFactory = (IQueryContextFactory)_queryContextFactoryField.GetValue(queryCompiler);
            var queryContext = queryContextFactory.Create();
            var logger = (IDiagnosticsLogger<DbLoggerCategory.Query>)_loggerField.GetValue(queryCompiler);
            expression = queryCompiler.ExtractParameters(expression, queryContext, logger, parameterize: false);

            var expressionKey = $"{ExpressionEqualityComparer.Instance.GetHashCode(expression)};";
            var parameterValues = queryContext.ParameterValues;
            if (parameterValues.Any())
            {
                expressionKey = parameterValues.Aggregate(expressionKey,
                    (current, item) => current + $"{item.Key}={item.Value?.GetHashCode()};");
            }

            return (cacheKeyHashProvider.ComputeHash(expressionKey), expression);
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFCachePolicy.cs
================================================
using System;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Defines the supported expiration modes for cache items.
    /// </summary>
    public enum CacheExpirationMode
    {
        /// <summary>
        /// Defines absolute expiration. The item will expire after the expiration timeout.
        /// </summary>
        Absolute,

        /// <summary>
        /// Defines sliding expiration. The expiration timeout will be refreshed on every access.
        /// </summary>
        Sliding
    }

    /// <summary>
    /// EFCachePolicy determines the Expiration time of the cache.
    /// If you don't define it, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.
    /// </summary>
    public class EFCachePolicy
    {
        /// <summary>
        /// Defines the expiration mode of the cache item.
        /// Its default value is Absolute.
        /// </summary>
        public CacheExpirationMode ExpirationMode { set; get; }

        /// <summary>
        /// The expiration timeout.
        /// Its default value is 20 minutes later.
        /// </summary>
        /// <value></value>
        public TimeSpan Timeout { set; get; } = TimeSpan.FromMinutes(20);

        /// <summary>
        /// If you think the computed hash of the query to calculate the cache-key is not enough, set this value.
        /// Its default value is string.Empty.
        /// </summary>
        public string SaltKey { set; get; } = string.Empty;

        /// <summary>
        /// EFCachePolicy determines the Expiration time of the cache.
        /// </summary>
        public EFCachePolicy() { }

        /// <summary>
        /// EFCachePolicy determines the Expiration time of the cache.
        /// </summary>
        /// <param name="expirationMode">Defines the expiration mode of the cache item.</param>
        /// <param name="timeout">The expiration timeout.</param>
        public EFCachePolicy(CacheExpirationMode expirationMode, TimeSpan timeout)
        {
            ExpirationMode = expirationMode;
            Timeout = timeout;
        }

        /// <summary>
        /// EFCachePolicy determines the Expiration time of the cache.
        /// </summary>
        /// <param name="expirationMode">Defines the expiration mode of the cache item.</param>
        /// <param name="timeout">The expiration timeout.</param>
        /// <param name="saltKey">If you think the computed hash of the query to calculate the cache-key is not enough, set this value.</param>
        public EFCachePolicy(CacheExpirationMode expirationMode, TimeSpan timeout, string saltKey)
        {
            ExpirationMode = expirationMode;
            Timeout = timeout;
            SaltKey = saltKey;
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFCacheServiceProvider.cs
================================================
using System.Collections.Generic;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using CacheManager.Core;
using EFSecondLevelCache.Core.Contracts;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Using ICacheManager as a cache service.
    /// </summary>
    public class EFCacheServiceProvider : IEFCacheServiceProvider
    {
        private static readonly EFCacheKey _nullObject = new EFCacheKey();

        private static readonly ICacheManager<ISet<string>> _dependenciesCacheManager =
            EFStaticServiceProvider.Instance.GetRequiredService<ICacheManager<ISet<string>>>();

        private static readonly ICacheManager<object> _valuesCacheManager =
            EFStaticServiceProvider.Instance.GetRequiredService<ICacheManager<object>>();

        private static readonly ReaderWriterLockSlim _vcmReaderWriterLock = new ReaderWriterLockSlim();
        private static readonly ReaderWriterLockSlim _dcReaderWriterLock = new ReaderWriterLockSlim();

        /// <summary>
        /// Some cache providers won't accept null values.
        /// So we need a custom Null object here. It should be defined `static readonly` in your code.
        /// </summary>
        public object NullObject => _nullObject;

        /// <summary>
        /// Using ICacheManager as a cache service.
        /// </summary>
        public EFCacheServiceProvider()
        {
            // Occurs when an item was removed by the cache handle due to expiration or e.g. memory pressure eviction.
            // Without _dependenciesCacheManager items, we can't invalidate cached items on Insert/Update/Delete.
            // So to prevent stale reads, we have to clear all cached data in this case.
            _dependenciesCacheManager.OnRemoveByHandle += (sender, args) => ClearAllCachedEntries();
        }

        /// <summary>
        /// Removes the cached entries added by this library.
        /// </summary>
        public void ClearAllCachedEntries()
        {
            _vcmReaderWriterLock.TryWriteLocked(() => _valuesCacheManager.Clear());
            _dcReaderWriterLock.TryWriteLocked(() => _dependenciesCacheManager.Clear());
        }

        /// <summary>
        /// Gets a cached entry by key.
        /// </summary>
        /// <param name="cacheKey">key to find</param>
        /// <returns>cached value</returns>
        public object GetValue(string cacheKey)
        {
            return _valuesCacheManager.Get(cacheKey);
        }

        /// <summary>
        /// Adds a new item to the cache.
        /// </summary>
        /// <param name="cacheKey">key</param>
        /// <param name="value">value</param>
        /// <param name="rootCacheKeys">cache dependencies</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item.</param>
        public void InsertValue(string cacheKey, object value, ISet<string> rootCacheKeys, EFCachePolicy cachePolicy)
        {
            if (value == null)
            {
                value = NullObject; // `HttpRuntime.Cache.Insert` won't accept null values.
            }

            foreach (var rootCacheKey in rootCacheKeys)
            {
                _dcReaderWriterLock.TryWriteLocked(() =>
                {
                    _dependenciesCacheManager.AddOrUpdate(rootCacheKey, new HashSet<string> { cacheKey },
                        updateValue: set =>
                        {
                            set.Add(cacheKey);
                            return set;
                        });
                });
            }

            _vcmReaderWriterLock.TryWriteLocked(() =>
            {
                if (cachePolicy == null)
                {
                    _valuesCacheManager.Add(cacheKey, value);
                }
                else
                {
                    _valuesCacheManager.Add(new CacheItem<object>(
                      cacheKey,
                      value,
                      cachePolicy.ExpirationMode == CacheExpirationMode.Absolute ? ExpirationMode.Absolute : ExpirationMode.Sliding,
                      cachePolicy.Timeout));
                }
            });
        }

        /// <summary>
        /// Invalidates all of the cache entries which are dependent on any of the specified root keys.
        /// </summary>
        /// <param name="rootCacheKeys">cache dependencies</param>
        public void InvalidateCacheDependencies(string[] rootCacheKeys)
        {
            foreach (var rootCacheKey in rootCacheKeys)
            {
                if (string.IsNullOrWhiteSpace(rootCacheKey))
                {
                    continue;
                }

                clearDependencyValues(rootCacheKey);
                _dcReaderWriterLock.TryWriteLocked(() => _dependenciesCacheManager.Remove(rootCacheKey));
            }
        }

        private void clearDependencyValues(string rootCacheKey)
        {
            _dcReaderWriterLock.TryReadLocked(() =>
            {
                var dependencyKeys = _dependenciesCacheManager.Get(rootCacheKey);
                if (dependencyKeys == null)
                {
                    return;
                }

                foreach (var dependencyKey in dependencyKeys)
                {
                    _vcmReaderWriterLock.TryWriteLocked(() => _valuesCacheManager.Remove(dependencyKey));
                }
            });
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFCachedDbSet.cs
================================================
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using EFSecondLevelCache.Core.Contracts;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query.Internal;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Provides functionality to evaluate queries against a specific data source.
    /// </summary>
    /// <typeparam name="TType"></typeparam>
    public class EFCachedDbSet<TType> : IOrderedQueryable<TType>, IAsyncEnumerable<TType>
        where TType : class
    {
        private readonly EFCachedQueryProvider _provider;

        /// <summary>
        /// Provides functionality to evaluate queries against a specific data source.
        /// </summary>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <param name="cacheKeyProvider">Gets an EF query and returns its hash to store in the cache.</param>
        /// <param name="cacheServiceProvider">Cache Service Provider.</param>
        public EFCachedDbSet(
            DbSet<TType> query,
            EFCachePolicy cachePolicy,
            EFCacheDebugInfo debugInfo,
            IEFCacheKeyProvider cacheKeyProvider,
            IEFCacheServiceProvider cacheServiceProvider)
        {
            CachePolicy = cachePolicy;
            DebugInfo = debugInfo;
            CacheKeyProvider = cacheKeyProvider;
            CacheServiceProvider = cacheServiceProvider;
            Query = query;
            var queryable = Query.AsNoTracking().AsQueryable();
            ElementType = queryable.ElementType;
            Expression = queryable.Expression;
            _provider = new EFCachedQueryProvider(queryable, cachePolicy, debugInfo, cacheKeyProvider, cacheServiceProvider);
        }

        /// <summary>
        /// Asynchronous version of the IEnumerable interface
        /// </summary>
        public IAsyncEnumerable<TType> AsyncEnumerable => new EFAsyncEnumerable<TType>(this.AsEnumerable().GetEnumerator());

        /// <summary>
        /// Gets an EF query and returns its hash to store in the cache.
        /// </summary>
        public IEFCacheKeyProvider CacheKeyProvider { get; }

        /// <summary>
        /// Cache Service Provider.
        /// </summary>
        public IEFCacheServiceProvider CacheServiceProvider { get; }

        /// <summary>
        /// Stores the debug information of the caching process.
        /// </summary>
        public EFCacheDebugInfo DebugInfo { get; }

        /// <summary>
        /// Gets the type of the element(s) that are returned when the expression tree associated with this instance of System.Linq.IQueryable is executed.
        /// </summary>
        public Type ElementType { get; }

        /// <summary>
        /// Gets the expression tree that is associated with the instance of System.Linq.IQueryable.
        /// </summary>
        public Expression Expression { get; }

        /// <summary>
        /// Gets the query provider that is associated with this data source.
        /// </summary>
        public IQueryProvider Provider => _provider;

        /// <summary>
        /// The input EF query.
        /// </summary>
        public DbSet<TType> Query { get; }

        /// <summary>
        /// Defines the expiration mode of the cache item.
        /// </summary>
        public EFCachePolicy CachePolicy { get; }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>A collections that can be used to iterate through the collection.</returns>
        public IEnumerator GetEnumerator()
        {
            return _provider.Materializer.Materialize(Expression, () => Query.ToArray()).GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>A collections that can be used to iterate through the collection.</returns>
        IEnumerator<TType> IEnumerable<TType>.GetEnumerator()
        {
            return ((IEnumerable<TType>)_provider.Materializer.Materialize(Expression, () => Query.ToArray())).GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>A collections that can be used to iterate through the collection.</returns>
        public IAsyncEnumerator<TType> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken())
        {
            return new EFAsyncEnumerator<TType>(
                    ((IEnumerable<TType>)_provider.Materializer.Materialize(Expression, () => Query.ToArray())).GetEnumerator());
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFCachedDbSetExtensions.cs
================================================
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore.Diagnostics;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    ///  DbSet Extensions
    /// </summary>
    public static class EFCachedDbSetExtensions
    {
        /// <summary>
        /// Finds an entity with the given primary key values.
        /// </summary>
        public static TEntity Find<TEntity>(
            this EFCachedDbSet<TEntity> cachedQueryable, params object[] keyValues)
            where TEntity : class
        {
            var query = buildFindQueryable(cachedQueryable, keyValues);
            return new EFCachedQueryable<TEntity>(
                query, cachedQueryable.CachePolicy, cachedQueryable.DebugInfo,
                cachedQueryable.CacheKeyProvider, cachedQueryable.CacheServiceProvider).FirstOrDefault();
        }

        /// <summary>
        /// Finds an entity with the given primary key values.
        /// </summary>
        public static Task<TEntity> FindAsync<TEntity>(
            this EFCachedDbSet<TEntity> cachedQueryable,
            object[] keyValues,
            CancellationToken cancellationToken) where TEntity : class
        {
            var query = buildFindQueryable(cachedQueryable, keyValues);
            return new EFCachedQueryable<TEntity>(
                query, cachedQueryable.CachePolicy, cachedQueryable.DebugInfo,
                cachedQueryable.CacheKeyProvider, cachedQueryable.CacheServiceProvider).FirstOrDefaultAsync(cancellationToken);
        }

        /// <summary>
        /// Finds an entity with the given primary key values.
        /// </summary>
        public static Task<TEntity> FindAsync<TEntity>(
            this EFCachedDbSet<TEntity> cachedQueryable,
            params object[] keyValues) where TEntity : class
        {
            return cachedQueryable.FindAsync(keyValues, default(CancellationToken));
        }

        private static IQueryable<TEntity> buildFindQueryable<TEntity>(
            EFCachedDbSet<TEntity> cachedQueryable, object[] keyValues)
            where TEntity : class
        {
            var set = cachedQueryable.Query;
            var context = set.GetInfrastructure().GetRequiredService<IDbContextServices>().CurrentContext.Context;
            var keyProperties = context.Model.FindEntityType(typeof(TEntity)).FindPrimaryKey().Properties;

            if (keyProperties.Count != keyValues.Length)
            {
                if (keyProperties.Count == 1)
                {
                    throw new ArgumentException(
                        CoreStrings.FindNotCompositeKey(typeof(TEntity).ShortDisplayName(), keyValues.Length));
                }
                throw new ArgumentException(
                    CoreStrings.FindValueCountMismatch(typeof(TEntity).ShortDisplayName(), keyProperties.Count,
                        keyValues.Length));
            }

            for (var i = 0; i < keyValues.Length; i++)
            {
                if (keyValues[i] == null)
                {
                    throw new ArgumentNullException(nameof(keyValues));
                }

                var valueType = keyValues[i].GetType();
                var propertyType = keyProperties[i].ClrType;
                if (valueType != propertyType)
                {
                    throw new ArgumentException(
                        CoreStrings.FindValueTypeMismatch(
                            i, typeof(TEntity).ShortDisplayName(), valueType.ShortDisplayName(),
                            propertyType.ShortDisplayName()));
                }
            }

            IQueryable<TEntity> query = context.Set<TEntity>().AsNoTracking();
            var parameter = Expression.Parameter(typeof(TEntity), "x");
            for (var i = 0; i < keyProperties.Count; i++)
            {
                var property = keyProperties[i];
                var keyValue = keyValues[i];
                var expression = Expression.Lambda(
                    Expression.Equal(
                        Expression.Property(parameter, property.Name),
                        Expression.Constant(keyValue)),
                    parameter) as Expression<Func<TEntity, bool>>;

                query = query.Where(expression);
            }
            return query;
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFCachedQueryExtensions.cs
================================================
using System;
using System.Linq;
using EFSecondLevelCache.Core.Contracts;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Returns a new cached query.
    /// </summary>
    public static class EFCachedQueryExtensions
    {
        private static readonly IEFCacheKeyProvider _defaultCacheKeyProvider;
        private static readonly IEFCacheServiceProvider _defaultCacheServiceProvider;

        static EFCachedQueryExtensions()
        {
            var serviceProvider = EFStaticServiceProvider.Instance;
            _defaultCacheServiceProvider = serviceProvider.GetRequiredService<IEFCacheServiceProvider>();
            _defaultCacheKeyProvider = serviceProvider.GetRequiredService<IEFCacheKeyProvider>();
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item. If you set it to null or don't specify it, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <param name="cacheKeyProvider">Gets an EF query and returns its hash to store in the cache.</param>
        /// <param name="cacheServiceProvider">Cache Service Provider.</param>
        /// <returns></returns>
        public static EFCachedQueryable<TType> Cacheable<TType>(
            this IQueryable<TType> query, EFCachePolicy cachePolicy, EFCacheDebugInfo debugInfo,
            IEFCacheKeyProvider cacheKeyProvider, IEFCacheServiceProvider cacheServiceProvider)
        {
            return new EFCachedQueryable<TType>(query, cachePolicy, debugInfo, cacheKeyProvider, cacheServiceProvider);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item. If you set it to null or don't specify it, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <param name="cacheKeyProvider">Gets an EF query and returns its hash to store in the cache.</param>
        /// <param name="cacheServiceProvider">Cache Service Provider.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static IQueryable Cacheable(
           this IQueryable query, EFCachePolicy cachePolicy, EFCacheDebugInfo debugInfo,
           IEFCacheKeyProvider cacheKeyProvider, IEFCacheServiceProvider cacheServiceProvider)
        {
            var type = typeof(EFCachedQueryable<>).MakeGenericType(query.ElementType);
            var cachedQueryable = Activator.CreateInstance(type, query, cachePolicy, debugInfo, cacheKeyProvider, cacheServiceProvider);
            return cachedQueryable as IQueryable;
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item. If you set it to null or don't specify it, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <param name="cacheKeyProvider">Gets an EF query and returns its hash to store in the cache.</param>
        /// <param name="cacheServiceProvider">Cache Service Provider.</param>
        /// <returns></returns>
        public static EFCachedDbSet<TType> Cacheable<TType>(
            this DbSet<TType> query, EFCachePolicy cachePolicy, EFCacheDebugInfo debugInfo,
            IEFCacheKeyProvider cacheKeyProvider, IEFCacheServiceProvider cacheServiceProvider) where TType : class
        {
            return new EFCachedDbSet<TType>(query, cachePolicy, debugInfo, cacheKeyProvider, cacheServiceProvider);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item. If you set it to null or don't specify it, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <param name="serviceProvider">Defines a mechanism for retrieving a service object.</param>
        /// <returns></returns>
        public static EFCachedQueryable<TType> Cacheable<TType>(
            this IQueryable<TType> query, EFCachePolicy cachePolicy, EFCacheDebugInfo debugInfo, IServiceProvider serviceProvider)
        {
            var cacheServiceProvider = serviceProvider.GetRequiredService<IEFCacheServiceProvider>();
            var cacheKeyProvider = serviceProvider.GetRequiredService<IEFCacheKeyProvider>();
            return new EFCachedQueryable<TType>(query, cachePolicy, debugInfo, cacheKeyProvider, cacheServiceProvider);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item. If you set it to null or don't specify it, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <param name="serviceProvider">Defines a mechanism for retrieving a service object.</param>
        /// <returns></returns>
        public static IQueryable Cacheable(
            this IQueryable query, EFCachePolicy cachePolicy, EFCacheDebugInfo debugInfo, IServiceProvider serviceProvider)
        {
            var cacheServiceProvider = serviceProvider.GetRequiredService<IEFCacheServiceProvider>();
            var cacheKeyProvider = serviceProvider.GetRequiredService<IEFCacheKeyProvider>();
            return Cacheable(query, cachePolicy, debugInfo, cacheKeyProvider, cacheServiceProvider);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item. If you set it to null or don't specify it, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <param name="serviceProvider">Defines a mechanism for retrieving a service object.</param>
        /// <returns></returns>
        public static EFCachedDbSet<TType> Cacheable<TType>(
            this DbSet<TType> query, EFCachePolicy cachePolicy, EFCacheDebugInfo debugInfo, IServiceProvider serviceProvider) where TType : class
        {
            var cacheServiceProvider = serviceProvider.GetRequiredService<IEFCacheServiceProvider>();
            var cacheKeyProvider = serviceProvider.GetRequiredService<IEFCacheKeyProvider>();
            return new EFCachedDbSet<TType>(query, cachePolicy, debugInfo, cacheKeyProvider, cacheServiceProvider);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <param name="serviceProvider">Defines a mechanism for retrieving a service object.</param>
        /// <returns></returns>
        public static EFCachedQueryable<TType> Cacheable<TType>(
            this IQueryable<TType> query, EFCacheDebugInfo debugInfo, IServiceProvider serviceProvider)
        {
            return Cacheable(query, null, debugInfo, serviceProvider);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <param name="query">The input EF query.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <param name="serviceProvider">Defines a mechanism for retrieving a service object.</param>
        /// <returns></returns>
        public static IQueryable Cacheable(
            this IQueryable query, EFCacheDebugInfo debugInfo, IServiceProvider serviceProvider)
        {
            return Cacheable(query, null, debugInfo, serviceProvider);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <param name="serviceProvider">Defines a mechanism for retrieving a service object.</param>
        /// <returns></returns>
        public static EFCachedDbSet<TType> Cacheable<TType>(
            this DbSet<TType> query, EFCacheDebugInfo debugInfo, IServiceProvider serviceProvider) where TType : class
        {
            return Cacheable(query, null, debugInfo, serviceProvider);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="serviceProvider">Defines a mechanism for retrieving a service object.</param>
        /// <returns></returns>
        public static EFCachedQueryable<TType> Cacheable<TType>(
            this IQueryable<TType> query, IServiceProvider serviceProvider)
        {
            return Cacheable(query, null, new EFCacheDebugInfo(), serviceProvider);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="serviceProvider">Defines a mechanism for retrieving a service object.</param>
        /// <returns></returns>
        public static EFCachedDbSet<TType> Cacheable<TType>(
            this DbSet<TType> query, IServiceProvider serviceProvider) where TType : class
        {
            return Cacheable(query, null, new EFCacheDebugInfo(), serviceProvider);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item. If you set it to null or don't specify it, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static EFCachedQueryable<TType> Cacheable<TType>(
            this IQueryable<TType> query, EFCachePolicy cachePolicy, EFCacheDebugInfo debugInfo)
        {
            return Cacheable(query, cachePolicy, debugInfo, _defaultCacheKeyProvider, _defaultCacheServiceProvider);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item. If you set it to null or don't specify it, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static EFCachedDbSet<TType> Cacheable<TType>(
            this DbSet<TType> query, EFCachePolicy cachePolicy, EFCacheDebugInfo debugInfo) where TType : class
        {
            return Cacheable(query, cachePolicy, debugInfo, _defaultCacheKeyProvider, _defaultCacheServiceProvider);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item. If you set it to null or don't specify it, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static IQueryable Cacheable(this IQueryable query, EFCachePolicy cachePolicy, EFCacheDebugInfo debugInfo)
        {
            return Cacheable(query, cachePolicy, debugInfo, _defaultCacheKeyProvider, _defaultCacheServiceProvider);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static EFCachedQueryable<TType> Cacheable<TType>(this IQueryable<TType> query)
        {
            return Cacheable(query, null, new EFCacheDebugInfo());
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <param name="query">The input EF query.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static IQueryable Cacheable(this IQueryable query)
        {
            return Cacheable(query, null, new EFCacheDebugInfo());
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static EFCachedDbSet<TType> Cacheable<TType>(this DbSet<TType> query) where TType : class
        {
            return Cacheable(query, null, new EFCacheDebugInfo());
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static EFCachedQueryable<TType> Cacheable<TType>(this IQueryable<TType> query, EFCacheDebugInfo debugInfo)
        {
            return Cacheable(query, null, debugInfo);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static EFCachedDbSet<TType> Cacheable<TType>(this DbSet<TType> query, EFCacheDebugInfo debugInfo) where TType : class
        {
            return Cacheable(query, null, debugInfo);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item. If you set it to null or don't specify it, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static EFCachedQueryable<TType> Cacheable<TType>(this IQueryable<TType> query, EFCachePolicy cachePolicy)
        {
            return Cacheable(query, cachePolicy, new EFCacheDebugInfo());
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="expirationMode">Defines the expiration mode of the cache item.</param>
        /// <param name="timeout">The expiration timeout.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static EFCachedQueryable<TType> Cacheable<TType>(
            this IQueryable<TType> query, CacheExpirationMode expirationMode, TimeSpan timeout)
        {
            return Cacheable(query, new EFCachePolicy(expirationMode, timeout), new EFCacheDebugInfo());
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="expirationMode">Defines the expiration mode of the cache item.</param>
        /// <param name="timeout">The expiration timeout.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static EFCachedQueryable<TType> Cacheable<TType>(
            this IQueryable<TType> query, CacheExpirationMode expirationMode, TimeSpan timeout, EFCacheDebugInfo debugInfo)
        {
            return Cacheable(query, new EFCachePolicy(expirationMode, timeout), debugInfo);
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item. If you set it to null or don't specify it, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static IQueryable Cacheable(this IQueryable query, EFCachePolicy cachePolicy)
        {
            return Cacheable(query, cachePolicy, new EFCacheDebugInfo());
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item. If you set it to null or don't specify it, the global `new CacheManager.Core.ConfigurationBuilder().WithExpiration()` setting will be used automatically.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static EFCachedDbSet<TType> Cacheable<TType>(
            this DbSet<TType> query, EFCachePolicy cachePolicy) where TType : class
        {
            return Cacheable(query, cachePolicy, new EFCacheDebugInfo());
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="expirationMode">Defines the expiration mode of the cache item.</param>
        /// <param name="timeout">The expiration timeout.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static EFCachedDbSet<TType> Cacheable<TType>(
            this DbSet<TType> query, CacheExpirationMode expirationMode, TimeSpan timeout) where TType : class
        {
            return Cacheable(query, new EFCachePolicy(expirationMode, timeout), new EFCacheDebugInfo());
        }

        /// <summary>
        /// Returns a new query where the entities returned will be cached in the IEFCacheServiceProvider.
        /// </summary>
        /// <typeparam name="TType">Entity type.</typeparam>
        /// <param name="query">The input EF query.</param>
        /// <param name="expirationMode">Defines the expiration mode of the cache item.</param>
        /// <param name="timeout">The expiration timeout.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <returns>Provides functionality to evaluate queries against a specific data source.</returns>
        public static EFCachedDbSet<TType> Cacheable<TType>(
            this DbSet<TType> query, CacheExpirationMode expirationMode, TimeSpan timeout, EFCacheDebugInfo debugInfo) where TType : class
        {
            return Cacheable(query, new EFCachePolicy(expirationMode, timeout), debugInfo);
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFCachedQueryProvider.cs
================================================
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using EFSecondLevelCache.Core.Contracts;
using Microsoft.EntityFrameworkCore.Query.Internal;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Defines methods to create and execute queries that are described by an System.Linq.IQueryable object.
    /// </summary>
    public class EFCachedQueryProvider : IAsyncQueryProvider
    {
        private readonly IEFCacheKeyProvider _cacheKeyProvider;
        private readonly IEFCacheServiceProvider _cacheServiceProvider;
        private readonly EFCacheDebugInfo _debugInfo;
        private readonly EFCachePolicy _cachePolicy;
        private readonly IQueryable _query;

        private static readonly MethodInfo _fromResultMethodInfo = typeof(Task).GetMethod("FromResult");
        private static readonly MethodInfo _materializeAsyncMethodInfo = typeof(EFMaterializer).GetMethod(nameof(EFMaterializer.MaterializeAsync));

        /// <summary>
        /// Defines methods to create and execute queries that are described by an System.Linq.IQueryable object.
        /// </summary>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <param name="cacheKeyProvider">Gets an EF query and returns its hash to store in the cache.</param>
        /// <param name="cacheServiceProvider">The Cache Service Provider.</param>
        public EFCachedQueryProvider(
            IQueryable query,
            EFCachePolicy cachePolicy,
            EFCacheDebugInfo debugInfo,
            IEFCacheKeyProvider cacheKeyProvider,
            IEFCacheServiceProvider cacheServiceProvider)
        {
            _query = query;
            _cachePolicy = cachePolicy;
            _debugInfo = debugInfo;
            _cacheKeyProvider = cacheKeyProvider;
            _cacheServiceProvider = cacheServiceProvider;
            Materializer =
                new EFMaterializer(_query, _cachePolicy, _debugInfo, _cacheKeyProvider, _cacheServiceProvider);
        }

        /// <summary>
        /// Defines methods to create and execute queries that are described by an System.Linq.IQueryable object.
        /// </summary>
        public EFMaterializer Materializer { get; }

        /// <summary>
        /// Constructs an System.Linq.IQueryable of T object that can evaluate the query represented by a specified expression tree.
        /// </summary>
        /// <typeparam name="TElement">The type of the elements that is returned.</typeparam>
        /// <param name="expression">An expression tree that represents a LINQ query.</param>
        /// <returns>An System.Linq.IQueryable of T that can evaluate the query represented by the specified expression tree.</returns>
        public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
        {
            return (IQueryable<TElement>)CreateQuery(expression);
        }

        /// <summary>
        /// Constructs an System.Linq.IQueryable object that can evaluate the query represented by a specified expression tree.
        /// </summary>
        /// <param name="expression">An expression tree that represents a LINQ query.</param>
        /// <returns>An System.Linq.IQueryable that can evaluate the query represented by the specified expression tree.</returns>
        public IQueryable CreateQuery(Expression expression)
        {
            var argumentType = expression.Type.GenericTypeArguments[0];
            var cachedQueryable = typeof(EFCachedQueryable<>).MakeGenericType(argumentType);
            var constructorArgs = new object[]
            {
                _query.Provider.CreateQuery(expression),
                _cachePolicy,
                _debugInfo,
                _cacheKeyProvider,
                _cacheServiceProvider
            };
            return (IQueryable)Activator.CreateInstance(cachedQueryable, constructorArgs);
        }

        /// <summary>
        /// Executes the query represented by a specified expression tree.
        /// </summary>
        /// <param name="expression">An expression tree that represents a LINQ query.</param>
        /// <returns>The value that results from executing the specified query.</returns>
        public object Execute(Expression expression)
        {
            return Materializer.Materialize(expression, () => _query.Provider.Execute(expression));
        }

        /// <summary>
        /// Executes the strongly-typed query represented by a specified expression tree.
        /// </summary>
        /// <typeparam name="TResult">The type of the value that results from executing the query.</typeparam>
        /// <param name="expression">An expression tree that represents a LINQ query.</param>
        /// <returns>The value that results from executing the specified query.</returns>
        public TResult Execute<TResult>(Expression expression)
        {
            return Materializer.Materialize(expression, () => _query.Provider.Execute<TResult>(expression));
        }

        /// <summary>
        /// Asynchronously executes the strongly-typed query represented by a specified expression tree.
        /// </summary>
        /// <typeparam name="TResult">The type of the value that results from executing the query.</typeparam>
        /// <param name="expression">An expression tree that represents a LINQ query.</param>
        /// <param name="cancellationToken">A CancellationToken to observe while waiting for the task to complete.</param>
        /// <returns>A task that represents the asynchronous operation.  The task result contains the value that results from executing the specified query.</returns>
        public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
        {
            var type = typeof(TResult);

            if (_query.Provider is EntityQueryProvider eqProvider)
            {
                if (isTaskOfT(type))
                {
                    var materializeAsyncMethod = _materializeAsyncMethodInfo.MakeGenericMethod(expression.Type);
                    return (TResult)materializeAsyncMethod.Invoke(Materializer, new object[]
                            {
                                expression,
                                new Func<TResult>(() => eqProvider.ExecuteAsync<TResult>(expression, cancellationToken))
                            });
                }

                return Materializer.Materialize(
                             expression,
                             () => eqProvider.ExecuteAsync<TResult>(expression, cancellationToken));
            }

            if (isTaskOfT(type))
            {
                var result = Execute(expression);
                var taskFromResultMethod = _fromResultMethodInfo.MakeGenericMethod(expression.Type);
                return (TResult)taskFromResultMethod.Invoke(null, new[] { result });
            }

            return Execute<TResult>(expression);
        }

        private static bool isTaskOfT(Type type)
        {
            return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>);
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFCachedQueryable.cs
================================================
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using EFSecondLevelCache.Core.Contracts;
using Microsoft.EntityFrameworkCore.Query.Internal;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Provides functionality to evaluate queries against a specific data source.
    /// </summary>
    /// <typeparam name="TType">Type of the entity.</typeparam>
    public class EFCachedQueryable<TType> : IOrderedQueryable<TType>, IAsyncEnumerable<TType>
    {
        private readonly EFCachedQueryProvider _provider;

        /// <summary>
        /// Provides functionality to evaluate queries against a specific data source.
        /// </summary>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <param name="cacheKeyProvider">Gets an EF query and returns its hash to store in the cache.</param>
        /// <param name="cacheServiceProvider">Cache Service Provider.</param>
        public EFCachedQueryable(
            IQueryable<TType> query,
            EFCachePolicy cachePolicy,
            EFCacheDebugInfo debugInfo,
            IEFCacheKeyProvider cacheKeyProvider,
            IEFCacheServiceProvider cacheServiceProvider)
        {
            CachePolicy = cachePolicy;
            DebugInfo = debugInfo;
            CacheKeyProvider = cacheKeyProvider;
            CacheServiceProvider = cacheServiceProvider;
            Query = query.MarkAsNoTracking();
            _provider = new EFCachedQueryProvider(Query, cachePolicy, debugInfo, cacheKeyProvider, cacheServiceProvider);
        }

        /// <summary>
        /// Asynchronous version of the IEnumerable interface
        /// </summary>
        public IAsyncEnumerable<TType> AsyncEnumerable => new EFAsyncEnumerable<TType>(this.AsEnumerable().GetEnumerator());

        /// <summary>
        /// Gets an EF query and returns its hash to store in the cache.
        /// </summary>
        public IEFCacheKeyProvider CacheKeyProvider { get; }

        /// <summary>
        /// Cache Service Provider.
        /// </summary>
        public IEFCacheServiceProvider CacheServiceProvider { get; }

        /// <summary>
        /// Stores the debug information of the caching process.
        /// </summary>
        public EFCacheDebugInfo DebugInfo { get; }

        /// <summary>
        /// Gets the type of the element(s) that are returned when the expression tree associated with this instance of System.Linq.IQueryable is executed.
        /// </summary>
        public Type ElementType => Query.ElementType;

        /// <summary>
        /// Gets the expression tree that is associated with the instance of System.Linq.IQueryable.
        /// </summary>
        public Expression Expression => Query.Expression;

        /// <summary>
        /// Gets the query provider that is associated with this data source.
        /// </summary>
        public IQueryProvider Provider => _provider;

        /// <summary>
        /// The input EF query.
        /// </summary>
        public IQueryable<TType> Query { get; }

        /// <summary>
        /// Defines the expiration mode of the cache item.
        /// </summary>
        public EFCachePolicy CachePolicy { get; }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>A collections that can be used to iterate through the collection.</returns>
        public IEnumerator GetEnumerator()
        {
            return ((IEnumerable)_provider.Materializer.Materialize(
                               Query.Expression,
                               () => Query.ToArray())).GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>A collections that can be used to iterate through the collection.</returns>
        IEnumerator<TType> IEnumerable<TType>.GetEnumerator()
        {
            return ((IEnumerable<TType>)_provider.Materializer.Materialize(
                                   Query.Expression,
                                   () => Query.ToArray())).GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>A collections that can be used to iterate through the collection.</returns>
        public IAsyncEnumerator<TType> GetAsyncEnumerator(CancellationToken cancellationToken)
        {
            return new EFAsyncEnumerator<TType>(
                     ((IEnumerable<TType>)_provider.Materializer.Materialize(
                                        Query.Expression,
                                        () => Query.ToArray())).GetEnumerator());
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFChangeTrackerExtensions.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Change Tracker Extensions
    /// </summary>
    public static class EFChangeTrackerExtensions
    {
        private static readonly MethodInfo _asNoTrackingMethodInfo =
            typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(EntityFrameworkQueryableExtensions.AsNoTracking));

        /// <summary>
        /// Find the base types of the given type, recursively.
        /// </summary>
        public static IEnumerable<Type> GetBaseTypes(this Type type)
        {
            if (type.GetTypeInfo().BaseType == null)
            {
                return type.GetInterfaces();
            }

            return Enumerable.Repeat(type.GetTypeInfo().BaseType, 1)
                             .Concat(type.GetInterfaces())
                             .Concat(type.GetInterfaces().SelectMany(GetBaseTypes))
                             .Concat(type.GetTypeInfo().BaseType.GetBaseTypes());
        }

        /// <summary>
        /// Using the ChangeTracker to find names of the changed entities.
        /// It calls ChangeTracker.DetectChanges() explicitly.
        /// </summary>
        public static string[] GetChangedEntityNames(this DbContext dbContext)
        {
            var typesList = new List<Type>();
            foreach (var type in dbContext.GetChangedEntityTypes())
            {
                typesList.Add(type);
                typesList.AddRange(type.GetBaseTypes().Where(t => t != typeof(object)).ToList());
            }

            var changedEntityNames = typesList
                .Select(type => type.FullName)
                .Distinct()
                .ToArray();

            return changedEntityNames;
        }

        /// <summary>
        /// Checks for changes to the entity and all owns entities.
        /// </summary>
        private static bool IsEntityChanged(EntityEntry entry)
        {
            return entry.State == EntityState.Added
                   || entry.State == EntityState.Modified
                   || entry.State == EntityState.Deleted
                   || entry.References.Any(r => r.TargetEntry != null
                                                && r.TargetEntry.Metadata.IsOwned()
                                                && IsEntityChanged(r.TargetEntry));
        }

        /// <summary>
        /// Using the ChangeTracker to find types of the changed entities.
        /// It calls ChangeTracker.DetectChanges() explicitly.
        /// </summary>
        public static IEnumerable<Type> GetChangedEntityTypes(this DbContext dbContext)
        {
            if (!dbContext.ChangeTracker.AutoDetectChangesEnabled)
            {
                // ChangeTracker.Entries() only calls `Try`DetectChanges() behind the scene.
                dbContext.ChangeTracker.DetectChanges();
            }

            return dbContext.ChangeTracker.Entries()
                .Where(IsEntityChanged)
                .Select(dbEntityEntry => dbEntityEntry.Entity.GetType());
        }

        /// <summary>
        /// Applies the AsNoTracking method dynamically
        /// </summary>
        public static IQueryable<TType> MarkAsNoTracking<TType>(this IQueryable<TType> query)
        {
            if (typeof(TType).GetTypeInfo().IsClass)
            {
                return query.Provider.CreateQuery<TType>(
                    Expression.Call(null, _asNoTrackingMethodInfo.MakeGenericMethod(typeof(TType)), query.Expression));
            }
            return query;
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFMaterializer.cs
================================================
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using EFSecondLevelCache.Core.Contracts;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Cache Result Container
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CacheResult<T>
    {
        /// <summary>
        /// Could read from the cache?
        /// </summary>
        public bool CanRead { set; get; }

        /// <summary>
        /// EFCacheKey value
        /// </summary>
        public EFCacheKey CacheKey { set; get; }

        /// <summary>
        ///  Retrieved result from the cache
        /// </summary>
        public T Result { set; get; }

        /// <summary>
        /// Cache Result Container
        /// </summary>
        public CacheResult()
        {
        }

        /// <summary>
        /// Cache Result Container
        /// </summary>
        /// <param name="canRead">Could read from the cache?</param>
        /// <param name="cacheKey">EFCacheKey value</param>
        /// <param name="result">Retrieved result from the cache</param>
        public CacheResult(bool canRead, EFCacheKey cacheKey, T result)
        {
            CanRead = canRead;
            CacheKey = cacheKey;
            Result = result;
        }
    }

    /// <summary>
    /// Defines methods to create and execute queries that are described by an System.Linq.IQueryable object.
    /// </summary>
    public class EFMaterializer
    {
        private readonly IEFCacheKeyProvider _cacheKeyProvider;
        private readonly IEFCacheServiceProvider _cacheServiceProvider;
        private readonly EFCacheDebugInfo _debugInfo;
        private readonly EFCachePolicy _cachePolicy;
        private readonly IQueryable _query;
        private static readonly object _syncLock = new object();
        private static readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);


        /// <summary>
        /// Defines methods to create and execute queries that are described by an System.Linq.IQueryable object.
        /// </summary>
        /// <param name="query">The input EF query.</param>
        /// <param name="cachePolicy">Defines the expiration mode of the cache item.</param>
        /// <param name="debugInfo">Stores the debug information of the caching process.</param>
        /// <param name="cacheKeyProvider">Gets an EF query and returns its hash to store in the cache.</param>
        /// <param name="cacheServiceProvider">The Cache Service Provider.</param>
        public EFMaterializer(
            IQueryable query,
            EFCachePolicy cachePolicy,
            EFCacheDebugInfo debugInfo,
            IEFCacheKeyProvider cacheKeyProvider,
            IEFCacheServiceProvider cacheServiceProvider)
        {
            _query = query;
            _cachePolicy = cachePolicy;
            _debugInfo = debugInfo;
            _cacheKeyProvider = cacheKeyProvider;
            _cacheServiceProvider = cacheServiceProvider;
        }

        /// <summary>
        /// Executes the query represented by a specified expression tree to cache its results.
        /// </summary>
        /// <param name="expression">An expression tree that represents a LINQ query.</param>
        /// <param name="materializer">How to run the query.</param>
        /// <returns>The value that results from executing the specified query.</returns>
        public async Task<T> MaterializeAsync<T>(Expression expression, Func<Task<T>> materializer)
        {
            await _semaphoreSlim.WaitAsync();
            try
            {
                var cacheResult = readFromCache<T>(expression);
                if (cacheResult.CanRead)
                {
                    return cacheResult.Result;
                }

                var result = await materializer();
                _cacheServiceProvider.InsertValue(cacheResult.CacheKey.KeyHash, result,
                    cacheResult.CacheKey.CacheDependencies, _cachePolicy);
                return result;
            }
            finally
            {
                _semaphoreSlim.Release();
            }
        }

        /// <summary>
        /// Executes the query represented by a specified expression tree to cache its results.
        /// </summary>
        /// <param name="expression">An expression tree that represents a LINQ query.</param>
        /// <param name="materializer">How to run the query.</param>
        /// <returns>The value that results from executing the specified query.</returns>
        public T Materialize<T>(Expression expression, Func<T> materializer)
        {
            lock (_syncLock)
            {
                var cacheResult = readFromCache<T>(expression);
                if (cacheResult.CanRead)
                {
                    return cacheResult.Result;
                }

                var result = materializer();
                _cacheServiceProvider.InsertValue(cacheResult.CacheKey.KeyHash, result,
                    cacheResult.CacheKey.CacheDependencies, _cachePolicy);
                return result;
            }
        }

        private CacheResult<T> readFromCache<T>(Expression expression)
        {
            var cacheKey = _cacheKeyProvider.GetEFCacheKey(_query, expression, _cachePolicy?.SaltKey);
            _debugInfo.EFCacheKey = cacheKey;
            var queryCacheKey = cacheKey.KeyHash;
            var result = _cacheServiceProvider.GetValue(queryCacheKey);
            if (Equals(result, _cacheServiceProvider.NullObject))
            {
                _debugInfo.IsCacheHit = true;
                return new CacheResult<T>(true, cacheKey, default);
            }

            if (result != null)
            {
                _debugInfo.IsCacheHit = true;
                return new CacheResult<T>(true, cacheKey, (T)result);
            }

            return new CacheResult<T>(false, cacheKey, default);
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFQueryExpressionVisitor.cs
================================================
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using EFSecondLevelCache.Core.Contracts;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Ref. https://github.com/dotnet/corefx/ -> src/System.Linq.Expressions/src/System/Linq/Expressions/DebugViewWriter.cs
    /// </summary>
    public class EFQueryExpressionVisitor : ExpressionVisitor
    {
        private const int MaxColumn = 120;

        private const int Tab = 4;

        private readonly TextWriter _out;
        private readonly Stack<int> _stack = new Stack<int>();
        private readonly HashSet<string> _types = new HashSet<string>();
        private int _column;

        private Flow _flow;

        // Associate every unique anonymous LabelTarget in the tree with an integer.
        // The id is used to create a name for the anonymous LabelTarget.
        //
        private Dictionary<LabelTarget, int> _labelIds;

        // Associate every unique anonymous LambdaExpression in the tree with an integer.
        // The id is used to create a name for the anonymous lambda.
        //
        private Dictionary<LambdaExpression, int> _lambdaIds;

        // All the unique lambda expressions in the ET, will be used for displaying all
        // the lambda definitions.
        private Queue<LambdaExpression> _lambdas;
        // Associate every unique anonymous parameter or variable in the tree with an integer.
        // The id is used to create a name for the anonymous parameter or variable.
        //
        private Dictionary<ParameterExpression, int> _paramIds;
        private EFQueryExpressionVisitor(TextWriter file)
        {
            _out = file;
        }

        [Flags]
        private enum Flow
        {
            None,
            Space,
            NewLine,

            Break = 0x8000      // newline if column > MaxColumn
        }

        private int Base => _stack.Count > 0 ? _stack.Peek() : 0;

        private int Delta { get; set; }

        private int Depth => Base + Delta;

        /// <summary>
        /// Write out the given AST
        /// </summary>
        public static EFQueryDebugView GetDebugView(Expression node)
        {
            using (var writer = new StringWriter(CultureInfo.CurrentCulture))
            {
                var efQueryExpressionVisitor = new EFQueryExpressionVisitor(writer);
                efQueryExpressionVisitor.writeTo(node);
                var types = efQueryExpressionVisitor._types;
                return new EFQueryDebugView { DebugView = writer.ToString(), Types = types };
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitBinary(BinaryExpression node)
        {
            if (node.NodeType == ExpressionType.ArrayIndex)
            {
                parenthesizedVisit(node, node.Left);
                Out("[");
                Visit(node.Right);
                Out("]");
            }
            else
            {
                bool parenthesizeLeft = needsParentheses(node, node.Left);
                bool parenthesizeRight = needsParentheses(node, node.Right);

                string op;
                bool isChecked = false;
                Flow beforeOp = Flow.Space;
                switch (node.NodeType)
                {
                    case ExpressionType.Assign: op = "="; break;
                    case ExpressionType.Equal: op = "=="; break;
                    case ExpressionType.NotEqual: op = "!="; break;
                    case ExpressionType.AndAlso: op = "&&"; beforeOp = Flow.Break | Flow.Space; break;
                    case ExpressionType.OrElse: op = "||"; beforeOp = Flow.Break | Flow.Space; break;
                    case ExpressionType.GreaterThan: op = ">"; break;
                    case ExpressionType.LessThan: op = "<"; break;
                    case ExpressionType.GreaterThanOrEqual: op = ">="; break;
                    case ExpressionType.LessThanOrEqual: op = "<="; break;
                    case ExpressionType.Add: op = "+"; break;
                    case ExpressionType.AddAssign: op = "+="; break;
                    case ExpressionType.AddAssignChecked: op = "+="; isChecked = true; break;
                    case ExpressionType.AddChecked: op = "+"; isChecked = true; break;
                    case ExpressionType.Subtract: op = "-"; break;
                    case ExpressionType.SubtractAssign: op = "-="; break;
                    case ExpressionType.SubtractAssignChecked: op = "-="; isChecked = true; break;
                    case ExpressionType.SubtractChecked: op = "-"; isChecked = true; break;
                    case ExpressionType.Divide: op = "/"; break;
                    case ExpressionType.DivideAssign: op = "/="; break;
                    case ExpressionType.Modulo: op = "%"; break;
                    case ExpressionType.ModuloAssign: op = "%="; break;
                    case ExpressionType.Multiply: op = "*"; break;
                    case ExpressionType.MultiplyAssign: op = "*="; break;
                    case ExpressionType.MultiplyAssignChecked: op = "*="; isChecked = true; break;
                    case ExpressionType.MultiplyChecked: op = "*"; isChecked = true; break;
                    case ExpressionType.LeftShift: op = "<<"; break;
                    case ExpressionType.LeftShiftAssign: op = "<<="; break;
                    case ExpressionType.RightShift: op = ">>"; break;
                    case ExpressionType.RightShiftAssign: op = ">>="; break;
                    case ExpressionType.And: op = "&"; break;
                    case ExpressionType.AndAssign: op = "&="; break;
                    case ExpressionType.Or: op = "|"; break;
                    case ExpressionType.OrAssign: op = "|="; break;
                    case ExpressionType.ExclusiveOr: op = "^"; break;
                    case ExpressionType.ExclusiveOrAssign: op = "^="; break;
                    case ExpressionType.Power: op = "**"; break;
                    case ExpressionType.PowerAssign: op = "**="; break;
                    case ExpressionType.Coalesce: op = "??"; break;

                    default:
                        throw new InvalidOperationException();
                }

                if (parenthesizeLeft)
                {
                    Out("(", Flow.None);
                }

                Visit(node.Left);
                if (parenthesizeLeft)
                {
                    Out(Flow.None, ")", Flow.Break);
                }

                // prepend # to the operator to represent checked op
                if (isChecked)
                {
                    op = string.Format(
                            CultureInfo.CurrentCulture,
                            "#{0}",
                            op
                    );
                }
                Out(beforeOp, op, Flow.Space | Flow.Break);

                if (parenthesizeRight)
                {
                    Out("(", Flow.None);
                }
                Visit(node.Right);
                if (parenthesizeRight)
                {
                    Out(Flow.None, ")", Flow.Break);
                }
            }
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitBlock(BlockExpression node)
        {
            Out(".Block");

            Out(string.Format(CultureInfo.CurrentCulture, "«{0}»", node.Type));
            addType(node.Type);

            visitDeclarations(node.Variables);
            Out(" ");
            // Use ; to separate expressions in the block
            visitExpressions('{', ';', node.Expressions);

            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override CatchBlock VisitCatchBlock(CatchBlock node)
        {
            Out(Flow.NewLine, $"}} .Catch ({node.Test}");
            if (node.Variable != null)
            {
                Out(Flow.Space, "");
                VisitParameter(node.Variable);
            }
            if (node.Filter != null)
            {
                Out(") .If (", Flow.Break);
                Visit(node.Filter);
            }
            Out(") {", Flow.NewLine);
            indent();
            Visit(node.Body);
            dedent();
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitConditional(ConditionalExpression node)
        {
            if (isSimpleExpression(node.Test))
            {
                Out(".If (");
                Visit(node.Test);
                Out(") {", Flow.NewLine);
            }
            else
            {
                Out(".If (", Flow.NewLine);
                indent();
                Visit(node.Test);
                dedent();
                Out(Flow.NewLine, ") {", Flow.NewLine);
            }
            indent();
            Visit(node.IfTrue);
            dedent();
            Out(Flow.NewLine, "} .Else {", Flow.NewLine);
            indent();
            Visit(node.IfFalse);
            dedent();
            Out(Flow.NewLine, "}");
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitConstant(ConstantExpression node)
        {
            printConstant(node, node.Value);
            return node;
        }

        private void printConstant(ConstantExpression node, object value)
        {
            if (value == null)
            {
                Out("null");
            }
            else if ((value is string) && node.Type == typeof(string))
            {
                Out(string.Format(
                    CultureInfo.CurrentCulture,
                    "\"{0}\"",
                    value));
            }
            else if ((value is char) && node.Type == typeof(char))
            {
                Out(string.Format(
                    CultureInfo.CurrentCulture,
                    "'{0}'",
                    value));
            }
            else if (((value is int) && node.Type == typeof(int))
              || ((value is bool) && node.Type == typeof(bool)))
            {
                Out(value.ToString());
            }
            else if (typeof(IEnumerable).IsAssignableFrom(value.GetType())
                        && !node.Type.ToString().Contains("Microsoft"))
            {
                Out($".Constant<{node.Type}>(new[]{{{string.Join(", ", ((IEnumerable)value).Cast<object>().ToList())}}})");
            }
            else
            {
                string suffix = getConstantValueSuffix(node.Type);
                if (suffix != null)
                {
                    Out(value.ToString());
                    Out(suffix);
                }
                else
                {
                    Out(string.Format(
                        CultureInfo.CurrentCulture,
                        ".Constant<{0}>({1})",
                        node.Type,
                        value));
                    addType(node.Type);
                }
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitDebugInfo(DebugInfoExpression node)
        {
            Out(string.Format(
                CultureInfo.CurrentCulture,
                ".DebugInfo({0}: {1}, {2} - {3}, {4})",
                node.Document.FileName,
                node.StartLine,
                node.StartColumn,
                node.EndLine,
                node.EndColumn)
            );
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitDefault(DefaultExpression node)
        {
            Out($".Default({node.Type})");
            addType(node.Type);
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override ElementInit VisitElementInit(ElementInit node)
        {
            if (node.Arguments.Count == 1)
            {
                Visit(node.Arguments[0]);
            }
            else
            {
                visitExpressions('{', node.Arguments);
            }
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitExtension(Expression node)
        {
            Out(string.Format(CultureInfo.CurrentCulture, ".Extension<{0}>", node.GetType()));

            if (node.CanReduce)
            {
                Out(Flow.Space, "{", Flow.NewLine);
                indent();
                Visit(node.Reduce());
                dedent();
                Out(Flow.NewLine, "}");
            }

            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitGoto(GotoExpression node)
        {
            Out($".{node.Kind}", Flow.Space);
            Out(getLabelTargetName(node.Target), Flow.Space);
            Out("{", Flow.Space);
            Visit(node.Value);
            Out(Flow.Space, "}");
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitIndex(IndexExpression node)
        {
            if (node.Indexer != null)
            {
                outMember(node, node.Object, node.Indexer);
            }
            else
            {
                parenthesizedVisit(node, node.Object);
            }

            visitExpressions('[', node.Arguments);
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitInvocation(InvocationExpression node)
        {
            Out(".Invoke ");
            parenthesizedVisit(node, node.Expression);
            visitExpressions('(', node.Arguments);
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitLabel(LabelExpression node)
        {
            Out(".Label", Flow.NewLine);
            indent();
            Visit(node.DefaultValue);
            dedent();
            newLine();
            dumpLabel(node.Target);
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitLambda<T>(Expression<T> node)
        {
            Out(
                string.Format(CultureInfo.CurrentCulture,
                    "{0} {1}<{2}>",
                    ".Lambda",
                    getLambdaName(node),
                    node.Type
                )
            );
            addType(node.Type);

            if (_lambdas == null)
            {
                _lambdas = new Queue<LambdaExpression>();
            }

            // N^2 performance, for keeping the order of the lambdas.
            if (!_lambdas.Contains(node))
            {
                _lambdas.Enqueue(node);
            }

            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitListInit(ListInitExpression node)
        {
            Visit(node.NewExpression);
            visitExpressions('{', ',', node.Initializers, e => VisitElementInit(e));
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitLoop(LoopExpression node)
        {
            Out(".Loop", Flow.Space);
            if (node.ContinueLabel != null)
            {
                dumpLabel(node.ContinueLabel);
            }
            Out(" {", Flow.NewLine);
            indent();
            Visit(node.Body);
            dedent();
            Out(Flow.NewLine, "}");
            if (node.BreakLabel != null)
            {
                Out("", Flow.NewLine);
                dumpLabel(node.BreakLabel);
            }
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitMember(MemberExpression node)
        {
            outMember(node, node.Expression, node.Member);
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="assignment"></param>
        /// <returns></returns>
        protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment)
        {
            Out(assignment.Member.Name);
            Out(Flow.Space, "=", Flow.Space);
            Visit(assignment.Expression);
            return assignment;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitMemberInit(MemberInitExpression node)
        {
            Visit(node.NewExpression);
            visitExpressions('{', ',', node.Bindings, e => VisitMemberBinding(e));
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="binding"></param>
        /// <returns></returns>
        protected override MemberListBinding VisitMemberListBinding(MemberListBinding binding)
        {
            Out(binding.Member.Name);
            Out(Flow.Space, "=", Flow.Space);
            visitExpressions('{', ',', binding.Initializers, e => VisitElementInit(e));
            return binding;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="binding"></param>
        /// <returns></returns>
        protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding)
        {
            Out(binding.Member.Name);
            Out(Flow.Space, "=", Flow.Space);
            visitExpressions('{', ',', binding.Bindings, e => VisitMemberBinding(e));
            return binding;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            Out(".Call ");
            if (node.Object != null)
            {
                parenthesizedVisit(node, node.Object);
            }
            else if (node.Method.DeclaringType != null)
            {
                Out(node.Method.DeclaringType.ToString());
            }
            else
            {
                Out("<UnknownType>");
            }
            Out(".");
            Out(node.Method.Name);
            visitExpressions('(', node.Arguments);
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitNew(NewExpression node)
        {
            Out($".New {node.Type}");
            addType(node.Type);
            visitExpressions('(', node.Arguments);
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitNewArray(NewArrayExpression node)
        {
            if (node.NodeType == ExpressionType.NewArrayBounds)
            {
                // .NewArray MyType[expr1, expr2]
                Out($".NewArray {node.Type.GetElementType()}");
                addType(node.Type);
                visitExpressions('[', node.Expressions);
            }
            else
            {
                // .NewArray MyType {expr1, expr2}
                Out($".NewArray {node.Type}", Flow.Space);
                addType(node.Type);
                visitExpressions('{', node.Expressions);
            }
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitParameter(ParameterExpression node)
        {
            // Have '$' for the DebugView of ParameterExpressions
            Out("$");
            if (string.IsNullOrEmpty(node.Name))
            {
                // If no name if provided, generate a name as $var1, $var2.
                // No guarantee for not having name conflicts with user provided variable names.
                //
                int id = getParamId(node);
                Out($"var{id}");
            }
            else
            {
                Out(getDisplayName(node.Name));
            }
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node)
        {
            Out(".RuntimeVariables");
            visitExpressions('(', node.Variables);
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitSwitch(SwitchExpression node)
        {
            Out(".Switch ");
            Out("(");
            Visit(node.SwitchValue);
            Out(") {", Flow.NewLine);
            Visit(node.Cases, VisitSwitchCase);
            if (node.DefaultBody != null)
            {
                Out(".Default:", Flow.NewLine);
                indent(); indent();
                Visit(node.DefaultBody);
                dedent(); dedent();
                newLine();
            }
            Out("}");
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override SwitchCase VisitSwitchCase(SwitchCase node)
        {
            foreach (var test in node.TestValues)
            {
                Out(".Case (");
                Visit(test);
                Out("):", Flow.NewLine);
            }
            indent(); indent();
            Visit(node.Body);
            dedent(); dedent();
            newLine();
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitTry(TryExpression node)
        {
            Out(".Try {", Flow.NewLine);
            indent();
            Visit(node.Body);
            dedent();
            Visit(node.Handlers, VisitCatchBlock);
            if (node.Finally != null)
            {
                Out(Flow.NewLine, "} .Finally {", Flow.NewLine);
                indent();
                Visit(node.Finally);
                dedent();
            }
            else if (node.Fault != null)
            {
                Out(Flow.NewLine, "} .Fault {", Flow.NewLine);
                indent();
                Visit(node.Fault);
                dedent();
            }

            Out(Flow.NewLine, "}");
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitTypeBinary(TypeBinaryExpression node)
        {
            parenthesizedVisit(node, node.Expression);
            switch (node.NodeType)
            {
                case ExpressionType.TypeIs:
                    Out(Flow.Space, ".Is", Flow.Space);
                    break;
                case ExpressionType.TypeEqual:
                    Out(Flow.Space, ".TypeEqual", Flow.Space);
                    break;
            }
            Out(node.TypeOperand.ToString());
            return node;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        protected override Expression VisitUnary(UnaryExpression node)
        {
            switch (node.NodeType)
            {
                case ExpressionType.Convert:
                    Out($"({node.Type})");
                    addType(node.Type);
                    break;
                case ExpressionType.ConvertChecked:
                    Out($"#({node.Type})");
                    addType(node.Type);
                    break;
                case ExpressionType.TypeAs:
                    break;
                case ExpressionType.Not:
                    Out(node.Type == typeof(bool) ? "!" : "~");
                    break;
                case ExpressionType.OnesComplement:
                    Out("~");
                    break;
                case ExpressionType.Negate:
                    Out("-");
                    break;
                case ExpressionType.NegateChecked:
                    Out("#-");
                    break;
                case ExpressionType.UnaryPlus:
                    Out("+");
                    break;
                case ExpressionType.ArrayLength:
                    break;
                case ExpressionType.Quote:
                    Out("'");
                    break;
                case ExpressionType.Throw:
                    if (node.Operand == null)
                    {
                        Out(".Rethrow");
                    }
                    else
                    {
                        Out(".Throw", Flow.Space);
                    }
                    break;
                case ExpressionType.IsFalse:
                    Out(".IsFalse");
                    break;
                case ExpressionType.IsTrue:
                    Out(".IsTrue");
                    break;
                case ExpressionType.Decrement:
                    Out(".Decrement");
                    break;
                case ExpressionType.Increment:
                    Out(".Increment");
                    break;
                case ExpressionType.PreDecrementAssign:
                    Out("--");
                    break;
                case ExpressionType.PreIncrementAssign:
                    Out("++");
                    break;
                case ExpressionType.Unbox:
                    Out(".Unbox");
                    break;
            }

            parenthesizedVisit(node, node.Operand);

            switch (node.NodeType)
            {
                case ExpressionType.TypeAs:
                    Out(Flow.Space, ".As", Flow.Space | Flow.Break);
                    Out(node.Type.ToString());
                    addType(node.Type);
                    break;

                case ExpressionType.ArrayLength:
                    Out(".Length");
                    break;

                case ExpressionType.PostDecrementAssign:
                    Out("--");
                    break;

                case ExpressionType.PostIncrementAssign:
                    Out("++");
                    break;
            }
            return node;
        }

        /// <summary>
        /// Return true if the input string contains any whitespace character.
        /// Otherwise false.
        /// </summary>
        private static bool containsWhiteSpace(string name)
        {
            foreach (char c in name)
            {
                if (char.IsWhiteSpace(c))
                {
                    return true;
                }
            }
            return false;
        }

        private static string getConstantValueSuffix(Type type)
        {
            if (type == typeof(uint))
            {
                return "U";
            }
            if (type == typeof(long))
            {
                return "L";
            }
            if (type == typeof(ulong))
            {
                return "UL";
            }
            if (type == typeof(double))
            {
                return "D";
            }
            if (type == typeof(float))
            {
                return "F";
            }
            if (type == typeof(decimal))
            {
                return "M";
            }
            return null;
        }

        private static string getDisplayName(string name)
        {
            if (containsWhiteSpace(name))
            {
                // if name has whitespace in it, quote it
                return quoteName(name);
            }
            else
            {
                return name;
            }
        }

        private static int getId<T>(T e, ref Dictionary<T, int> ids)
        {
            if (ids == null)
            {
                ids = new Dictionary<T, int> { { e, 1 } };
                return 1;
            }
            else
            {
                if (!ids.TryGetValue(e, out int id))
                {
                    // e is met the first time
                    id = ids.Count + 1;
                    ids.Add(e, id);
                }
                return id;
            }
        }

        // the greater the higher

        private static int getOperatorPrecedence(Expression node)
        {
            // Roughly matches C# operator precedence, with some additional
            // operators. Also things which are not binary/unary expressions,
            // such as conditional and type testing, don't use this mechanism.
            switch (node.NodeType)
            {
                // Assignment
                case ExpressionType.Assign:
                case ExpressionType.ExclusiveOrAssign:
                case ExpressionType.AddAssign:
                case ExpressionType.AddAssignChecked:
                case ExpressionType.SubtractAssign:
                case ExpressionType.SubtractAssignChecked:
                case ExpressionType.DivideAssign:
                case ExpressionType.ModuloAssign:
                case ExpressionType.MultiplyAssign:
                case ExpressionType.MultiplyAssignChecked:
                case ExpressionType.LeftShiftAssign:
                case ExpressionType.RightShiftAssign:
                case ExpressionType.AndAssign:
                case ExpressionType.OrAssign:
                case ExpressionType.PowerAssign:
                case ExpressionType.Coalesce:
                    return 1;

                // Conditional (?:) would go here

                // Conditional OR
                case ExpressionType.OrElse:
                    return 2;

                // Conditional AND
                case ExpressionType.AndAlso:
                    return 3;

                // Logical OR
                case ExpressionType.Or:
                    return 4;

                // Logical XOR
                case ExpressionType.ExclusiveOr:
                    return 5;

                // Logical AND
                case ExpressionType.And:
                    return 6;

                // Equality
                case ExpressionType.Equal:
                case ExpressionType.NotEqual:
                    return 7;

                // Relational, type testing
                case ExpressionType.GreaterThan:
                case ExpressionType.LessThan:
                case ExpressionType.GreaterThanOrEqual:
                case ExpressionType.LessThanOrEqual:
                case ExpressionType.TypeAs:
                case ExpressionType.TypeIs:
                case ExpressionType.TypeEqual:
                    return 8;

                // Shift
                case ExpressionType.LeftShift:
                case ExpressionType.RightShift:
                    return 9;

                // Additive
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                    return 10;

                // Multiplicative
                case ExpressionType.Divide:
                case ExpressionType.Modulo:
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                    return 11;

                // Unary
                case ExpressionType.Negate:
                case ExpressionType.NegateChecked:
                case ExpressionType.UnaryPlus:
                case ExpressionType.Not:
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked:
                case ExpressionType.PreIncrementAssign:
                case ExpressionType.PreDecrementAssign:
                case ExpressionType.OnesComplement:
                case ExpressionType.Increment:
                case ExpressionType.Decrement:
                case ExpressionType.IsTrue:
                case ExpressionType.IsFalse:
                case ExpressionType.Unbox:
                case ExpressionType.Throw:
                    return 12;

                // Power, which is not in C#
                // But VB/Python/Ruby put it here, above unary.
                case ExpressionType.Power:
                    return 13;

                // Primary, which includes all other node types:
                //   member access, calls, indexing, new.
                case ExpressionType.PostIncrementAssign:
                case ExpressionType.PostDecrementAssign:
                default:
                    return 14;

                // These aren't expressions, so never need parentheses:
                //   constants, variables
                case ExpressionType.Constant:
                case ExpressionType.Parameter:
                    return 15;
            }
        }

        private static bool isSimpleExpression(Expression node)
        {
            if (node is BinaryExpression binary)
            {
                return !(binary.Left is BinaryExpression || binary.Right is BinaryExpression);
            }

            return false;
        }


        private static bool needsParentheses(Expression parent, Expression child)
        {
            Debug.Assert(parent != null);
            if (child == null)
            {
                return false;
            }

            // Some nodes always have parentheses because of how they are
            // displayed, for example: ".Unbox(obj.Foo)"
            switch (parent.NodeType)
            {
                case ExpressionType.Increment:
                case ExpressionType.Decrement:
                case ExpressionType.IsTrue:
                case ExpressionType.IsFalse:
                case ExpressionType.Unbox:
                    return true;
            }

            int childOpPrec = getOperatorPrecedence(child);
            int parentOpPrec = getOperatorPrecedence(parent);

            if (childOpPrec == parentOpPrec)
            {
                // When parent op and child op has the same precedence,
                // we want to be a little conservative to have more clarity.
                // Parentheses are not needed if
                // 1) Both ops are &&, ||, &, |, or ^, all of them are the only
                // op that has the precedence.
                // 2) Parent op is + or *, e.g. x + (y - z) can be simplified to
                // x + y - z.
                // 3) Parent op is -, / or %, and the child is the left operand.
                // In this case, if left and right operand are the same, we don't
                // remove parenthesis, e.g. (x + y) - (x + y)
                //
                switch (parent.NodeType)
                {
                    case ExpressionType.AndAlso:
                    case ExpressionType.OrElse:
                    case ExpressionType.And:
                    case ExpressionType.Or:
                    case ExpressionType.ExclusiveOr:
                        // Since these ops are the only ones on their precedence,
                        // the child op must be the same.
                        Debug.Assert(child.NodeType == parent.NodeType);
                        // We remove the parenthesis, e.g. x && y && z
                        return false;
                    case ExpressionType.Add:
                    case ExpressionType.AddChecked:
                    case ExpressionType.Multiply:
                    case ExpressionType.MultiplyChecked:
                        return false;
                    case ExpressionType.Subtract:
                    case ExpressionType.SubtractChecked:
                    case ExpressionType.Divide:
                    case ExpressionType.Modulo:
                        BinaryExpression binary = parent as BinaryExpression;
                        Debug.Assert(binary != null);
                        // Need to have parenthesis for the right operand.
                        return child == binary.Right;
                }
                return true;
            }

            // Special case: negate of a constant needs parentheses, to
            // disambiguate it from a negative constant.
            if (child.NodeType == ExpressionType.Constant
                && (parent.NodeType == ExpressionType.Negate || parent.NodeType == ExpressionType.NegateChecked))
            {
                return true;
            }

            // If the parent op has higher precedence, need parentheses for the child.
            return childOpPrec < parentOpPrec;
        }

        private static string quoteName(string name)
        {
            return string.Format(CultureInfo.CurrentCulture, "'{0}'", name);
        }

        private Flow checkBreak(Flow flow)
        {
            if ((flow & Flow.Break) != 0)
            {
                if (_column > (MaxColumn + Depth))
                {
                    flow = Flow.NewLine;
                }
                else
                {
                    flow &= ~Flow.Break;
                }
            }
            return flow;
        }

        private void dedent()
        {
            Delta -= Tab;
        }

        private void dumpLabel(LabelTarget target)
        {
            Out(string.Format(CultureInfo.CurrentCulture, ".LabelTarget {0}:", getLabelTargetName(target)));
        }

        private Flow getFlow(Flow flow)
        {
            var last = checkBreak(_flow);
            flow = checkBreak(flow);

            // Get the biggest flow that is requested None < Space < NewLine
            return (Flow)Math.Max((int)last, (int)flow);
        }

        private int getLabelTargetId(LabelTarget target)
        {
            Debug.Assert(string.IsNullOrEmpty(target.Name));
            return getId(target, ref _labelIds);
        }

        private string getLabelTargetName(LabelTarget target)
        {
            if (string.IsNullOrEmpty(target.Name))
            {
                // Create the label target name as #Label1, #Label2, etc.
                return string.Format(CultureInfo.CurrentCulture, "#Label{0}", getLabelTargetId(target));
            }
            else
            {
                return getDisplayName(target.Name);
            }
        }

        private int getLambdaId(LambdaExpression le)
        {
            Debug.Assert(string.IsNullOrEmpty(le.Name));
            return getId(le, ref _lambdaIds);
        }

        private string getLambdaName(LambdaExpression lambda)
        {
            if (string.IsNullOrEmpty(lambda.Name))
            {
                return $"#Lambda{getLambdaId(lambda)}";
            }
            return getDisplayName(lambda.Name);
        }

        private int getParamId(ParameterExpression p)
        {
            Debug.Assert(string.IsNullOrEmpty(p.Name));
            return getId(p, ref _paramIds);
        }

        private void indent()
        {
            Delta += Tab;
        }

        private void newLine()
        {
            _flow = Flow.NewLine;
        }

        private void Out(string s)
        {
            Out(Flow.None, s, Flow.None);
        }

        private void Out(Flow before, string s)
        {
            Out(before, s, Flow.None);
        }

        private void Out(string s, Flow after)
        {
            Out(Flow.None, s, after);
        }

        private void Out(Flow before, string s, Flow after)
        {
            switch (getFlow(before))
            {
                case Flow.None:
                    break;
                case Flow.Space:
                    write(" ");
                    break;
                case Flow.NewLine:
                    writeLine();
                    write(new string(' ', Depth));
                    break;
            }
            write(s);
            _flow = after;
        }

        // Prints ".instanceField" or "declaringType.staticField"
        private void outMember(Expression node, Expression instance, MemberInfo member)
        {
            if (instance != null)
            {
                parenthesizedVisit(node, instance);
                Out($".{member.Name}");

                if (instance is ConstantExpression instanceExp)
                {
                    var memberInfoValue = getMemberInfoValue(member, instanceExp.Value);
                    printConstant(instanceExp, memberInfoValue);
                }
            }
            else
            {
                // For static members, include the type name
                Out($"{member.DeclaringType}.{member.Name}");
            }
        }

        private static object getMemberInfoValue(MemberInfo memberInfo, object forObject)
        {
            var field = memberInfo as FieldInfo;
            if (field != null)
            {
                return field.GetValue(forObject);
            }

            var property = memberInfo as PropertyInfo;
            if (property != null)
            {
                return property.GetValue(forObject, null);
            }

            return null;
        }

        private void parenthesizedVisit(Expression parent, Expression nodeToVisit)
        {
            if (needsParentheses(parent, nodeToVisit))
            {
                Out("(");
                Visit(nodeToVisit);
                Out(")");
            }
            else
            {
                Visit(nodeToVisit);
            }
        }

        private void visitDeclarations(IList<ParameterExpression> expressions)
        {
            visitExpressions('(', ',', expressions, variable =>
            {
                Out(variable.Type.ToString());
                if (variable.IsByRef)
                {
                    Out("&");
                }
                Out(" ");
                VisitParameter(variable);
            });
        }

        private void visitExpressions<T>(char open, IList<T> expressions) where T : Expression
        {
            visitExpressions<T>(open, ',', expressions);
        }

        private void visitExpressions<T>(char open, char separator, IList<T> expressions) where T : Expression
        {
            visitExpressions(open, separator, expressions, e => Visit(e));
        }

        private void visitExpressions<T>(char open, char separator, IList<T> expressions, Action<T> visit)
        {
            Out(open.ToString());

            if (expressions != null)
            {
                indent();
                bool isFirst = true;
                foreach (T e in expressions)
                {
                    if (isFirst)
                    {
                        if (open == '{' || expressions.Count > 1)
                        {
                            newLine();
                        }
                        isFirst = false;
                    }
                    else
                    {
                        Out(separator.ToString(), Flow.NewLine);
                    }
                    visit(e);
                }
                dedent();
            }

            char close;
            switch (open)
            {
                case '(': close = ')'; break;
                case '{': close = '}'; break;
                case '[': close = ']'; break;
                case '<': close = '>'; break;
                default: throw new InvalidOperationException();
            }

            if (open == '{')
            {
                newLine();
            }
            Out(close.ToString(), Flow.Break);
        }

        private void write(string s)
        {
            _out.Write(s);
            _column += s.Length;
        }

        private void writeLambda(LambdaExpression lambda)
        {
            Out(
                string.Format(
                    CultureInfo.CurrentCulture,
                    ".Lambda {0}<{1}>",
                    getLambdaName(lambda),
                    lambda.Type)
            );

            visitDeclarations(lambda.Parameters);

            Out(Flow.Space, "{", Flow.NewLine);
            indent();
            Visit(lambda.Body);
            dedent();
            Out(Flow.NewLine, "}");
            Debug.Assert(_stack.Count == 0);
        }

        private void writeLine()
        {
            _out.WriteLine();
            _column = 0;
        }

        private void writeTo(Expression node)
        {
            if (node is LambdaExpression lambda)
            {
                writeLambda(lambda);
            }
            else
            {
                Visit(node);
                Debug.Assert(_stack.Count == 0);
            }

            //
            // Output all lambda expression definitions.
            // in the order of their appearances in the tree.
            //
            while (_lambdas?.Count > 0)
            {
                writeLine();
                writeLine();
                writeLambda(_lambdas.Dequeue());
            }
        }

        private void addType(Type type)
        {
            var typeInfo = type.GetTypeInfo();
            if (typeInfo.IsGenericType)
            {
                foreach (var genericType in type.GetGenericArguments())
                {
                    var genericTypeInfo = genericType.GetTypeInfo();
                    if (genericTypeInfo.IsGenericType)
                    {
                        addType(genericType);
                    }
                    else
                    {
                        if (genericTypeInfo.IsClass)
                        {
                            var item = genericType.ToString();
                            if (!isCompilerGenerated(item))
                            {
                                _types.Add(item);
                            }
                        }
                    }
                }
            }
            else
            {
                if (typeInfo.IsClass)
                {
                    var item = type.ToString();
                    if (!isCompilerGenerated(item))
                    {
                        _types.Add(item);
                    }
                }
            }
        }

        private bool isCompilerGenerated(string name)
        {
            return name.Contains("c__DisplayClass");
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFSecondLevelCache.Core.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <Description>Entity Framework Core Second Level Caching Library.</Description>
    <VersionPrefix>2.9.1</VersionPrefix>
    <Authors>Vahid Nasiri</Authors>
    <TargetFrameworks>netstandard2.0;net461;</TargetFrameworks>
    <TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netstandard2.0</TargetFrameworks>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <AssemblyName>EFSecondLevelCache.Core</AssemblyName>
    <PackageId>EFSecondLevelCache.Core</PackageId>
    <PackageTags>EntityFramework;Cache;Caching;SecondLevelCache;EFCore;ORM;.NET Core;aspnetcore</PackageTags>
    <PackageProjectUrl>https://github.com/VahidN/EFSecondLevelCache.Core</PackageProjectUrl>
    <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
    <GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
    <GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
    <GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>
  <ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
    <Reference Include="System" />
    <Reference Include="Microsoft.CSharp" />
    <PackageReference Include="CacheManager.Core" Version="1.2.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
  </ItemGroup>
  <PropertyGroup Condition="'$(TargetFramework)' == 'net461'">
    <DefineConstants>NET4_6_1</DefineConstants>
  </PropertyGroup>
  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
    <PackageReference Include="CacheManager.Core" Version="1.2.0" />
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
  </ItemGroup>
  <PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
    <DefineConstants>NETSTANDARD2_0</DefineConstants>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    <PlatformTarget>anycpu</PlatformTarget>
  </PropertyGroup>
</Project>

================================================
FILE: src/EFSecondLevelCache.Core/EFServiceCollectionExtensions.cs
================================================
using System;
using EFSecondLevelCache.Core.Contracts;
using Microsoft.Extensions.DependencyInjection;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// ServiceCollection Extensions
    /// </summary>
    public static class EFServiceCollectionExtensions
    {
        /// <summary>
        /// A collection of service descriptors.
        /// </summary>
        public static IServiceCollection ServiceCollection { get; set; }

        /// <summary>
        /// Registers the required services of the EFSecondLevelCache.Core.
        /// </summary>
        public static IServiceCollection AddEFSecondLevelCache(this IServiceCollection services)
        {
            services.AddSingleton<IEFCacheKeyHashProvider, EFCacheKeyHashProvider>();
            services.AddSingleton<IEFCacheKeyProvider, EFCacheKeyProvider>();
            services.AddSingleton<IEFCacheServiceProvider, EFCacheServiceProvider>();

            ServiceCollection = services;
            return services;
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/EFStaticServiceProvider.cs
================================================
using System;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// A lazy loaded thread-safe singleton App ServiceProvider.
    /// It's required for static `Cacheable()` methods.
    /// </summary>
    public static class EFStaticServiceProvider
    {
        private static readonly Lazy<IServiceProvider> _serviceProviderBuilder =
            new Lazy<IServiceProvider>(getServiceProvider, LazyThreadSafetyMode.ExecutionAndPublication);

        /// <summary>
        /// Defines a mechanism for retrieving a service object.
        /// </summary>
        public static IServiceProvider Instance { get; } = _serviceProviderBuilder.Value;

        private static IServiceProvider getServiceProvider()
        {
            var serviceProvider = EFServiceCollectionExtensions.ServiceCollection?.BuildServiceProvider();
            return serviceProvider ?? throw new InvalidOperationException("Please add `AddEFSecondLevelCache()` method to your `IServiceCollection`.");
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/ParallelExtensions.cs
================================================
using System;
using System.Threading;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// Reader writer locking utils
    /// </summary>
    public static class ParallelExtensions
    {
        /// <summary>
        /// Tries to enter the lock in read mode, with an optional integer time-out.
        /// </summary>
        public static void TryReadLocked(this ReaderWriterLockSlim readerWriterLock,
                                         Action action, int timeout = Timeout.Infinite)
        {
            if (!readerWriterLock.TryEnterReadLock(timeout))
            {
                throw new TimeoutException();
            }
            try
            {
                action();
            }
            finally
            {
                readerWriterLock.ExitReadLock();
            }
        }

        /// <summary>
        /// Tries to enter the lock in write mode, with an optional time-out.
        /// </summary>
        public static void TryWriteLocked(this ReaderWriterLockSlim readerWriterLock,
                                          Action action, int timeout = Timeout.Infinite)
        {
            if (!readerWriterLock.TryEnterWriteLock(timeout))
            {
                throw new TimeoutException();
            }
            try
            {
                action();
            }
            finally
            {
                readerWriterLock.ExitWriteLock();
            }
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/Properties/AssemblyInfo.cs
================================================
using System.Reflection;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("EFSecondLevelCache.Core")]
[assembly: AssemblyTrademark("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("755d6a18-e67a-4e14-8289-bd33a901c52b")]


================================================
FILE: src/EFSecondLevelCache.Core/XxHashUnsafe.cs
================================================

using System;

namespace EFSecondLevelCache.Core
{
    /// <summary>
    /// xxHash is an extremely fast non-cryptographic Hash algorithm, working at speeds close to RAM limits.
    /// http://code.google.com/p/xxhash/
    /// </summary>
    public static class XxHashUnsafe
    {
        private const uint Prime1 = 2654435761U;
        private const uint Prime2 = 2246822519U;
        private const uint Prime3 = 3266489917U;
        private const uint Prime4 = 668265263U;
        private const int Prime5 = 0x165667b1;
        private const uint Seed = 0xc58f1a7b;

        /// <summary>
        /// Computes the xxHash of the input string. xxHash is an extremely fast non-cryptographic Hash algorithm.
        /// </summary>
        /// <param name="data">the input string</param>
        /// <returns>xxHash</returns>
        public static unsafe UInt32 ComputeHash(string data)
        {
            fixed (char* input = data)
            {
                return hash((byte*)input, (uint)data.Length * sizeof(char), Seed);
            }
        }

        /// <summary>
        /// Computes the xxHash of the input byte array. xxHash is an extremely fast non-cryptographic Hash algorithm.
        /// </summary>
        /// <param name="data">the input byte array</param>
        /// <returns>xxHash</returns>
        public static unsafe uint ComputeHash(byte[] data)
        {
            fixed (byte* input = &data[0])
            {
                return hash(input, (uint)data.Length, Seed);
            }
        }

        /// <summary>
        /// Computes the xxHash of the input byte array. xxHash is an extremely fast non-cryptographic Hash algorithm.
        /// </summary>
        /// <param name="data">the input byte array</param>
        /// <param name="offset">start offset</param>
        /// <param name="len">length</param>
        /// <param name="seed">initial seed</param>
        /// <returns>xxHash</returns>
        public static unsafe uint ComputeHash(byte[] data, int offset, uint len, uint seed)
        {
            fixed (byte* input = &data[offset])
            {
                return hash(input, len, seed);
            }
        }

        private unsafe static uint hash(byte* data, uint len, uint seed)
        {
            if (len < 16)
                return hashSmall(data, len, seed);
            var v1 = seed + Prime1;
            var v2 = v1 * Prime2 + len;
            var v3 = v2 * Prime3;
            var v4 = v3 * Prime4;
            var p = (uint*)data;
            var limit = (uint*)(data + len - 16);
            while (p < limit)
            {
                v1 += rotl32(v1, 13); v1 *= Prime1; v1 += *p; p++;
                v2 += rotl32(v2, 11); v2 *= Prime1; v2 += *p; p++;
                v3 += rotl32(v3, 17); v3 *= Prime1; v3 += *p; p++;
                v4 += rotl32(v4, 19); v4 *= Prime1; v4 += *p; p++;
            }
            p = limit;
            v1 += rotl32(v1, 17); v2 += rotl32(v2, 19); v3 += rotl32(v3, 13); v4 += rotl32(v4, 11);
            v1 *= Prime1; v2 *= Prime1; v3 *= Prime1; v4 *= Prime1;
            v1 += *p; p++; v2 += *p; p++; v3 += *p; p++; v4 += *p;
            v1 *= Prime2; v2 *= Prime2; v3 *= Prime2; v4 *= Prime2;
            v1 += rotl32(v1, 11); v2 += rotl32(v2, 17); v3 += rotl32(v3, 19); v4 += rotl32(v4, 13);
            v1 *= Prime3; v2 *= Prime3; v3 *= Prime3; v4 *= Prime3;
            var crc = v1 + rotl32(v2, 3) + rotl32(v3, 6) + rotl32(v4, 9);
            crc ^= crc >> 11;
            crc += (Prime4 + len) * Prime1;
            crc ^= crc >> 15;
            crc *= Prime2;
            crc ^= crc >> 13;
            return crc;
        }

        private unsafe static uint hashSmall(byte* data, uint len, uint seed)
        {
            var p = data;
            var bEnd = data + len;
            var limit = bEnd - 4;
            var idx = seed + Prime1;
            uint crc = Prime5;
            while (p < limit)
            {
                crc += (*(uint*)p) + idx;
                idx++;
                crc += rotl32(crc, 17) * Prime4;
                crc *= Prime1;
                p += 4;
            }
            while (p < bEnd)
            {
                crc += (*p) + idx;
                idx++;
                crc *= Prime1;
                p++;
            }
            crc += len;
            crc ^= crc >> 15;
            crc *= Prime2;
            crc ^= crc >> 13;
            crc *= Prime3;
            crc ^= crc >> 16;
            return crc;
        }

        private static UInt32 rotl32(UInt32 x, int r)
        {
            return (x << r) | (x >> (32 - r));
        }
    }
}

================================================
FILE: src/EFSecondLevelCache.Core/_0-restore.bat
================================================
rmdir /S /Q bin
rmdir /S /Q obj
dotnet restore
pause

================================================
FILE: src/EFSecondLevelCache.Core/_1-dotnet_pack.bat
================================================
dotnet pack -c release
pause

================================================
FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/App_Data/.gitkeep.txt
================================================


================================================
FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Controllers/HomeController.cs
================================================
using System.Linq;
using EFSecondLevelCache.Core.AspNetCoreSample.DataLayer;
using Microsoft.AspNetCore.Mvc;
using EFSecondLevelCache.Core.AspNetCoreSample.DataLayer.Entities;
using EFSecondLevelCache.Core.Contracts;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using AutoMapper;
using EFSecondLevelCache.Core.AspNetCoreSample.Models;
using AutoMapper.QueryableExtensions;
using EFSecondLevelCache.Core.AspNetCoreSample.Others;

namespace EFSecondLevelCache.Core.AspNetCoreSample.Controllers
{
    public class HomeController : Controller
    {
        private readonly SampleContext _context;
        private readonly IMapper _mapper;
        private static List<Post> _inMemoryPosts;

        public HomeController(SampleContext context, IMapper mapper)
        {
            _context = context;
            _mapper = mapper;
            cacheInMemory();
        }

        private void cacheInMemory()
        {
            if (_inMemoryPosts == null)
            {
                _inMemoryPosts = _context.Set<Post>().AsNoTracking().ToList();
            }
        }

        /// <summary>
        /// Get https://localhost:5001/home/RunInMemory
        /// </summary>
        public IActionResult RunInMemory()
        {
            var debugInfo = new EFCacheDebugInfo();
            var post1 = _inMemoryPosts.AsQueryable()
                .Where(x => x.Id > 0)
                .OrderBy(x => x.Id)
                .Cacheable(debugInfo)
                .FirstOrDefault();
            return Json(new { post1?.Title, debugInfo });
        }

        public async Task<IActionResult> Index()
        {
            var debugInfo = new EFCacheDebugInfo();
            var post1 = await _context.Set<Post>()
                .Where(x => x.Id > 0)
                .OrderBy(x => x.Id)
                .Cacheable(debugInfo)
                .FirstOrDefaultAsync();
            return Json(new { post1.Title, debugInfo });
        }

        public async Task<IActionResult> TaskWhenAll()
        {
            var debugInfo = new EFCacheDebugInfo();
            var task1 = _context.Set<Post>().Where(x => x.Id > 0).Cacheable(debugInfo).ToListAsync();
            var results = await Task.WhenAll(task1);
            return Json(new { results, debugInfo });
        }

        public async Task<IActionResult> MapToDtoBefore()
        {
            var debugInfo = new EFCacheDebugInfo();
            var posts = await _context.Set<Post>()
                .Where(x => x.Id > 0)
                .OrderBy(x => x.Id)
                .ProjectTo<PostDto>(configuration: _mapper.ConfigurationProvider)
                .Cacheable(debugInfo)
                .ToListAsync();
            return Json(new { posts, debugInfo });
        }

        public async Task<IActionResult> MapToDtoAfter()
        {
            var debugInfo = new EFCacheDebugInfo();
            var posts = await _context.Set<Post>()
                .Where(x => x.Id > 0)
                .OrderBy(x => x.Id)
                .Cacheable(debugInfo)
                .ProjectTo<PostDto>(configuration: _mapper.ConfigurationProvider)
                .ToListAsync();
            return Json(new { posts, debugInfo });
        }

        /// <summary>
        /// Get https://localhost:5001/home/WithSlidingExpiration
        /// </summary>
        public async Task<IActionResult> WithSlidingExpiration()
        {
            var debugInfo = new EFCacheDebugInfo();
            var post1 = await _context.Set<Post>()
                .Where(x => x.Id > 0)
                .OrderBy(x => x.Id)
                .Cacheable(new EFCachePolicy(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(5)), debugInfo)
                .FirstOrDefaultAsync();
            return Json(new { post1.Title, debugInfo });
        }

        /// <summary>
        /// Get https://localhost:5001/home/WithAbsoluteExpiration
        /// </summary>
        public async Task<IActionResult> WithAbsoluteExpiration()
        {
            var debugInfo = new EFCacheDebugInfo();
            var post1 = await _context.Set<Post>()
                .Where(x => x.Id > 0)
                .OrderBy(x => x.Id)
                .Cacheable(CacheExpirationMode.Absolute, TimeSpan.FromMinutes(5), debugInfo)
                .FirstOrDefaultAsync();
            return Json(new { post1.Title, debugInfo });
        }

        public async Task<IActionResult> AsyncTest()
        {
            var debugInfo = new EFCacheDebugInfo();
            var post1 = await _context.Posts
                .Where(x => x.Id > 0)
                .Cacheable(debugInfo)
                .FirstOrDefaultAsync();
            return Json(new { post1.Title, debugInfo });
        }

        public async Task<IActionResult> CountTest()
        {
            var debugInfo = new EFCacheDebugInfo();
            var count = await _context.Posts
                .Where(x => x.Id > 0)
                .Cacheable(debugInfo)
                .CountAsync();
            return Json(new { count, debugInfo });
        }

        public async Task<IActionResult> CountWithParamsTest()
        {
            var debugInfo = new EFCacheDebugInfo();
            var count = await _context.Posts
                .Cacheable(debugInfo)
                .CountAsync(x => x.Id > 0);
            return Json(new { count, debugInfo });
        }

        public async Task<IActionResult> CollectionsTest()
        {
            var collection1 = new[] { 1, 2, 3 };
            var debugInfo1 = new EFCacheDebugInfo();
            var post1 = await _context.Posts
                .Where(x => collection1.Contains(x.Id))
                .Cacheable(debugInfo1)
                .FirstOrDefaultAsync();

            var collection2 = new[] { 1, 2, 3, 4 };
            var debugInfo2 = new EFCacheDebugInfo();
            var post2 = await _context.Posts
                .Where(x => collection2.Contains(x.Id))
                .Cacheable(debugInfo2)
                .FirstOrDefaultAsync();
            return Json(new { post1.Title, post2.Id, debugInfo1, debugInfo2 });
        }

        /// <summary>
        /// Get https://localhost:5001/home/StringEqualsTest
        /// </summary>
        public async Task<IActionResult> StringEqualsTest()
        {
            var debugInfo = new EFCacheDebugInfo();
            var rnd = new Random();
            var value = rnd.Next(1, 1000000).ToString();
            var post1 = await _context.Posts
                .Where(x => x.Title.Equals(value))
                .Cacheable(debugInfo)
                .FirstOrDefaultAsync();
            return Json(new { post1?.Title, debugInfo });
        }

        /// <summary>
        /// Get https://localhost:5001/home/Issue36
        /// </summary>
        public IActionResult Issue36()
        {
            User user1;
            const string user1Name = "User1";
            if (!_context.Users.Any(user => user.Name == user1Name))
            {
                user1 = new User { Name = user1Name };
                user1 = _context.Users.Add(user1).Entity;
            }
            else
            {
                user1 = _context.Users.First(user => user.Name == user1Name);
            }

            var product = new Product
            {
                ProductName = "P981122",
                IsActive = true,
                Notes = "Notes ...",
                ProductNumber = "098112",
                User = user1
            };

            product = _context.Products.Add(product).Entity;
            _context.SaveChanges();

            // 1st query, reading from db
            var debugInfo1 = new EFCacheDebugInfo();
            var firstQueryResult = _context.Products
                .Cacheable(debugInfo1)
                .FirstOrDefault(p => p.ProductId == product.ProductId);

            var debugInfoWithWhere1 = new EFCacheDebugInfo();
            var firstQueryWithWhereClauseResult = _context.Products.Where(p => p.ProductId == product.ProductId)
                .Cacheable(debugInfoWithWhere1)
                .FirstOrDefault();

            // Delete it from db, invalidates the cache on SaveChanges
            _context.Products.Remove(product);
            _context.SaveChanges();

            // same query, reading from 2nd level cache? No.
            var debugInfo2 = new EFCacheDebugInfo();
            var secondQueryResult = _context.Products
                .Cacheable(debugInfo2)
                .FirstOrDefault(p => p.ProductId == product.ProductId);

            // same query, reading from 2nd level cache? No.
            var debugInfo3 = new EFCacheDebugInfo();
            var thirdQueryResult = _context.Products.Where(p => p.ProductId == product.ProductId)
                .Cacheable(debugInfo3)
                .FirstOrDefault();

            // retrieving it directly from database
            var p98 = _context.Products.FirstOrDefault(p => p.ProductId == product.ProductId);

            return Json(new
            {
                firstQueryResult,
                isFirstQueryCached = debugInfo1,

                firstQueryWithWhereClauseResult,
                isFirstQueryWithWhereClauseCached = debugInfoWithWhere1,

                secondQueryResult,
                isSecondQueryCached = debugInfo2,

                thirdQueryResult,
                isThirdQueryCached = debugInfo3,

                directlyFromDatabase = p98
            });
        }

        public IActionResult DynamicGetWithCacheableAtFirst()
        {
            var debugInfo = new EFCacheDebugInfo();
            var users = _context.Users.DynamicGetWithCacheableAtFirst(debugInfo, x => x.Id > 0, x => x.Posts);
            return Json(new { users, debugInfo });
        }

        public IActionResult DynamicGetWithCacheableAtEnd()
        {
            var debugInfo = new EFCacheDebugInfo();
            var users = _context.Users.DynamicGetWithCacheableAtEnd(debugInfo, x => x.Id > 0, x => x.Posts);
            return Json(new { users, debugInfo }); // https://github.com/aspnet/EntityFrameworkCore/issues/12098
        }

        public async Task<IActionResult> FirstOrDefaultInline()
        {
            var debugInfo = new EFCacheDebugInfo();
            var post1 = await _context.Set<Post>()
                .Where(x => x.Id == 1)
                .Cacheable(debugInfo)
                .FirstOrDefaultAsync();
            return Json(new { post1.Title, debugInfo });
        }

        public async Task<IActionResult> FirstOrDefaultInlineAtTheEnd()
        {
            var debugInfo = new EFCacheDebugInfo();
            var post1 = await _context.Set<Post>()
                .Cacheable(debugInfo)
                .FirstOrDefaultAsync(x => x.Id == 1);
            return Json(new { post1.Title, debugInfo });
        }

        public async Task<IActionResult> FirstOrDefaultWithParam()
        {
            var param1 = 1;
            var debugInfo = new EFCacheDebugInfo();
            var post1 = await _context.Set<Post>()
                .Where(x => x.Id == param1)
                .Cacheable(debugInfo)
                .FirstOrDefaultAsync();
            return Json(new { post1.Title, debugInfo });
        }

        public async Task<IActionResult> FirstOrDefaultAtTheEndWithParam()
        {
            var param1 = 1;
            var debugInfo = new EFCacheDebugInfo();
            var post1 = await _context.Set<Post>()
                .Cacheable(debugInfo)
                .FirstOrDefaultAsync(x => x.Id == param1);
            return Json(new { post1.Title, debugInfo });
        }

        // https://localhost:5001/home/FirstOrDefaultWithParams
        public async Task<IActionResult> FirstOrDefaultWithParams()
        {
            var param1 = 1;
            var debugInfo1 = new EFCacheDebugInfo();
            var param2 = param1;
            var post1 = await _context.Set<Post>()
                .Where(x => x.Id == param2)
                .Cacheable(debugInfo1)
                .FirstOrDefaultAsync();

            param1 = 2;
            var debugInfo2 = new EFCacheDebugInfo();
            var post2 = await _context.Set<Post>()
                .Where(x => x.Id == param1)
                .Cacheable(debugInfo2)
                .FirstOrDefaultAsync();

            return Json(new { post1Title = post1.Title, debugInfo1, post2Title = post2.Title, debugInfo2 });
        }

        // https://github.com/VahidN/EFSecondLevelCache.Core/issues/53
        // https://localhost:5001/home/FirstOrDefaultWithParams2
        public async Task<IActionResult> FirstOrDefaultWithParams2()
        {
            var param1 = 1;
            var debugInfo1 = new EFCacheDebugInfo();
            var post1 = await _context.GetFirstOrDefaultAsync<Post, SampleContext>(debugInfo1, x => x.Id == param1);

            param1 = 2;
            var debugInfo2 = new EFCacheDebugInfo();
            var post2 = await _context.GetFirstOrDefaultAsync<Post, SampleContext>(debugInfo2, x => x.Id == param1);

            return Json(new { post1Title = post1.Title, debugInfo1, post2Title = post2.Title, debugInfo2 });
        }

        public async Task<IActionResult> TestEnumsWithParams()
        {
            var param1 = UserStatus.Active;
            var debugInfo = new EFCacheDebugInfo();
            var user1 = await _context.Set<User>()
                .Cacheable(debugInfo)
                .FirstOrDefaultAsync(x => x.UserStatus == param1);
            return Json(new { user1, debugInfo });
        }

        public async Task<IActionResult> TestInlineEnums()
        {
            var debugInfo = new EFCacheDebugInfo();
            var user1 = await _context.Set<User>()
                .Cacheable(debugInfo)
                .FirstOrDefaultAsync(x => x.UserStatus == UserStatus.Active);
            return Json(new { user1, debugInfo });
        }

        public async Task<IActionResult> TestEFCachedDbSet()
        {
            var users1 = await _context.CachedPosts.OrderByDescending(x => x.Id).ToListAsync();

            var debugInfo2 = new EFCacheDebugInfo();
            var users2 = await _context.Set<Post>().Cacheable(debugInfo2).OrderByDescending(x => x.Id).ToListAsync();

            var debugInfo3 = new EFCacheDebugInfo();
            var users3 = await _context.Set<Post>().OrderByDescending(x => x.Id).Cacheable(debugInfo3).ToListAsync();
            return Json(new { users1, debugInfo2, d
Download .txt
gitextract_jukctmgq/

├── .gitattributes
├── .github/
│   ├── issue_template.md
│   ├── lock.yml
│   └── workflows/
│       └── build.yml
├── .gitignore
├── .vscode/
│   ├── launch.json
│   ├── settings.json
│   └── tasks.json
├── EFSecondLevelCache.Core.sln
├── LICENSE.md
├── README.md
├── global.json
├── src/
│   ├── EFSecondLevelCache.Core/
│   │   ├── Contracts/
│   │   │   ├── EFCacheDebugInfo.cs
│   │   │   ├── EFCacheKey.cs
│   │   │   ├── EFQueryDebugView.cs
│   │   │   ├── IEFCacheKeyHashProvider.cs
│   │   │   ├── IEFCacheKeyProvider.cs
│   │   │   └── IEFCacheServiceProvider.cs
│   │   ├── EFAsyncEnumerable.cs
│   │   ├── EFAsyncEnumerator.cs
│   │   ├── EFAsyncTaskEnumerable.cs
│   │   ├── EFAsyncTaskEnumerator.cs
│   │   ├── EFCacheKeyHashProvider.cs
│   │   ├── EFCacheKeyProvider3x.cs
│   │   ├── EFCachePolicy.cs
│   │   ├── EFCacheServiceProvider.cs
│   │   ├── EFCachedDbSet.cs
│   │   ├── EFCachedDbSetExtensions.cs
│   │   ├── EFCachedQueryExtensions.cs
│   │   ├── EFCachedQueryProvider.cs
│   │   ├── EFCachedQueryable.cs
│   │   ├── EFChangeTrackerExtensions.cs
│   │   ├── EFMaterializer.cs
│   │   ├── EFQueryExpressionVisitor.cs
│   │   ├── EFSecondLevelCache.Core.csproj
│   │   ├── EFServiceCollectionExtensions.cs
│   │   ├── EFStaticServiceProvider.cs
│   │   ├── ParallelExtensions.cs
│   │   ├── Properties/
│   │   │   └── AssemblyInfo.cs
│   │   ├── XxHashUnsafe.cs
│   │   ├── _0-restore.bat
│   │   └── _1-dotnet_pack.bat
│   └── Tests/
│       ├── EFSecondLevelCache.Core.AspNetCoreSample/
│       │   ├── App_Data/
│       │   │   └── .gitkeep.txt
│       │   ├── Controllers/
│       │   │   └── HomeController.cs
│       │   ├── DataLayer/
│       │   │   ├── Entities/
│       │   │   │   ├── Post.cs
│       │   │   │   ├── Product.cs
│       │   │   │   ├── Tag.cs
│       │   │   │   ├── TagProduct.cs
│       │   │   │   └── User.cs
│       │   │   ├── SampleContext.cs
│       │   │   └── Utils/
│       │   │       ├── ApplicationDbContextSeedData.cs
│       │   │       └── DBInitialization.cs
│       │   ├── EFSecondLevelCache.Core.AspNetCoreSample.csproj
│       │   ├── Migrations/
│       │   │   ├── 20191022095356_V2019_10_22_1323.Designer.cs
│       │   │   ├── 20191022095356_V2019_10_22_1323.cs
│       │   │   └── SampleContextModelSnapshot.cs
│       │   ├── Models/
│       │   │   └── PostDto.cs
│       │   ├── Others/
│       │   │   └── TestUtils.cs
│       │   ├── Profiles/
│       │   │   └── PostProfile.cs
│       │   ├── Program.cs
│       │   ├── Properties/
│       │   │   └── launchSettings.json
│       │   ├── Startup.cs
│       │   ├── _0-restore.bat
│       │   ├── _1-dotnet_run.bat
│       │   ├── _update_db.bat
│       │   ├── appsettings.json
│       │   ├── web.config
│       │   └── wwwroot/
│       │       └── App_Data/
│       │           └── .gitkeep
│       ├── EFSecondLevelCache.Core.NET46Sample/
│       │   └── EFSecondLevelCache.Core.NET46Sample/
│       │       ├── App.config
│       │       ├── DataLayer/
│       │       │   ├── ConfigureServices.cs
│       │       │   ├── Entities/
│       │       │   │   └── Post.cs
│       │       │   └── SampleContext.cs
│       │       ├── EFSecondLevelCache.Core.NET46Sample.csproj
│       │       ├── Program.cs
│       │       ├── Properties/
│       │       │   └── AssemblyInfo.cs
│       │       └── packages.config
│       ├── EFSecondLevelCache.Core.PerformanceTests/
│       │   ├── BenchmarkTests.cs
│       │   ├── EFSecondLevelCache.Core.PerformanceTests.csproj
│       │   ├── Program.cs
│       │   ├── SampleContext.cs
│       │   ├── TestsServiceProvider.cs
│       │   ├── _0-restore.bat
│       │   ├── _1-dotnet_run.bat
│       │   └── app_data/
│       │       └── .gitkeep
│       ├── EFSecondLevelCache.Core.Tests/
│       │   ├── EFCacheServiceProviderTests.cs
│       │   ├── EFCachedQueryProviderAsyncTests.cs
│       │   ├── EFCachedQueryProviderBasicTests.cs
│       │   ├── EFCachedQueryProviderInvalidationTests.cs
│       │   ├── EFSecondLevelCache.Core.Tests.csproj
│       │   ├── Properties/
│       │   │   └── AssemblyInfo.cs
│       │   ├── TestsBase.cs
│       │   ├── XxHashTests.cs
│       │   ├── _0-restore.bat
│       │   └── _1-dotnet_test.bat
│       └── Issues/
│           ├── Issue15/
│           │   ├── ConfigureServices.cs
│           │   ├── Issue15.csproj
│           │   ├── Payment.cs
│           │   ├── Program.cs
│           │   ├── SampleContext.cs
│           │   └── app_data/
│           │       └── .git.keep
│           └── Issue43/
│               ├── Issue43.csproj
│               ├── Program.cs
│               ├── _0-restore.bat
│               ├── _1-dotnet_run.bat
│               └── app_data/
│                   └── .git.keep
├── tag-it.bat
└── update-dependencies.bat
Download .txt
SYMBOL INDEX (398 symbols across 62 files)

FILE: src/EFSecondLevelCache.Core/Contracts/EFCacheDebugInfo.cs
  class EFCacheDebugInfo (line 6) | public class EFCacheDebugInfo

FILE: src/EFSecondLevelCache.Core/Contracts/EFCacheKey.cs
  class EFCacheKey (line 8) | public class EFCacheKey
    method EFCacheKey (line 29) | public EFCacheKey()
    method Equals (line 39) | public override bool Equals(object obj)
    method GetHashCode (line 52) | public override int GetHashCode()

FILE: src/EFSecondLevelCache.Core/Contracts/EFQueryDebugView.cs
  class EFQueryDebugView (line 8) | public class EFQueryDebugView

FILE: src/EFSecondLevelCache.Core/Contracts/IEFCacheKeyHashProvider.cs
  type IEFCacheKeyHashProvider (line 6) | public interface IEFCacheKeyHashProvider
    method ComputeHash (line 13) | string ComputeHash(string data);

FILE: src/EFSecondLevelCache.Core/Contracts/IEFCacheKeyProvider.cs
  type IEFCacheKeyProvider (line 9) | public interface IEFCacheKeyProvider
    method GetEFCacheKey (line 18) | EFCacheKey GetEFCacheKey(IQueryable query, Expression expression, stri...

FILE: src/EFSecondLevelCache.Core/Contracts/IEFCacheServiceProvider.cs
  type IEFCacheServiceProvider (line 8) | public interface IEFCacheServiceProvider
    method ClearAllCachedEntries (line 13) | void ClearAllCachedEntries();
    method GetValue (line 20) | object GetValue(string cacheKey);
    method InsertValue (line 29) | void InsertValue(string cacheKey, object value, ISet<string> rootCache...
    method InvalidateCacheDependencies (line 35) | void InvalidateCacheDependencies(string[] rootCacheKeys);

FILE: src/EFSecondLevelCache.Core/EFAsyncEnumerable.cs
  class EFAsyncEnumerable (line 9) | public class EFAsyncEnumerable<T> : IAsyncEnumerable<T>
    method EFAsyncEnumerable (line 16) | public EFAsyncEnumerable(IEnumerator<T> inner)
    method GetEnumerator (line 24) | public IAsyncEnumerator<T> GetEnumerator()
    method GetAsyncEnumerator (line 32) | public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancel...

FILE: src/EFSecondLevelCache.Core/EFAsyncEnumerator.cs
  class EFAsyncEnumerator (line 11) | public class EFAsyncEnumerator<T> : IAsyncEnumerator<T>
    method EFAsyncEnumerator (line 19) | public EFAsyncEnumerator(IEnumerator<T> inner)
    method Dispose (line 32) | public void Dispose()
    method MoveNext (line 42) | public Task<bool> MoveNext(CancellationToken cancellationToken)
    method MoveNextAsync (line 52) | public ValueTask<bool> MoveNextAsync()
    method DisposeAsync (line 60) | public ValueTask DisposeAsync()

FILE: src/EFSecondLevelCache.Core/EFAsyncTaskEnumerable.cs
  class EFAsyncTaskEnumerable (line 10) | public class EFAsyncTaskEnumerable<T> : IAsyncEnumerable<T>
    method EFAsyncTaskEnumerable (line 17) | public EFAsyncTaskEnumerable(Task<T> task)
    method GetEnumerator (line 25) | public IAsyncEnumerator<T> GetEnumerator() => new EFAsyncTaskEnumerato...
    method GetAsyncEnumerator (line 30) | public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancel...

FILE: src/EFSecondLevelCache.Core/EFAsyncTaskEnumerator.cs
  class EFAsyncTaskEnumerator (line 11) | public sealed class EFAsyncTaskEnumerator<T> : IAsyncEnumerator<T>
    method EFAsyncTaskEnumerator (line 19) | public EFAsyncTaskEnumerator(Task<T> task)
    method MoveNext (line 32) | public async Task<bool> MoveNext(CancellationToken cancellationToken)
    method MoveNextAsync (line 51) | public ValueTask<bool> MoveNextAsync()
    method DisposeAsync (line 59) | public ValueTask DisposeAsync()

FILE: src/EFSecondLevelCache.Core/EFCacheKeyHashProvider.cs
  class EFCacheKeyHashProvider (line 9) | public class EFCacheKeyHashProvider : IEFCacheKeyHashProvider
    method ComputeHash (line 16) | public string ComputeHash(string data)

FILE: src/EFSecondLevelCache.Core/EFCacheKeyProvider3x.cs
  class EFCacheKeyProvider (line 18) | public class EFCacheKeyProvider : IEFCacheKeyProvider
    method EFCacheKeyProvider (line 42) | public EFCacheKeyProvider(IEFCacheKeyHashProvider cacheKeyHashProvider)
    method GetEFCacheKey (line 54) | public EFCacheKey GetEFCacheKey(IQueryable query, Expression expressio...
    method setCache (line 81) | private static void setCache(string expressionKeyHash, EFCacheKey value)
    method getExpressionKeyHash (line 87) | private static (string ExpressionKeyHash, Expression ModifiedExpressio...

FILE: src/EFSecondLevelCache.Core/EFCachePolicy.cs
  type CacheExpirationMode (line 8) | public enum CacheExpirationMode
  class EFCachePolicy (line 25) | public class EFCachePolicy
    method EFCachePolicy (line 49) | public EFCachePolicy() { }
    method EFCachePolicy (line 56) | public EFCachePolicy(CacheExpirationMode expirationMode, TimeSpan time...
    method EFCachePolicy (line 68) | public EFCachePolicy(CacheExpirationMode expirationMode, TimeSpan time...

FILE: src/EFSecondLevelCache.Core/EFCacheServiceProvider.cs
  class EFCacheServiceProvider (line 12) | public class EFCacheServiceProvider : IEFCacheServiceProvider
    method EFCacheServiceProvider (line 34) | public EFCacheServiceProvider()
    method ClearAllCachedEntries (line 45) | public void ClearAllCachedEntries()
    method GetValue (line 56) | public object GetValue(string cacheKey)
    method InsertValue (line 68) | public void InsertValue(string cacheKey, object value, ISet<string> ro...
    method InvalidateCacheDependencies (line 109) | public void InvalidateCacheDependencies(string[] rootCacheKeys)
    method clearDependencyValues (line 123) | private void clearDependencyValues(string rootCacheKey)

FILE: src/EFSecondLevelCache.Core/EFCachedDbSet.cs
  class EFCachedDbSet (line 17) | public class EFCachedDbSet<TType> : IOrderedQueryable<TType>, IAsyncEnum...
    method EFCachedDbSet (line 30) | public EFCachedDbSet(
    method GetEnumerator (line 97) | public IEnumerator GetEnumerator()
    method GetEnumerator (line 106) | IEnumerator<TType> IEnumerable<TType>.GetEnumerator()
    method GetAsyncEnumerator (line 115) | public IAsyncEnumerator<TType> GetAsyncEnumerator(CancellationToken ca...

FILE: src/EFSecondLevelCache.Core/EFCachedDbSetExtensions.cs
  class EFCachedDbSetExtensions (line 17) | public static class EFCachedDbSetExtensions
    method Find (line 22) | public static TEntity Find<TEntity>(
    method FindAsync (line 35) | public static Task<TEntity> FindAsync<TEntity>(
    method FindAsync (line 49) | public static Task<TEntity> FindAsync<TEntity>(
    method buildFindQueryable (line 56) | private static IQueryable<TEntity> buildFindQueryable<TEntity>(

FILE: src/EFSecondLevelCache.Core/EFCachedQueryExtensions.cs
  class EFCachedQueryExtensions (line 12) | public static class EFCachedQueryExtensions
    method EFCachedQueryExtensions (line 17) | static EFCachedQueryExtensions()
    method Cacheable (line 34) | public static EFCachedQueryable<TType> Cacheable<TType>(
    method Cacheable (line 50) | public static IQueryable Cacheable(
    method Cacheable (line 69) | public static EFCachedDbSet<TType> Cacheable<TType>(
    method Cacheable (line 85) | public static EFCachedQueryable<TType> Cacheable<TType>(
    method Cacheable (line 101) | public static IQueryable Cacheable(
    method Cacheable (line 118) | public static EFCachedDbSet<TType> Cacheable<TType>(
    method Cacheable (line 134) | public static EFCachedQueryable<TType> Cacheable<TType>(
    method Cacheable (line 147) | public static IQueryable Cacheable(
    method Cacheable (line 161) | public static EFCachedDbSet<TType> Cacheable<TType>(
    method Cacheable (line 174) | public static EFCachedQueryable<TType> Cacheable<TType>(
    method Cacheable (line 187) | public static EFCachedDbSet<TType> Cacheable<TType>(
    method Cacheable (line 201) | public static EFCachedQueryable<TType> Cacheable<TType>(
    method Cacheable (line 215) | public static EFCachedDbSet<TType> Cacheable<TType>(
    method Cacheable (line 228) | public static IQueryable Cacheable(this IQueryable query, EFCachePolic...
    method Cacheable (line 239) | public static EFCachedQueryable<TType> Cacheable<TType>(this IQueryabl...
    method Cacheable (line 249) | public static IQueryable Cacheable(this IQueryable query)
    method Cacheable (line 260) | public static EFCachedDbSet<TType> Cacheable<TType>(this DbSet<TType> ...
    method Cacheable (line 272) | public static EFCachedQueryable<TType> Cacheable<TType>(this IQueryabl...
    method Cacheable (line 284) | public static EFCachedDbSet<TType> Cacheable<TType>(this DbSet<TType> ...
    method Cacheable (line 296) | public static EFCachedQueryable<TType> Cacheable<TType>(this IQueryabl...
    method Cacheable (line 309) | public static EFCachedQueryable<TType> Cacheable<TType>(
    method Cacheable (line 324) | public static EFCachedQueryable<TType> Cacheable<TType>(
    method Cacheable (line 336) | public static IQueryable Cacheable(this IQueryable query, EFCachePolic...
    method Cacheable (line 348) | public static EFCachedDbSet<TType> Cacheable<TType>(
    method Cacheable (line 362) | public static EFCachedDbSet<TType> Cacheable<TType>(
    method Cacheable (line 377) | public static EFCachedDbSet<TType> Cacheable<TType>(

FILE: src/EFSecondLevelCache.Core/EFCachedQueryProvider.cs
  class EFCachedQueryProvider (line 15) | public class EFCachedQueryProvider : IAsyncQueryProvider
    method EFCachedQueryProvider (line 34) | public EFCachedQueryProvider(
    method CreateQuery (line 61) | public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    method CreateQuery (line 71) | public IQueryable CreateQuery(Expression expression)
    method Execute (line 91) | public object Execute(Expression expression)
    method Execute (line 102) | public TResult Execute<TResult>(Expression expression)
    method ExecuteAsync (line 114) | public TResult ExecuteAsync<TResult>(Expression expression, Cancellati...
    method isTaskOfT (line 145) | private static bool isTaskOfT(Type type)

FILE: src/EFSecondLevelCache.Core/EFCachedQueryable.cs
  class EFCachedQueryable (line 16) | public class EFCachedQueryable<TType> : IOrderedQueryable<TType>, IAsync...
    method EFCachedQueryable (line 28) | public EFCachedQueryable(
    method GetEnumerator (line 92) | public IEnumerator GetEnumerator()
    method GetEnumerator (line 103) | IEnumerator<TType> IEnumerable<TType>.GetEnumerator()
    method GetAsyncEnumerator (line 114) | public IAsyncEnumerator<TType> GetAsyncEnumerator(CancellationToken ca...

FILE: src/EFSecondLevelCache.Core/EFChangeTrackerExtensions.cs
  class EFChangeTrackerExtensions (line 14) | public static class EFChangeTrackerExtensions
    method GetBaseTypes (line 22) | public static IEnumerable<Type> GetBaseTypes(this Type type)
    method GetChangedEntityNames (line 39) | public static string[] GetChangedEntityNames(this DbContext dbContext)
    method IsEntityChanged (line 59) | private static bool IsEntityChanged(EntityEntry entry)
    method GetChangedEntityTypes (line 73) | public static IEnumerable<Type> GetChangedEntityTypes(this DbContext d...
    method MarkAsNoTracking (line 89) | public static IQueryable<TType> MarkAsNoTracking<TType>(this IQueryabl...

FILE: src/EFSecondLevelCache.Core/EFMaterializer.cs
  class CacheResult (line 14) | public class CacheResult<T>
    method CacheResult (line 34) | public CacheResult()
    method CacheResult (line 44) | public CacheResult(bool canRead, EFCacheKey cacheKey, T result)
  class EFMaterializer (line 55) | public class EFMaterializer
    method EFMaterializer (line 74) | public EFMaterializer(
    method MaterializeAsync (line 94) | public async Task<T> MaterializeAsync<T>(Expression expression, Func<T...
    method Materialize (line 122) | public T Materialize<T>(Expression expression, Func<T> materializer)
    method readFromCache (line 139) | private CacheResult<T> readFromCache<T>(Expression expression)

FILE: src/EFSecondLevelCache.Core/EFQueryExpressionVisitor.cs
  class EFQueryExpressionVisitor (line 17) | public class EFQueryExpressionVisitor : ExpressionVisitor
    method EFQueryExpressionVisitor (line 47) | private EFQueryExpressionVisitor(TextWriter file)
    type Flow (line 52) | [Flags]
    method GetDebugView (line 71) | public static EFQueryDebugView GetDebugView(Expression node)
    method VisitBinary (line 87) | protected override Expression VisitBinary(BinaryExpression node)
    method VisitBlock (line 189) | protected override Expression VisitBlock(BlockExpression node)
    method VisitCatchBlock (line 209) | protected override CatchBlock VisitCatchBlock(CatchBlock node)
    method VisitConditional (line 234) | protected override Expression VisitConditional(ConditionalExpression n...
    method VisitConstant (line 266) | protected override Expression VisitConstant(ConstantExpression node)
    method printConstant (line 272) | private void printConstant(ConstantExpression node, object value)
    method VisitDebugInfo (line 327) | protected override Expression VisitDebugInfo(DebugInfoExpression node)
    method VisitDefault (line 346) | protected override Expression VisitDefault(DefaultExpression node)
    method VisitElementInit (line 358) | protected override ElementInit VisitElementInit(ElementInit node)
    method VisitExtension (line 376) | protected override Expression VisitExtension(Expression node)
    method VisitGoto (line 397) | protected override Expression VisitGoto(GotoExpression node)
    method VisitIndex (line 412) | protected override Expression VisitIndex(IndexExpression node)
    method VisitInvocation (line 432) | protected override Expression VisitInvocation(InvocationExpression node)
    method VisitLabel (line 445) | protected override Expression VisitLabel(LabelExpression node)
    method VisitLambda (line 462) | protected override Expression VisitLambda<T>(Expression<T> node)
    method VisitListInit (line 493) | protected override Expression VisitListInit(ListInitExpression node)
    method VisitLoop (line 505) | protected override Expression VisitLoop(LoopExpression node)
    method VisitMember (line 530) | protected override Expression VisitMember(MemberExpression node)
    method VisitMemberAssignment (line 541) | protected override MemberAssignment VisitMemberAssignment(MemberAssign...
    method VisitMemberInit (line 554) | protected override Expression VisitMemberInit(MemberInitExpression node)
    method VisitMemberListBinding (line 566) | protected override MemberListBinding VisitMemberListBinding(MemberList...
    method VisitMemberMemberBinding (line 579) | protected override MemberMemberBinding VisitMemberMemberBinding(Member...
    method VisitMethodCall (line 592) | protected override Expression VisitMethodCall(MethodCallExpression node)
    method VisitNew (line 618) | protected override Expression VisitNew(NewExpression node)
    method VisitNewArray (line 631) | protected override Expression VisitNewArray(NewArrayExpression node)
    method VisitParameter (line 655) | protected override Expression VisitParameter(ParameterExpression node)
    method VisitRuntimeVariables (line 679) | protected override Expression VisitRuntimeVariables(RuntimeVariablesEx...
    method VisitSwitch (line 691) | protected override Expression VisitSwitch(SwitchExpression node)
    method VisitSwitchCase (line 715) | protected override SwitchCase VisitSwitchCase(SwitchCase node)
    method VisitTry (line 735) | protected override Expression VisitTry(TryExpression node)
    method VisitTypeBinary (line 766) | protected override Expression VisitTypeBinary(TypeBinaryExpression node)
    method VisitUnary (line 787) | protected override Expression VisitUnary(UnaryExpression node)
    method containsWhiteSpace (line 883) | private static bool containsWhiteSpace(string name)
    method getConstantValueSuffix (line 895) | private static string getConstantValueSuffix(Type type)
    method getDisplayName (line 924) | private static string getDisplayName(string name)
    method getId (line 937) | private static int getId<T>(T e, ref Dictionary<T, int> ids)
    method getOperatorPrecedence (line 958) | private static int getOperatorPrecedence(Expression node)
    method isSimpleExpression (line 1078) | private static bool isSimpleExpression(Expression node)
    method needsParentheses (line 1089) | private static bool needsParentheses(Expression parent, Expression child)
    method quoteName (line 1166) | private static string quoteName(string name)
    method checkBreak (line 1171) | private Flow checkBreak(Flow flow)
    method dedent (line 1187) | private void dedent()
    method dumpLabel (line 1192) | private void dumpLabel(LabelTarget target)
    method getFlow (line 1197) | private Flow getFlow(Flow flow)
    method getLabelTargetId (line 1206) | private int getLabelTargetId(LabelTarget target)
    method getLabelTargetName (line 1212) | private string getLabelTargetName(LabelTarget target)
    method getLambdaId (line 1225) | private int getLambdaId(LambdaExpression le)
    method getLambdaName (line 1231) | private string getLambdaName(LambdaExpression lambda)
    method getParamId (line 1240) | private int getParamId(ParameterExpression p)
    method indent (line 1246) | private void indent()
    method newLine (line 1251) | private void newLine()
    method Out (line 1256) | private void Out(string s)
    method Out (line 1261) | private void Out(Flow before, string s)
    method Out (line 1266) | private void Out(string s, Flow after)
    method Out (line 1271) | private void Out(Flow before, string s, Flow after)
    method outMember (line 1290) | private void outMember(Expression node, Expression instance, MemberInf...
    method getMemberInfoValue (line 1310) | private static object getMemberInfoValue(MemberInfo memberInfo, object...
    method parenthesizedVisit (line 1327) | private void parenthesizedVisit(Expression parent, Expression nodeToVi...
    method visitDeclarations (line 1341) | private void visitDeclarations(IList<ParameterExpression> expressions)
    method visitExpressions (line 1355) | private void visitExpressions<T>(char open, IList<T> expressions) wher...
    method visitExpressions (line 1360) | private void visitExpressions<T>(char open, char separator, IList<T> e...
    method visitExpressions (line 1365) | private void visitExpressions<T>(char open, char separator, IList<T> e...
    method write (line 1409) | private void write(string s)
    method writeLambda (line 1415) | private void writeLambda(LambdaExpression lambda)
    method writeLine (line 1435) | private void writeLine()
    method writeTo (line 1441) | private void writeTo(Expression node)
    method addType (line 1465) | private void addType(Type type)
    method isCompilerGenerated (line 1503) | private bool isCompilerGenerated(string name)

FILE: src/EFSecondLevelCache.Core/EFServiceCollectionExtensions.cs
  class EFServiceCollectionExtensions (line 10) | public static class EFServiceCollectionExtensions
    method AddEFSecondLevelCache (line 20) | public static IServiceCollection AddEFSecondLevelCache(this IServiceCo...

FILE: src/EFSecondLevelCache.Core/EFStaticServiceProvider.cs
  class EFStaticServiceProvider (line 11) | public static class EFStaticServiceProvider
    method getServiceProvider (line 21) | private static IServiceProvider getServiceProvider()

FILE: src/EFSecondLevelCache.Core/ParallelExtensions.cs
  class ParallelExtensions (line 9) | public static class ParallelExtensions
    method TryReadLocked (line 14) | public static void TryReadLocked(this ReaderWriterLockSlim readerWrite...
    method TryWriteLocked (line 34) | public static void TryWriteLocked(this ReaderWriterLockSlim readerWrit...

FILE: src/EFSecondLevelCache.Core/XxHashUnsafe.cs
  method ComputeHash (line 24) | public static unsafe UInt32 ComputeHash(string data)
  method ComputeHash (line 37) | public static unsafe uint ComputeHash(byte[] data)
  method ComputeHash (line 53) | public static unsafe uint ComputeHash(byte[] data, int offset, uint len,...
  method hash (line 61) | private unsafe static uint hash(byte* data, uint len, uint seed)

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Controllers/HomeController.cs
  class HomeController (line 17) | public class HomeController : Controller
    method HomeController (line 23) | public HomeController(SampleContext context, IMapper mapper)
    method cacheInMemory (line 30) | private void cacheInMemory()
    method RunInMemory (line 41) | public IActionResult RunInMemory()
    method Index (line 52) | public async Task<IActionResult> Index()
    method TaskWhenAll (line 63) | public async Task<IActionResult> TaskWhenAll()
    method MapToDtoBefore (line 71) | public async Task<IActionResult> MapToDtoBefore()
    method MapToDtoAfter (line 83) | public async Task<IActionResult> MapToDtoAfter()
    method WithSlidingExpiration (line 98) | public async Task<IActionResult> WithSlidingExpiration()
    method WithAbsoluteExpiration (line 112) | public async Task<IActionResult> WithAbsoluteExpiration()
    method AsyncTest (line 123) | public async Task<IActionResult> AsyncTest()
    method CountTest (line 133) | public async Task<IActionResult> CountTest()
    method CountWithParamsTest (line 143) | public async Task<IActionResult> CountWithParamsTest()
    method CollectionsTest (line 152) | public async Task<IActionResult> CollectionsTest()
    method StringEqualsTest (line 173) | public async Task<IActionResult> StringEqualsTest()
    method Issue36 (line 188) | public IActionResult Issue36()
    method DynamicGetWithCacheableAtFirst (line 262) | public IActionResult DynamicGetWithCacheableAtFirst()
    method DynamicGetWithCacheableAtEnd (line 269) | public IActionResult DynamicGetWithCacheableAtEnd()
    method FirstOrDefaultInline (line 276) | public async Task<IActionResult> FirstOrDefaultInline()
    method FirstOrDefaultInlineAtTheEnd (line 286) | public async Task<IActionResult> FirstOrDefaultInlineAtTheEnd()
    method FirstOrDefaultWithParam (line 295) | public async Task<IActionResult> FirstOrDefaultWithParam()
    method FirstOrDefaultAtTheEndWithParam (line 306) | public async Task<IActionResult> FirstOrDefaultAtTheEndWithParam()
    method FirstOrDefaultWithParams (line 317) | public async Task<IActionResult> FirstOrDefaultWithParams()
    method FirstOrDefaultWithParams2 (line 339) | public async Task<IActionResult> FirstOrDefaultWithParams2()
    method TestEnumsWithParams (line 352) | public async Task<IActionResult> TestEnumsWithParams()
    method TestInlineEnums (line 362) | public async Task<IActionResult> TestInlineEnums()
    method TestEFCachedDbSet (line 371) | public async Task<IActionResult> TestEFCachedDbSet()
    method TestIncludes (line 385) | public async Task<IActionResult> TestIncludes()
    method TestFind (line 401) | public async Task<IActionResult> TestFind()

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/Entities/Post.cs
  class Post (line 3) | public class Post
  class Page (line 12) | public class Page : Post

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/Entities/Product.cs
  class Product (line 5) | public class Product
    method Product (line 7) | public Product()

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/Entities/Tag.cs
  class Tag (line 5) | public class Tag
    method Tag (line 7) | public Tag()

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/Entities/TagProduct.cs
  class TagProduct (line 3) | public class TagProduct

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/Entities/User.cs
  class User (line 5) | public class User
    method User (line 7) | public User()
  type UserStatus (line 21) | public enum UserStatus

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/SampleContext.cs
  class SampleContext (line 11) | public class SampleContext : DbContext
    method SampleContext (line 21) | public SampleContext(DbContextOptions<SampleContext> options) : base(o...
    method OnModelCreating (line 24) | protected override void OnModelCreating(ModelBuilder modelBuilder)
    method SaveChanges (line 88) | public override int SaveChanges()
    method SaveChangesAsync (line 101) | public override Task<int> SaveChangesAsync(CancellationToken cancellat...

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/Utils/ApplicationDbContextSeedData.cs
  class ApplicationDbContextSeedData (line 7) | public static class ApplicationDbContextSeedData
    method SeedData (line 9) | public static void SeedData(this IServiceScopeFactory scopeFactory)

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/Utils/DBInitialization.cs
  class DbInitialization (line 6) | public static class DbInitialization
    method Initialize (line 8) | public static void Initialize(this IServiceScopeFactory scopeFactory)

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Migrations/20191022095356_V2019_10_22_1323.Designer.cs
  class V2019_10_22_1323 (line 11) | [DbContext(typeof(SampleContext))]
    method BuildTargetModel (line 15) | protected override void BuildTargetModel(ModelBuilder modelBuilder)

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Migrations/20191022095356_V2019_10_22_1323.cs
  class V2019_10_22_1323 (line 5) | public partial class V2019_10_22_1323 : Migration
    method Up (line 7) | protected override void Up(MigrationBuilder migrationBuilder)
    method Down (line 131) | protected override void Down(MigrationBuilder migrationBuilder)

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Migrations/SampleContextModelSnapshot.cs
  class SampleContextModelSnapshot (line 10) | [DbContext(typeof(SampleContext))]
    method BuildModel (line 13) | protected override void BuildModel(ModelBuilder modelBuilder)

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Models/PostDto.cs
  class PostDto (line 3) | public class PostDto

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Others/TestUtils.cs
  class TestUtils (line 13) | public static class TestUtils
    method DynamicGetWithCacheableAtFirst (line 15) | public static IEnumerable<T> DynamicGetWithCacheableAtFirst<T>(
    method DynamicGetWithCacheableAtEnd (line 34) | public static IEnumerable<T> DynamicGetWithCacheableAtEnd<T>(
    method GetFirstOrDefaultAsync (line 52) | public static async Task<TEntity> GetFirstOrDefaultAsync<TEntity, TDbC...

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Profiles/PostProfile.cs
  class PostProfile (line 7) | public class PostProfile : Profile
    method PostProfile (line 9) | public PostProfile()

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Program.cs
  class Program (line 6) | public class Program
    method Main (line 8) | public static void Main(string[] args)
    method CreateHostBuilder (line 13) | public static IHostBuilder CreateHostBuilder(string[] args) =>

FILE: src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Startup.cs
  class Startup (line 18) | public class Startup
    method Startup (line 22) | public Startup(IConfiguration configuration, IWebHostEnvironment env)
    method Configure (line 30) | public void Configure(
    method ConfigureServices (line 58) | public void ConfigureServices(IServiceCollection services)
    method addInMemoryCacheServiceProvider (line 96) | private static void addInMemoryCacheServiceProvider(IServiceCollection...
    method addRedisCacheServiceProvider (line 115) | private static void addRedisCacheServiceProvider(IServiceCollection se...

FILE: src/Tests/EFSecondLevelCache.Core.NET46Sample/EFSecondLevelCache.Core.NET46Sample/DataLayer/ConfigureServices.cs
  class ConfigureServices (line 9) | public static class ConfigureServices
    method GetEFCacheServiceProvider (line 19) | public static IEFCacheServiceProvider GetEFCacheServiceProvider()
    method getServiceProvider (line 24) | private static IServiceProvider getServiceProvider()

FILE: src/Tests/EFSecondLevelCache.Core.NET46Sample/EFSecondLevelCache.Core.NET46Sample/DataLayer/Entities/Post.cs
  class Post (line 3) | public class Post

FILE: src/Tests/EFSecondLevelCache.Core.NET46Sample/EFSecondLevelCache.Core.NET46Sample/DataLayer/SampleContext.cs
  class SampleContext (line 9) | public class SampleContext : DbContext
    method SaveChanges (line 15) | public override int SaveChanges()
    method SaveChangesAsync (line 30) | public override Task<int> SaveChangesAsync(CancellationToken cancellat...
    method OnConfiguring (line 44) | protected override void OnConfiguring(DbContextOptionsBuilder optionsB...

FILE: src/Tests/EFSecondLevelCache.Core.NET46Sample/EFSecondLevelCache.Core.NET46Sample/Program.cs
  class Program (line 8) | class Program
    method Main (line 10) | static void Main(string[] args)

FILE: src/Tests/EFSecondLevelCache.Core.PerformanceTests/BenchmarkTests.cs
  class BenchmarkTests (line 9) | public class BenchmarkTests
    method Setup (line 13) | [GlobalSetup]
    method SetupDatabase (line 20) | private static void SetupDatabase()
    method RunQueryDirectly (line 43) | [Benchmark(Baseline = true)]
    method RunCacheableQueryWithJsonSerializer (line 56) | [Benchmark]
    method RunCacheableQueryWithGzJsonSerializer (line 69) | [Benchmark]
    method RunCacheableQueryWithDictionaryHandle (line 82) | [Benchmark]
    method RunCacheableQueryWithMicrosoftMemoryCache (line 95) | [Benchmark]
    method GlobalCleanup (line 108) | [GlobalCleanup]

FILE: src/Tests/EFSecondLevelCache.Core.PerformanceTests/Program.cs
  class Program (line 9) | class Program
    method Main (line 11) | static void Main(string[] args)

FILE: src/Tests/EFSecondLevelCache.Core.PerformanceTests/SampleContext.cs
  class Student (line 8) | public class Student
  class SampleContext (line 14) | public class SampleContext : DbContext
    method SampleContext (line 18) | public SampleContext(DbContextOptions<SampleContext> options) : base(o...
    method OnModelCreating (line 21) | protected override void OnModelCreating(ModelBuilder modelBuilder)
    method SaveChanges (line 31) | public override int SaveChanges()

FILE: src/Tests/EFSecondLevelCache.Core.PerformanceTests/TestsServiceProvider.cs
  class TestsServiceProvider (line 15) | public static class TestsServiceProvider
    method getWithJsonSerializerServiceProvider (line 37) | private static IServiceProvider getWithJsonSerializerServiceProvider()
    method getWithGzJsonSerializerServiceProvider (line 42) | private static IServiceProvider getWithGzJsonSerializerServiceProvider()
    method getWithDictionaryHandleServiceProvider (line 47) | private static IServiceProvider getWithDictionaryHandleServiceProvider()
    method getWithMicrosoftMemoryCacheHandleServiceProvider (line 52) | private static IServiceProvider getWithMicrosoftMemoryCacheHandleServi...
    method addJsonSerializer (line 57) | private static void addJsonSerializer(ServiceCollection services)
    method addGzJsonSerializer (line 77) | private static void addGzJsonSerializer(ServiceCollection services)
    method addDictionaryHandle (line 97) | private static void addDictionaryHandle(ServiceCollection services)
    method addMicrosoftMemoryCacheHandle (line 109) | private static void addMicrosoftMemoryCacheHandle(ServiceCollection se...
    method createServiceProvider (line 121) | private static IServiceProvider createServiceProvider(Action<ServiceCo...
    method getConnectionString (line 135) | private static string getConnectionString()

FILE: src/Tests/EFSecondLevelCache.Core.Tests/EFCacheServiceProviderTests.cs
  class EFCacheServiceProviderTests (line 11) | [TestClass]
    method EFCacheServiceProviderTests (line 16) | public EFCacheServiceProviderTests()
    method ClearEFGlobalCacheBeforeEachTest (line 22) | [TestInitialize]
    method TestCacheInvalidationWithTwoRoots (line 28) | [TestMethod]
    method TestCacheInvalidationWithOneRoot (line 51) | [TestMethod]
    method TestObjectCacheInvalidationWithOneRoot (line 73) | [TestMethod]
    method TestCacheInvalidationWithSimilarRoots (line 99) | [TestMethod]
    method TestInsertingNullValues (line 121) | [TestMethod]
    method TestParallelInsertsAndRemoves (line 130) | [TestMethod]

FILE: src/Tests/EFSecondLevelCache.Core.Tests/EFCachedQueryProviderAsyncTests.cs
  class EFCachedQueryProviderAsyncTests (line 13) | [TestClass]
    method EFCachedQueryProviderAsyncTests (line 18) | public EFCachedQueryProviderAsyncTests()
    method ClearEFGlobalCacheBeforeEachTest (line 23) | [TestInitialize]
    method TestSecondLevelCacheUsingAsyncMethodsDoesNotHitTheDatabase (line 29) | [TestMethod]
    method TestSecondLevelCacheUsingDifferentAsyncMethods (line 106) | [TestMethod]
    method TestSecondLevelCacheUsingTwoCountAsyncMethods (line 173) | [TestMethod]
    method TestSecondLevelCacheUsingFindAsyncMethods (line 206) | [TestMethod]
    method TestParallelAsyncCalls (line 231) | [TestMethod]

FILE: src/Tests/EFSecondLevelCache.Core.Tests/EFCachedQueryProviderBasicTests.cs
  class EFCachedQueryProviderBasicTests (line 17) | [TestClass]
    method EFCachedQueryProviderBasicTests (line 22) | public EFCachedQueryProviderBasicTests()
    method ClearEFGlobalCacheBeforeEachTest (line 27) | [TestInitialize]
    method TestIncludeMethodAffectsKeyCache (line 33) | [TestMethod]
    method TestQueriesUsingDifferentParameterValuesWillNotUseTheCache (line 72) | [TestMethod]
    method TestSecondLevelCacheCreatesTheCommandTreeAfterCallingTheSameNormalQuery (line 122) | [TestMethod]
    method TestSecondLevelCacheDoesNotHitTheDatabase (line 168) | [TestMethod]
    method TestSecondLevelCacheInTwoDifferentContextsDoesNotHitTheDatabase (line 232) | [TestMethod]
    method TestSecondLevelCacheInTwoDifferentParallelContexts (line 280) | [TestMethod]
    method TestSecondLevelCacheUsingDifferentSyncMethods (line 329) | [TestMethod]
    method TestSecondLevelCacheUsingTwoCountMethods (line 396) | [TestMethod]
    method TestSecondLevelCacheUsingProjections (line 430) | [TestMethod]
    method TestSecondLevelCacheUsingFiltersAfterCacheableMethod (line 466) | [TestMethod]
    method TestEagerlyLoadingMultipleLevels (line 520) | [TestMethod]
    method TestIncludeMethodAndProjectionAffectsKeyCache (line 580) | [TestMethod]
    method TestParallelQueriesShouldCacheData (line 661) | [TestMethod]
    method TestSecondLevelCacheUsingFindMethods (line 684) | [TestMethod]
    method TestNullValuesWillUseTheCache (line 709) | [TestMethod]
    method TestEqualsMethodWillUseTheCache (line 739) | [TestMethod]
    method TestSecondLevelCacheDoesNotHitTheDatabaseForIQueryableCacheables (line 776) | [TestMethod]
    method Test2DifferentCollectionsWillNotUseTheCache (line 808) | [TestMethod]
    method TestSubqueriesWillUseTheCache (line 836) | [TestMethod]
    method TestSecondLevelCacheUsingProjectToMethods (line 873) | [TestMethod]

FILE: src/Tests/EFSecondLevelCache.Core.Tests/EFCachedQueryProviderInvalidationTests.cs
  class EFCachedQueryProviderInvalidationTests (line 12) | [TestClass]
    method EFCachedQueryProviderInvalidationTests (line 17) | public EFCachedQueryProviderInvalidationTests()
    method ClearEFGlobalCacheBeforeEachTest (line 22) | [TestInitialize]
    method TestInsertingDataIntoTheSameTableShouldInvalidateTheCacheAutomatically (line 28) | [TestMethod]
    method TestInsertingDataToOtherTablesShouldNotInvalidateTheCacheDependencyAutomatically (line 92) | [TestMethod]
    method TestInsertingDataToRelatedTablesShouldInvalidateTheCacheDependencyAutomatically (line 153) | [TestMethod]
    method TestTransactionRollbackShouldNotInvalidateTheCacheDependencyAutomatically (line 214) | [TestMethod]
    method TestRemoveDataShouldInvalidateTheCacheAutomatically (line 284) | [TestMethod]
    method TestRemoveTptDataShouldInvalidateTheCacheAutomatically (line 340) | [TestMethod]
    method TestAddThenRemoveDataShouldInvalidateTheCacheAutomatically (line 381) | [TestMethod]

FILE: src/Tests/EFSecondLevelCache.Core.Tests/TestsBase.cs
  class TestsBase (line 20) | public static class TestsBase
    method GetInMemoryCacheServiceProvider (line 22) | public static IEFCacheServiceProvider GetInMemoryCacheServiceProvider()
    method GetRedisCacheServiceProvider (line 33) | public static IEFCacheServiceProvider GetRedisCacheServiceProvider()
    method GetServiceProvider (line 44) | public static IServiceProvider GetServiceProvider()
    method ExecuteInParallel (line 77) | public static void ExecuteInParallel(Action test, int count = 40)
    method addInMemoryCacheServiceProvider (line 87) | private static void addInMemoryCacheServiceProvider(IServiceCollection...
    method addRedisCacheServiceProvider (line 100) | private static void addRedisCacheServiceProvider(IServiceCollection se...

FILE: src/Tests/EFSecondLevelCache.Core.Tests/XxHashTests.cs
  class TestConstants (line 5) | internal sealed class TestConstants
  class XxHashTests (line 12) | [TestClass]
    method TestEmptyXxHashReturnsCorrectValue (line 15) | [TestMethod]
    method TestFooBarXxHashReturnsCorrectValue (line 22) | [TestMethod]
    method TestLoremIpsumXxHashReturnsCorrectValue (line 29) | [TestMethod]

FILE: src/Tests/Issues/Issue15/ConfigureServices.cs
  class ConfigureServices (line 10) | public static class ConfigureServices
    method GetEFCacheServiceProvider (line 20) | public static IEFCacheServiceProvider GetEFCacheServiceProvider()
    method getServiceProvider (line 25) | private static IServiceProvider getServiceProvider()

FILE: src/Tests/Issues/Issue15/Payment.cs
  class Payment (line 3) | public class Payment

FILE: src/Tests/Issues/Issue15/Program.cs
  class Program (line 9) | class Program
    method Main (line 11) | static async Task Main(string[] args)
    method SetupDatabase (line 97) | private static void SetupDatabase()

FILE: src/Tests/Issues/Issue15/SampleContext.cs
  class SampleContext (line 10) | public class SampleContext : DbContext
    method SampleContext (line 17) | public SampleContext()
    method SampleContext (line 20) | public SampleContext(DbContextOptions options)
    method OnConfiguring (line 24) | protected override void OnConfiguring(DbContextOptionsBuilder optionsB...
    method OnModelCreating (line 31) | protected override void OnModelCreating(ModelBuilder modelBuilder)
    method SaveChanges (line 39) | public override int SaveChanges()
    method SaveChangesAsync (line 54) | public override Task<int> SaveChangesAsync(CancellationToken cancellat...

FILE: src/Tests/Issues/Issue43/Program.cs
  class ConfigureServices (line 15) | public static class ConfigureServices
    method GetEFCacheServiceProvider (line 25) | public static IEFCacheServiceProvider GetEFCacheServiceProvider()
    method getServiceProvider (line 30) | private static IServiceProvider getServiceProvider()
  class User (line 49) | public class User
  class SampleContext (line 55) | public class SampleContext : DbContext
    method SampleContext (line 62) | public SampleContext()
    method SampleContext (line 66) | public SampleContext(DbContextOptions options)
    method OnConfiguring (line 71) | protected override void OnConfiguring(DbContextOptionsBuilder optionsB...
    method SaveChanges (line 78) | public override int SaveChanges()
    method SaveChangesAsync (line 93) | public override Task<int> SaveChangesAsync(CancellationToken cancellat...
  class Program (line 109) | class Program
    method Main (line 111) | static async Task Main(string[] args)
    method SetupDatabase (line 193) | private static void SetupDatabase()
Condensed preview — 107 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (429K chars).
[
  {
    "path": ".gitattributes",
    "chars": 2518,
    "preview": "###############################################################################\n# Set default behavior to automatically "
  },
  {
    "path": ".github/issue_template.md",
    "chars": 274,
    "preview": "# Summary of the issue\n\n\n\n## Environment\n\n```\n.NET Core SDK version: \nMicrosoft.EntityFrameworkCore version: \nEFSecondLe"
  },
  {
    "path": ".github/lock.yml",
    "chars": 279,
    "preview": "daysUntilLock: 90\n\nskipCreatedBefore: false\n\nexemptLabels: []\n\nlockLabel: false\n\nlockComment: >\n  This thread has been a"
  },
  {
    "path": ".github/workflows/build.yml",
    "chars": 390,
    "preview": "name: .NET Core Build\n\non: [push, pull_request]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: acti"
  },
  {
    "path": ".gitignore",
    "chars": 3918,
    "preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 2178,
    "preview": "{\n    // Use IntelliSense to find out which attributes exist for C# debugging\n    // Use hover for the description of th"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 709,
    "preview": "{\n    \"workbench.colorCustomizations\": {\n        \"activityBar.background\": \"#7295b1\",\n        \"activityBar.activeBorder\""
  },
  {
    "path": ".vscode/tasks.json",
    "chars": 420,
    "preview": "{\n    \"version\": \"0.1.0\",\n    \"command\": \"dotnet\",\n    \"isShellCommand\": true,\n    \"args\": [],\n    \"tasks\": [\n        {\n"
  },
  {
    "path": "EFSecondLevelCache.Core.sln",
    "chars": 7650,
    "preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 15\nVisualStudioVersion = 15.0.26228.4\nMini"
  },
  {
    "path": "LICENSE.md",
    "chars": 11325,
    "preview": "Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licens"
  },
  {
    "path": "README.md",
    "chars": 7708,
    "preview": "# [Announcing a better Second Level Caching Library!](https://github.com/VahidN/EFSecondLevelCache.Core/issues/67)\r\n\r\n# "
  },
  {
    "path": "global.json",
    "chars": 43,
    "preview": "{\n  \"sdk\": {\n    \"version\": \"3.1.101\"\n  }\n}"
  },
  {
    "path": "src/EFSecondLevelCache.Core/Contracts/EFCacheDebugInfo.cs",
    "chars": 526,
    "preview": "namespace EFSecondLevelCache.Core.Contracts\n{\n    /// <summary>\n    /// Stores the debug information of the caching pro"
  },
  {
    "path": "src/EFSecondLevelCache.Core/Contracts/EFCacheKey.cs",
    "chars": 1727,
    "preview": "using System.Collections.Generic;\n\nnamespace EFSecondLevelCache.Core.Contracts\n{\n    /// <summary>\n    /// Stores inform"
  },
  {
    "path": "src/EFSecondLevelCache.Core/Contracts/EFQueryDebugView.cs",
    "chars": 467,
    "preview": "using System.Collections.Generic;\n\nnamespace EFSecondLevelCache.Core.Contracts\n{\n    /// <summary>\n    /// Expression a"
  },
  {
    "path": "src/EFSecondLevelCache.Core/Contracts/IEFCacheKeyHashProvider.cs",
    "chars": 428,
    "preview": "namespace EFSecondLevelCache.Core.Contracts\n{\n    /// <summary>\n    /// The CacheKey Hash Provider Contract.\n    /// </s"
  },
  {
    "path": "src/EFSecondLevelCache.Core/Contracts/IEFCacheKeyProvider.cs",
    "chars": 790,
    "preview": "using System.Linq;\nusing System.Linq.Expressions;\n\nnamespace EFSecondLevelCache.Core.Contracts\n{\n    /// <summary>\n    /"
  },
  {
    "path": "src/EFSecondLevelCache.Core/Contracts/IEFCacheServiceProvider.cs",
    "chars": 1703,
    "preview": "using System.Collections.Generic;\n\nnamespace EFSecondLevelCache.Core.Contracts\n{\n    /// <summary>\n    /// Cache Servic"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFAsyncEnumerable.cs",
    "chars": 1133,
    "preview": "using System.Collections.Generic;\nusing System.Threading;\n\nnamespace EFSecondLevelCache.Core\n{\n    /// <summary>\n    //"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFAsyncEnumerator.cs",
    "chars": 2704,
    "preview": "using System.Collections.Generic;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace EFSecondLevelCache.C"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFAsyncTaskEnumerable.cs",
    "chars": 1124,
    "preview": "using System.Collections.Generic;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace EFSecondLevelCache.C"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFAsyncTaskEnumerator.cs",
    "chars": 1871,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace EFSeco"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFCacheKeyHashProvider.cs",
    "chars": 749,
    "preview": "using System;\nusing EFSecondLevelCache.Core.Contracts;\n\nnamespace EFSecondLevelCache.Core\n{\n    /// <summary>\n    /// C"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFCacheKeyProvider3x.cs",
    "chars": 4906,
    "preview": "using System.Linq;\nusing System.Linq.Expressions;\nusing EFSecondLevelCache.Core.Contracts;\nusing System;\nusing CacheMana"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFCachePolicy.cs",
    "chars": 2779,
    "preview": "using System;\n\nnamespace EFSecondLevelCache.Core\n{\n    /// <summary>\n    /// Defines the supported expiration modes for "
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFCacheServiceProvider.cs",
    "chars": 5429,
    "preview": "using System.Collections.Generic;\nusing System.Threading;\nusing Microsoft.Extensions.DependencyInjection;\nusing CacheMa"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFCachedDbSet.cs",
    "chars": 4958,
    "preview": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Linq.Expressio"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFCachedDbSetExtensions.cs",
    "chars": 4564,
    "preview": "using System;\nusing System.Linq;\nusing System.Linq.Expressions;\nusing System.Threading;\nusing System.Threading.Tasks;\nus"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFCachedQueryExtensions.cs",
    "chars": 23941,
    "preview": "using System;\nusing System.Linq;\nusing EFSecondLevelCache.Core.Contracts;\nusing Microsoft.EntityFrameworkCore;\nusing Mic"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFCachedQueryProvider.cs",
    "chars": 7365,
    "preview": "using System;\nusing System.Linq;\nusing System.Linq.Expressions;\nusing System.Reflection;\nusing System.Threading;\nusing "
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFCachedQueryable.cs",
    "chars": 5034,
    "preview": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Linq.Expressi"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFChangeTrackerExtensions.cs",
    "chars": 3777,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Linq.Expressions;\nusing System.Reflecti"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFMaterializer.cs",
    "chars": 5965,
    "preview": "using System;\nusing System.Linq;\nusing System.Linq.Expressions;\nusing System.Threading;\nusing System.Threading.Tasks;\nus"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFQueryExpressionVisitor.cs",
    "chars": 48420,
    "preview": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Global"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFSecondLevelCache.Core.csproj",
    "chars": 2127,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <Description>Entity Framework Core Second Level Caching Library"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFServiceCollectionExtensions.cs",
    "chars": 1010,
    "preview": "using System;\nusing EFSecondLevelCache.Core.Contracts;\nusing Microsoft.Extensions.DependencyInjection;\n\nnamespace EFSec"
  },
  {
    "path": "src/EFSecondLevelCache.Core/EFStaticServiceProvider.cs",
    "chars": 1072,
    "preview": "using System;\nusing System.Threading;\nusing Microsoft.Extensions.DependencyInjection;\n\nnamespace EFSecondLevelCache.Cor"
  },
  {
    "path": "src/EFSecondLevelCache.Core/ParallelExtensions.cs",
    "chars": 1452,
    "preview": "using System;\nusing System.Threading;\n\nnamespace EFSecondLevelCache.Core\n{\n    /// <summary>\n    /// Reader writer locki"
  },
  {
    "path": "src/EFSecondLevelCache.Core/Properties/AssemblyInfo.cs",
    "chars": 794,
    "preview": "using System.Reflection;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled "
  },
  {
    "path": "src/EFSecondLevelCache.Core/XxHashUnsafe.cs",
    "chars": 4634,
    "preview": "\nusing System;\n\nnamespace EFSecondLevelCache.Core\n{\n    /// <summary>\n    /// xxHash is an extremely fast non-cryptogra"
  },
  {
    "path": "src/EFSecondLevelCache.Core/_0-restore.bat",
    "chars": 52,
    "preview": "rmdir /S /Q bin\nrmdir /S /Q obj\ndotnet restore\npause"
  },
  {
    "path": "src/EFSecondLevelCache.Core/_1-dotnet_pack.bat",
    "chars": 28,
    "preview": "dotnet pack -c release\npause"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/App_Data/.gitkeep.txt",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Controllers/HomeController.cs",
    "chars": 15834,
    "preview": "using System.Linq;\nusing EFSecondLevelCache.Core.AspNetCoreSample.DataLayer;\nusing Microsoft.AspNetCore.Mvc;\nusing EFSe"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/Entities/Post.cs",
    "chars": 317,
    "preview": "namespace EFSecondLevelCache.Core.AspNetCoreSample.DataLayer.Entities\n{\n    public class Post\n    {\n        public int "
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/Entities/Product.cs",
    "chars": 634,
    "preview": "using System.Collections.Generic;\n\nnamespace EFSecondLevelCache.Core.AspNetCoreSample.DataLayer.Entities\n{\n    public c"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/Entities/Tag.cs",
    "chars": 389,
    "preview": "using System.Collections.Generic;\n\nnamespace EFSecondLevelCache.Core.AspNetCoreSample.DataLayer.Entities\n{\n    public c"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/Entities/TagProduct.cs",
    "chars": 303,
    "preview": "namespace EFSecondLevelCache.Core.AspNetCoreSample.DataLayer.Entities\n{\n    public class TagProduct\n    {\n        publi"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/Entities/User.cs",
    "chars": 605,
    "preview": "using System.Collections.Generic;\n\nnamespace EFSecondLevelCache.Core.AspNetCoreSample.DataLayer.Entities\n{\n    public c"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/SampleContext.cs",
    "chars": 3967,
    "preview": "using System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing EFSecondLevelCache.Core.AspNetCoreSample"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/Utils/ApplicationDbContextSeedData.cs",
    "chars": 5310,
    "preview": "using System.Linq;\nusing EFSecondLevelCache.Core.AspNetCoreSample.DataLayer.Entities;\nusing Microsoft.Extensions.Depend"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/DataLayer/Utils/DBInitialization.cs",
    "chars": 691,
    "preview": "using Microsoft.EntityFrameworkCore;\nusing Microsoft.Extensions.DependencyInjection;\n\nnamespace EFSecondLevelCache.Core"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/EFSecondLevelCache.Core.AspNetCoreSample.csproj",
    "chars": 1831,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n  <PropertyGroup>\n    <TargetFramework>netcoreapp3.1</TargetFramework>\n    <Prese"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Migrations/20191022095356_V2019_10_22_1323.Designer.cs",
    "chars": 7047,
    "preview": "// <auto-generated />\nusing EFSecondLevelCache.Core.AspNetCoreSample.DataLayer;\nusing Microsoft.EntityFrameworkCore;\nus"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Migrations/20191022095356_V2019_10_22_1323.cs",
    "chars": 5733,
    "preview": "using Microsoft.EntityFrameworkCore.Migrations;\n\nnamespace EFSecondLevelCache.Core.AspNetCoreSample.Migrations\n{\n    pu"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Migrations/SampleContextModelSnapshot.cs",
    "chars": 6968,
    "preview": "// <auto-generated />\nusing EFSecondLevelCache.Core.AspNetCoreSample.DataLayer;\nusing Microsoft.EntityFrameworkCore;\nus"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Models/PostDto.cs",
    "chars": 183,
    "preview": "namespace EFSecondLevelCache.Core.AspNetCoreSample.Models\n{\n    public class PostDto\n    {\n        public string Title {"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Others/TestUtils.cs",
    "chars": 3236,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Linq.Expressions;\nusing System.Threading"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Profiles/PostProfile.cs",
    "chars": 529,
    "preview": "using AutoMapper;\nusing EFSecondLevelCache.Core.AspNetCoreSample.DataLayer.Entities;\nusing EFSecondLevelCache.Core.AspNe"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Program.cs",
    "chars": 547,
    "preview": "using Microsoft.AspNetCore.Hosting;\nusing Microsoft.Extensions.Hosting;\n\nnamespace EFSecondLevelCache.Core.AspNetCoreSa"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Properties/launchSettings.json",
    "chars": 648,
    "preview": "{\n  \"iisSettings\": {\n    \"windowsAuthentication\": false, \n    \"anonymousAuthentication\": true, \n    \"iisExpress\": {\n    "
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/Startup.cs",
    "chars": 6006,
    "preview": "using System;\nusing AutoMapper;\nusing CacheManager.Core;\nusing EFSecondLevelCache.Core.AspNetCoreSample.DataLayer;\nusin"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/_0-restore.bat",
    "chars": 52,
    "preview": "rmdir /S /Q bin\nrmdir /S /Q obj\ndotnet restore\npause"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/_1-dotnet_run.bat",
    "chars": 16,
    "preview": "dotnet watch run"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/_update_db.bat",
    "chars": 299,
    "preview": "For /f \"tokens=2-4 delims=/ \" %%a in ('date /t') do (set mydate=%%c_%%a_%%b)\nFor /f \"tokens=1-2 delims=/:\" %%a in (\"%TIM"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/appsettings.json",
    "chars": 487,
    "preview": "{\n    \"Logging\": {\n        \"IncludeScopes\": false,\n        \"LogLevel\": {\n            \"Default\": \"Debug\",\n            \"S"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/web.config",
    "chars": 549,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n\n  <!--\n    Configure your application settings in appsettings.j"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.AspNetCoreSample/wwwroot/App_Data/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.NET46Sample/EFSecondLevelCache.Core.NET46Sample/App.config",
    "chars": 2274,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".NE"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.NET46Sample/EFSecondLevelCache.Core.NET46Sample/DataLayer/ConfigureServices.cs",
    "chars": 1803,
    "preview": "using System;\nusing CacheManager.Core;\nusing Microsoft.Extensions.DependencyInjection;\nusing System.Threading;\nusing EF"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.NET46Sample/EFSecondLevelCache.Core.NET46Sample/DataLayer/Entities/Post.cs",
    "chars": 181,
    "preview": "namespace EFSecondLevelCache.Core.NET46Sample.DataLayer.Entities\n{\n    public class Post\n    {\n        public int Id { "
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.NET46Sample/EFSecondLevelCache.Core.NET46Sample/DataLayer/SampleContext.cs",
    "chars": 1826,
    "preview": "using System.Threading;\nusing System.Threading.Tasks;\nusing EFSecondLevelCache.Core.Contracts;\nusing EFSecondLevelCache"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.NET46Sample/EFSecondLevelCache.Core.NET46Sample/EFSecondLevelCache.Core.NET46Sample.csproj",
    "chars": 16915,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"14.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.micros"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.NET46Sample/EFSecondLevelCache.Core.NET46Sample/Program.cs",
    "chars": 598,
    "preview": "using System;\nusing System.Linq;\nusing EFSecondLevelCache.Core.NET46Sample.DataLayer;\nusing EFSecondLevelCache.Core.NET"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.NET46Sample/EFSecondLevelCache.Core.NET46Sample/Properties/AssemblyInfo.cs",
    "chars": 1404,
    "preview": "using System.Reflection;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled "
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.NET46Sample/EFSecondLevelCache.Core.NET46Sample/packages.config",
    "chars": 6584,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n  <package id=\"CacheManager.Core\" version=\"1.0.0\" targetFramework=\"ne"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.PerformanceTests/BenchmarkTests.cs",
    "chars": 4141,
    "preview": "using System;\nusing System.Linq;\nusing BenchmarkDotNet.Attributes;\nusing BenchmarkDotNet.Order;\nusing Microsoft.Extensi"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.PerformanceTests/EFSecondLevelCache.Core.PerformanceTests.csproj",
    "chars": 902,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>netcoreapp3.1<"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.PerformanceTests/Program.cs",
    "chars": 1269,
    "preview": "using BenchmarkDotNet.Columns;\nusing BenchmarkDotNet.Configs;\nusing BenchmarkDotNet.Horology;\nusing BenchmarkDotNet.Job"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.PerformanceTests/SampleContext.cs",
    "chars": 1365,
    "preview": "using EFSecondLevelCache.Core.Contracts;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.Extensions.DependencyInjec"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.PerformanceTests/TestsServiceProvider.cs",
    "chars": 6908,
    "preview": "using System;\nusing System.IO;\nusing CacheManager.Core;\nusing EFSecondLevelCache.Core.Contracts;\nusing Microsoft.Entity"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.PerformanceTests/_0-restore.bat",
    "chars": 52,
    "preview": "rmdir /S /Q bin\nrmdir /S /Q obj\ndotnet restore\npause"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.PerformanceTests/_1-dotnet_run.bat",
    "chars": 27,
    "preview": "dotnet watch run -c Release"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.PerformanceTests/app_data/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.Tests/EFCacheServiceProviderTests.cs",
    "chars": 5539,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing EFSecondLevelCac"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.Tests/EFCachedQueryProviderAsyncTests.cs",
    "chars": 11782,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing EFSecondLevelCac"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.Tests/EFCachedQueryProviderBasicTests.cs",
    "chars": 45866,
    "preview": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tas"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.Tests/EFCachedQueryProviderInvalidationTests.cs",
    "chars": 22709,
    "preview": "using System;\nusing System.Linq;\nusing EFSecondLevelCache.Core.AspNetCoreSample.DataLayer;\nusing EFSecondLevelCache.Cor"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.Tests/EFSecondLevelCache.Core.Tests.csproj",
    "chars": 1822,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <TargetFramework>netcoreapp3.1</TargetFramework>\n    <AssemblyN"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.Tests/Properties/AssemblyInfo.cs",
    "chars": 800,
    "preview": "using System.Reflection;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled "
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.Tests/TestsBase.cs",
    "chars": 5684,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Reflection;\nusing System.Threading.Tasks;\nusing AutoMapper"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.Tests/XxHashTests.cs",
    "chars": 1196,
    "preview": "using Microsoft.VisualStudio.TestTools.UnitTesting;\n\nnamespace EFSecondLevelCache.Core.Tests\n{\n    internal sealed clas"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.Tests/_0-restore.bat",
    "chars": 52,
    "preview": "rmdir /S /Q bin\nrmdir /S /Q obj\ndotnet restore\npause"
  },
  {
    "path": "src/Tests/EFSecondLevelCache.Core.Tests/_1-dotnet_test.bat",
    "chars": 17,
    "preview": "dotnet test\npause"
  },
  {
    "path": "src/Tests/Issues/Issue15/ConfigureServices.cs",
    "chars": 1662,
    "preview": "using System;\nusing System.Threading;\nusing CacheManager.Core;\nusing EFSecondLevelCache.Core;\nusing EFSecondLevelCache.C"
  },
  {
    "path": "src/Tests/Issues/Issue15/Issue15.csproj",
    "chars": 1549,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>netcoreapp3.1<"
  },
  {
    "path": "src/Tests/Issues/Issue15/Payment.cs",
    "chars": 187,
    "preview": "namespace Issue15\n{\n    public class Payment\n    {\n        public int Id { set; get; }\n        public int IdPaymentType "
  },
  {
    "path": "src/Tests/Issues/Issue15/Program.cs",
    "chars": 5078,
    "preview": "using System;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing EFSecondLevelCache.Core;\nusing Microsoft.EntityFra"
  },
  {
    "path": "src/Tests/Issues/Issue15/SampleContext.cs",
    "chars": 2528,
    "preview": "using System.IO;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing EFSecondLevelCache.Core;\nusing EFSecondLeve"
  },
  {
    "path": "src/Tests/Issues/Issue15/app_data/.git.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/Tests/Issues/Issue43/Issue43.csproj",
    "chars": 1511,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>netcoreapp3.1<"
  },
  {
    "path": "src/Tests/Issues/Issue43/Program.cs",
    "chars": 9894,
    "preview": "using System.IO;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing EFSecondLevelCache.Core;\nusing EFSecondLev"
  },
  {
    "path": "src/Tests/Issues/Issue43/_0-restore.bat",
    "chars": 52,
    "preview": "rmdir /S /Q bin\nrmdir /S /Q obj\ndotnet restore\npause"
  },
  {
    "path": "src/Tests/Issues/Issue43/_1-dotnet_run.bat",
    "chars": 16,
    "preview": "dotnet watch run"
  },
  {
    "path": "src/Tests/Issues/Issue43/app_data/.git.keep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tag-it.bat",
    "chars": 79,
    "preview": "git tag -a 2.9.0 -m \"Published 2.9.0 to nuget.org\"\ngit push --follow-tags\npause"
  },
  {
    "path": "update-dependencies.bat",
    "chars": 77,
    "preview": "dotnet tool update -g dotnet-outdated\ndotnet outdated -u\ndotnet restore\npause"
  }
]

About this extraction

This page contains the full source code of the VahidN/EFSecondLevelCache.Core GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 107 files (396.0 KB), approximately 86.6k tokens, and a symbol index with 398 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!