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:
[](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
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
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.