diff --git a/.github/workflows/TestCosmos.yaml b/.github/workflows/TestCosmos.yaml
index 735b4ca4cfc..dec6cb5e44c 100644
--- a/.github/workflows/TestCosmos.yaml
+++ b/.github/workflows/TestCosmos.yaml
@@ -17,7 +17,7 @@ jobs:
- name: Start Cosmos Emulator
run: |
Import-Module "$env:ProgramFiles\Azure Cosmos DB Emulator\PSModules\Microsoft.Azure.CosmosDB.Emulator"
- Start-CosmosDbEmulator -Timeout 360
+ Start-CosmosDbEmulator -Timeout 540 -NoUI -NoTelemetry -NoFirewall -EnablePreview
- name: Checkout
uses: actions/checkout@v4
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 01edcee871c..3e248f71184 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -32,6 +32,7 @@
+
@@ -54,12 +55,5 @@
-
-
-
-
-
-
-
diff --git a/EFCore.sln b/EFCore.sln
index b835bb26b56..0999c19305c 100644
--- a/EFCore.sln
+++ b/EFCore.sln
@@ -14,6 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
global.json = global.json
tools\Resources.tt = tools\Resources.tt
eng\Versions.props = eng\Versions.props
+ NuGet.config = NuGet.config
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}"
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 334a076e526..b4dc09de049 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -150,7 +150,7 @@ extends:
steps:
- template: /eng/common/templates-official/steps/enable-internal-sources.yml
- template: /eng/common/templates-official/steps/enable-internal-runtimes.yml
- - script: eng/common/cibuild.sh --configuration $(_BuildConfig) --prepareMachine $(_InternalRuntimeDownloadArgs)
+ - script: eng/common/build.sh --restore --build --test --pack --ci --configuration $(_BuildConfig) --prepareMachine $(_InternalRuntimeDownloadArgs)
env:
Test__Cosmos__DefaultConnection: $(_CosmosConnectionUrl)
# Work-around for https://github.com/dotnet/runtime/issues/70758
@@ -186,7 +186,7 @@ extends:
condition: and(eq(variables['_CosmosConnectionUrl'], 'true'), or(endsWith(variables['_runCounter'], '1'), endsWith(variables['_runCounter'], '3'), endsWith(variables['_runCounter'], '5'), endsWith(variables['_runCounter'], '7'), endsWith(variables['_runCounter'], '9')))
- template: /eng/common/templates-official/steps/enable-internal-sources.yml
- template: /eng/common/templates-official/steps/enable-internal-runtimes.yml
- - script: eng/common/cibuild.sh --configuration $(_BuildConfig) --prepareMachine $(_InternalRuntimeDownloadArgs)
+ - script: eng/common/build.sh --restore --build --test --pack --ci --configuration $(_BuildConfig) --prepareMachine $(_InternalRuntimeDownloadArgs)
displayName: Build
- task: AzureCLI@2
displayName: Run Cosmos tests
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index dbb49ebc941..46f81485861 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -1,83 +1,83 @@
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/runtime
- 088d199063dd1bf7fa00a445d23f93f915f84b31
+ a50b4a078439c518d1074c43ff4740f528d37fea
-
+ https://github.com/dotnet/arcade
- 6d4b01bce3a29c172faff5abc0bfe2ae3d1fef3d
+ 235443a5c1136571cacdfd40576f263f26bf5b9b
-
+ https://github.com/dotnet/arcade
- 6d4b01bce3a29c172faff5abc0bfe2ae3d1fef3d
+ 235443a5c1136571cacdfd40576f263f26bf5b9b
-
+ https://github.com/dotnet/arcade
- 6d4b01bce3a29c172faff5abc0bfe2ae3d1fef3d
+ 235443a5c1136571cacdfd40576f263f26bf5b9b
diff --git a/eng/Versions.props b/eng/Versions.props
index 78f8e60baa5..e6ff4d0969d 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -2,7 +2,7 @@
10.0.0preview
- 3
+ 4Falsetrue
- 4.8.0
- 1.1.2
+ 17.13.9
+ 17.13.9
+
+ 4.13.0
+ 1.1.3-beta1.24423.1
+ 1.1.3-beta1.24352.11.13.11.3.21.8.1
diff --git a/eng/common/CIBuild.cmd b/eng/common/CIBuild.cmd
index 56c2f25ac22..ac1f72bf94e 100644
--- a/eng/common/CIBuild.cmd
+++ b/eng/common/CIBuild.cmd
@@ -1,2 +1,2 @@
@echo off
-powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0Build.ps1""" -restore -build -test -sign -pack -publish -ci %*"
\ No newline at end of file
+powershell -ExecutionPolicy ByPass -NoProfile -command "& """%~dp0Build.ps1""" -restore -build -test -sign -pack -publish -ci %*"
diff --git a/eng/common/build.ps1 b/eng/common/build.ps1
index 438f9920c43..d419c3a2eff 100644
--- a/eng/common/build.ps1
+++ b/eng/common/build.ps1
@@ -7,6 +7,7 @@ Param(
[string] $msbuildEngine = $null,
[bool] $warnAsError = $true,
[bool] $nodeReuse = $true,
+ [switch] $buildCheck = $false,
[switch][Alias('r')]$restore,
[switch] $deployDeps,
[switch][Alias('b')]$build,
@@ -71,6 +72,8 @@ function Print-Usage() {
Write-Host " -msbuildEngine Msbuild engine to use to run build ('dotnet', 'vs', or unspecified)."
Write-Host " -excludePrereleaseVS Set to exclude build engines in prerelease versions of Visual Studio"
Write-Host " -nativeToolsOnMachine Sets the native tools on machine environment variable (indicating that the script should use native tools on machine)"
+ Write-Host " -nodeReuse Sets nodereuse msbuild parameter ('true' or 'false')"
+ Write-Host " -buildCheck Sets /check msbuild parameter"
Write-Host ""
Write-Host "Command line arguments not listed above are passed thru to msbuild."
@@ -97,6 +100,7 @@ function Build {
$bl = if ($binaryLog) { '/bl:' + (Join-Path $LogDir 'Build.binlog') } else { '' }
$platformArg = if ($platform) { "/p:Platform=$platform" } else { '' }
+ $check = if ($buildCheck) { '/check' } else { '' }
if ($projects) {
# Re-assign properties to a new variable because PowerShell doesn't let us append properties directly for unclear reasons.
@@ -113,6 +117,7 @@ function Build {
MSBuild $toolsetBuildProj `
$bl `
$platformArg `
+ $check `
/p:Configuration=$configuration `
/p:RepoRoot=$RepoRoot `
/p:Restore=$restore `
@@ -166,4 +171,4 @@ catch {
ExitWithExitCode 1
}
-ExitWithExitCode 0
+ExitWithExitCode 0
\ No newline at end of file
diff --git a/eng/common/build.sh b/eng/common/build.sh
index 483647daf18..e24bb68f484 100755
--- a/eng/common/build.sh
+++ b/eng/common/build.sh
@@ -42,6 +42,7 @@ usage()
echo " --prepareMachine Prepare machine for CI run, clean up processes after build"
echo " --nodeReuse Sets nodereuse msbuild parameter ('true' or 'false')"
echo " --warnAsError Sets warnaserror msbuild parameter ('true' or 'false')"
+ echo " --buildCheck Sets /check msbuild parameter"
echo ""
echo "Command line arguments not listed above are passed thru to msbuild."
echo "Arguments can also be passed in with a single hyphen."
@@ -76,6 +77,7 @@ clean=false
warn_as_error=true
node_reuse=true
+build_check=false
binary_log=false
exclude_ci_binary_log=false
pipelines_log=false
@@ -173,6 +175,9 @@ while [[ $# > 0 ]]; do
node_reuse=$2
shift
;;
+ -buildcheck)
+ build_check=true
+ ;;
-runtimesourcefeed)
runtime_source_feed=$2
shift
@@ -224,8 +229,14 @@ function Build {
bl="/bl:\"$log_dir/Build.binlog\""
fi
+ local check=""
+ if [[ "$build_check" == true ]]; then
+ check="/check"
+ fi
+
MSBuild $_InitializeToolset \
$bl \
+ $check \
/p:Configuration=$configuration \
/p:RepoRoot="$repo_root" \
/p:Restore=$restore \
@@ -256,4 +267,4 @@ if [[ "$restore" == true ]]; then
InitializeNativeTools
fi
-Build
+Build
\ No newline at end of file
diff --git a/eng/common/cibuild.sh b/eng/common/cibuild.sh
index 1a02c0dec8f..66e3b0ac61c 100755
--- a/eng/common/cibuild.sh
+++ b/eng/common/cibuild.sh
@@ -13,4 +13,4 @@ while [[ -h $source ]]; do
done
scriptroot="$( cd -P "$( dirname "$source" )" && pwd )"
-. "$scriptroot/build.sh" --restore --build --test --pack --publish --ci $@
\ No newline at end of file
+. "$scriptroot/build.sh" --restore --build --test --pack --publish --ci $@
diff --git a/eng/common/core-templates/job/job.yml b/eng/common/core-templates/job/job.yml
index 295c9a2317c..4dfb3d86836 100644
--- a/eng/common/core-templates/job/job.yml
+++ b/eng/common/core-templates/job/job.yml
@@ -23,7 +23,6 @@ parameters:
enablePublishBuildArtifacts: false
enablePublishBuildAssets: false
enablePublishTestResults: false
- enablePublishUsingPipelines: false
enableBuildRetry: false
mergeTestResults: false
testRunTitle: ''
diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml
index c7d59dcbfe1..5da3c2a2a20 100644
--- a/eng/common/core-templates/job/publish-build-assets.yml
+++ b/eng/common/core-templates/job/publish-build-assets.yml
@@ -20,9 +20,6 @@ parameters:
# if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects.
runAsPublic: false
- # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing
- publishUsingPipelines: false
-
# Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing
publishAssetsImmediately: false
@@ -96,7 +93,6 @@ jobs:
arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet
/p:ManifestsPath='$(Build.StagingDirectory)/AssetManifests'
/p:MaestroApiEndpoint=https://maestro.dot.net
- /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }}
/p:OfficialBuildId=$(Build.BuildNumber)
condition: ${{ parameters.condition }}
continueOnError: ${{ parameters.continueOnError }}
diff --git a/eng/common/core-templates/jobs/codeql-build.yml b/eng/common/core-templates/jobs/codeql-build.yml
index f2144252cc6..693b00b3704 100644
--- a/eng/common/core-templates/jobs/codeql-build.yml
+++ b/eng/common/core-templates/jobs/codeql-build.yml
@@ -15,7 +15,6 @@ jobs:
enablePublishBuildArtifacts: false
enablePublishTestResults: false
enablePublishBuildAssets: false
- enablePublishUsingPipelines: false
enableTelemetry: true
variables:
diff --git a/eng/common/core-templates/jobs/jobs.yml b/eng/common/core-templates/jobs/jobs.yml
index ea69be4341c..3d2deaa4a06 100644
--- a/eng/common/core-templates/jobs/jobs.yml
+++ b/eng/common/core-templates/jobs/jobs.yml
@@ -5,9 +5,6 @@ parameters:
# Optional: Include PublishBuildArtifacts task
enablePublishBuildArtifacts: false
- # Optional: Enable publishing using release pipelines
- enablePublishUsingPipelines: false
-
# Optional: Enable running the source-build jobs to build repo from source
enableSourceBuild: false
@@ -112,7 +109,6 @@ jobs:
- Source_Build_Complete
runAsPublic: ${{ parameters.runAsPublic }}
- publishUsingPipelines: ${{ parameters.enablePublishUsingPipelines }}
publishAssetsImmediately: ${{ parameters.publishAssetsImmediately }}
enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }}
artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }}
diff --git a/eng/common/core-templates/steps/install-microbuild.yml b/eng/common/core-templates/steps/install-microbuild.yml
index 2a6a529482b..dba506e74c3 100644
--- a/eng/common/core-templates/steps/install-microbuild.yml
+++ b/eng/common/core-templates/steps/install-microbuild.yml
@@ -5,7 +5,7 @@ parameters:
# Will be ignored if 'enableMicrobuild' is false or 'Agent.Os' is 'Windows_NT'
enableMicrobuildForMacAndLinux: false
# Location of the MicroBuild output folder
- microBuildOutputFolder: '$(Agent.TempDirectory)'
+ microBuildOutputFolder: '$(Build.SourcesDirectory)'
continueOnError: false
steps:
@@ -41,7 +41,7 @@ steps:
inputs:
packageType: sdk
version: 8.0.x
- installationPath: ${{ parameters.microBuildOutputFolder }}/dotnet
+ installationPath: ${{ parameters.microBuildOutputFolder }}/.dotnet
workingDirectory: ${{ parameters.microBuildOutputFolder }}
condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT'))
@@ -53,6 +53,7 @@ steps:
feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json
${{ if and(eq(parameters.enableMicrobuildForMacAndLinux, 'true'), ne(variables['Agent.Os'], 'Windows_NT')) }}:
azureSubscription: 'MicroBuild Signing Task (DevDiv)'
+ useEsrpCli: true
env:
TeamName: $(_TeamName)
MicroBuildOutputFolderOverride: ${{ parameters.microBuildOutputFolder }}
@@ -71,3 +72,28 @@ steps:
eq(variables['_SignType'], 'real')
)
))
+
+ # Workaround for ESRP CLI on Linux - https://github.com/dotnet/source-build/issues/4964
+ - ${{ if eq(parameters.enableMicrobuildForMacAndLinux, 'true') }}:
+ - task: UseDotNet@2
+ displayName: Install .NET 9.0 SDK for ESRP CLI Workaround
+ inputs:
+ packageType: sdk
+ version: 9.0.x
+ installationPath: ${{ parameters.microBuildOutputFolder }}/.dotnet
+ workingDirectory: ${{ parameters.microBuildOutputFolder }}
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
+
+ - task: PowerShell@2
+ displayName: Workaround for ESRP CLI on Linux
+ inputs:
+ targetType: 'inline'
+ script: |
+ Write-Host "Copying Linux Path"
+ $MBSIGN_APPFOLDER = '$(MBSIGN_APPFOLDER)'
+ $MBSIGN_APPFOLDER = $MBSIGN_APPFOLDER -replace '/build', ''
+ $MBSIGN_APPFOLDER = $MBSIGN_APPFOLDER + '/1.1.1032' + '/build'
+ $MBSIGN_APPFOLDER | Write-Host
+ $SignConfigPath = $MBSIGN_APPFOLDER + '/signconfig.xml'
+ Copy-Item -Path "$(MBSIGN_APPFOLDER)/signconfig.xml" -Destination $SignConfigPath -Force
+ condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
diff --git a/eng/common/cross/armel/armel.jessie.patch b/eng/common/cross/armel/armel.jessie.patch
deleted file mode 100644
index 2d261561935..00000000000
--- a/eng/common/cross/armel/armel.jessie.patch
+++ /dev/null
@@ -1,43 +0,0 @@
-diff -u -r a/usr/include/urcu/uatomic/generic.h b/usr/include/urcu/uatomic/generic.h
---- a/usr/include/urcu/uatomic/generic.h 2014-10-22 15:00:58.000000000 -0700
-+++ b/usr/include/urcu/uatomic/generic.h 2020-10-30 21:38:28.550000000 -0700
-@@ -69,10 +69,10 @@
- #endif
- #ifdef UATOMIC_HAS_ATOMIC_SHORT
- case 2:
-- return __sync_val_compare_and_swap_2(addr, old, _new);
-+ return __sync_val_compare_and_swap_2((uint16_t*) addr, old, _new);
- #endif
- case 4:
-- return __sync_val_compare_and_swap_4(addr, old, _new);
-+ return __sync_val_compare_and_swap_4((uint32_t*) addr, old, _new);
- #if (CAA_BITS_PER_LONG == 64)
- case 8:
- return __sync_val_compare_and_swap_8(addr, old, _new);
-@@ -109,7 +109,7 @@
- return;
- #endif
- case 4:
-- __sync_and_and_fetch_4(addr, val);
-+ __sync_and_and_fetch_4((uint32_t*) addr, val);
- return;
- #if (CAA_BITS_PER_LONG == 64)
- case 8:
-@@ -148,7 +148,7 @@
- return;
- #endif
- case 4:
-- __sync_or_and_fetch_4(addr, val);
-+ __sync_or_and_fetch_4((uint32_t*) addr, val);
- return;
- #if (CAA_BITS_PER_LONG == 64)
- case 8:
-@@ -187,7 +187,7 @@
- return __sync_add_and_fetch_2(addr, val);
- #endif
- case 4:
-- return __sync_add_and_fetch_4(addr, val);
-+ return __sync_add_and_fetch_4((uint32_t*) addr, val);
- #if (CAA_BITS_PER_LONG == 64)
- case 8:
- return __sync_add_and_fetch_8(addr, val);
diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh
index 74f399716ba..d6f005b5dab 100755
--- a/eng/common/cross/build-rootfs.sh
+++ b/eng/common/cross/build-rootfs.sh
@@ -164,9 +164,13 @@ while :; do
armel)
__BuildArch=armel
__UbuntuArch=armel
- __UbuntuRepo="http://ftp.debian.org/debian/"
- __CodeName=jessie
+ __UbuntuRepo="http://archive.debian.org/debian/"
+ __CodeName=buster
__KeyringFile="/usr/share/keyrings/debian-archive-keyring.gpg"
+ __LLDB_Package="liblldb-6.0-dev"
+ __UbuntuPackages="${__UbuntuPackages// libomp-dev/}"
+ __UbuntuPackages="${__UbuntuPackages// libomp5/}"
+ __UbuntuSuites=
;;
armv6)
__BuildArch=armv6
@@ -278,46 +282,23 @@ while :; do
;;
xenial) # Ubuntu 16.04
- if [[ "$__CodeName" != "jessie" ]]; then
- __CodeName=xenial
- fi
- ;;
- zesty) # Ubuntu 17.04
- if [[ "$__CodeName" != "jessie" ]]; then
- __CodeName=zesty
- fi
+ __CodeName=xenial
;;
bionic) # Ubuntu 18.04
- if [[ "$__CodeName" != "jessie" ]]; then
- __CodeName=bionic
- fi
+ __CodeName=bionic
;;
focal) # Ubuntu 20.04
- if [[ "$__CodeName" != "jessie" ]]; then
- __CodeName=focal
- fi
+ __CodeName=focal
;;
jammy) # Ubuntu 22.04
- if [[ "$__CodeName" != "jessie" ]]; then
- __CodeName=jammy
- fi
+ __CodeName=jammy
;;
noble) # Ubuntu 24.04
- if [[ "$__CodeName" != "jessie" ]]; then
- __CodeName=noble
- fi
+ __CodeName=noble
if [[ -n "$__LLDB_Package" ]]; then
__LLDB_Package="liblldb-18-dev"
fi
;;
- jessie) # Debian 8
- __CodeName=jessie
- __KeyringFile="/usr/share/keyrings/debian-archive-keyring.gpg"
-
- if [[ -z "$__UbuntuRepo" ]]; then
- __UbuntuRepo="http://ftp.debian.org/debian/"
- fi
- ;;
stretch) # Debian 9
__CodeName=stretch
__LLDB_Package="liblldb-6.0-dev"
@@ -333,7 +314,7 @@ while :; do
__KeyringFile="/usr/share/keyrings/debian-archive-keyring.gpg"
if [[ -z "$__UbuntuRepo" ]]; then
- __UbuntuRepo="http://ftp.debian.org/debian/"
+ __UbuntuRepo="http://archive.debian.org/debian/"
fi
;;
bullseye) # Debian 11
@@ -473,10 +454,6 @@ if [[ "$__AlpineVersion" =~ 3\.1[345] ]]; then
__AlpinePackages="${__AlpinePackages/compiler-rt/compiler-rt-static}"
fi
-if [[ "$__BuildArch" == "armel" ]]; then
- __LLDB_Package="lldb-3.5-dev"
-fi
-
__UbuntuPackages+=" ${__LLDB_Package:-}"
if [[ -z "$__UbuntuRepo" ]]; then
@@ -850,12 +827,6 @@ EOF
if [[ "$__SkipUnmount" == "0" ]]; then
umount "$__RootfsDir"/* || true
fi
-
- if [[ "$__BuildArch" == "armel" && "$__CodeName" == "jessie" ]]; then
- pushd "$__RootfsDir"
- patch -p1 < "$__CrossDir/$__BuildArch/armel.jessie.patch"
- popd
- fi
elif [[ "$__Tizen" == "tizen" ]]; then
ROOTFS_DIR="$__RootfsDir" "$__CrossDir/tizen-build-rootfs.sh" "$__BuildArch"
else
diff --git a/eng/common/native/install-dependencies.sh b/eng/common/native/install-dependencies.sh
index ce661e9e5cd..477a44f335b 100644
--- a/eng/common/native/install-dependencies.sh
+++ b/eng/common/native/install-dependencies.sh
@@ -27,8 +27,9 @@ case "$os" in
libssl-dev libkrb5-dev pigz cpio
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
- elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ]; then
- dnf install -y cmake llvm lld lldb clang python curl libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio
+ elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ] || [ "$ID" = "azurelinux" ]; then
+ pkg_mgr="$(command -v tdnf 2>/dev/null || command -v dnf)"
+ $pkg_mgr install -y cmake llvm lld lldb clang python curl libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio
elif [ "$ID" = "alpine" ]; then
apk add build-base cmake bash curl clang llvm-dev lld lldb krb5-dev lttng-ust-dev icu-dev openssl-dev pigz cpio
else
@@ -44,7 +45,7 @@ case "$os" in
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
# Skip brew update for now, see https://github.com/actions/setup-python/issues/577
# brew update --preinstall
- brew bundle --no-upgrade --no-lock --file=- <true
true$(NoWarn);NU5128
+ $(NoWarn);RS1038
diff --git a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs
index 291926112ad..2f6579ef1ef 100644
--- a/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs
+++ b/src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs
@@ -23,8 +23,7 @@ private static readonly DiagnosticDescriptor Descriptor
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
- public override ImmutableArray SupportedDiagnostics
- => ImmutableArray.Create(Descriptor);
+ public override ImmutableArray SupportedDiagnostics => [Descriptor];
public override void Initialize(AnalysisContext context)
{
diff --git a/src/EFCore.Analyzers/InterpolatedStringUsageInRawQueriesCodeFixProvider.cs b/src/EFCore.Analyzers/InterpolatedStringUsageInRawQueriesCodeFixProvider.cs
index 4ad0cbd0c20..adcd46f5371 100644
--- a/src/EFCore.Analyzers/InterpolatedStringUsageInRawQueriesCodeFixProvider.cs
+++ b/src/EFCore.Analyzers/InterpolatedStringUsageInRawQueriesCodeFixProvider.cs
@@ -15,8 +15,7 @@ namespace Microsoft.EntityFrameworkCore;
[Shared]
public sealed class InterpolatedStringUsageInRawQueriesCodeFixProvider : CodeFixProvider
{
- public override ImmutableArray FixableDiagnosticIds
- => ImmutableArray.Create(EFDiagnostics.InterpolatedStringUsageInRawQueries);
+ public override ImmutableArray FixableDiagnosticIds => [EFDiagnostics.InterpolatedStringUsageInRawQueries];
public override FixAllProvider GetFixAllProvider()
=> WellKnownFixAllProviders.BatchFixer;
diff --git a/src/EFCore.Analyzers/InterpolatedStringUsageInRawQueriesDiagnosticAnalyzer.cs b/src/EFCore.Analyzers/InterpolatedStringUsageInRawQueriesDiagnosticAnalyzer.cs
index 4de8039d555..5d7f07b5523 100644
--- a/src/EFCore.Analyzers/InterpolatedStringUsageInRawQueriesDiagnosticAnalyzer.cs
+++ b/src/EFCore.Analyzers/InterpolatedStringUsageInRawQueriesDiagnosticAnalyzer.cs
@@ -21,8 +21,7 @@ private static readonly DiagnosticDescriptor Descriptor
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
- public override ImmutableArray SupportedDiagnostics
- => ImmutableArray.Create(Descriptor);
+ public override ImmutableArray SupportedDiagnostics => [Descriptor];
public override void Initialize(AnalysisContext context)
{
diff --git a/src/EFCore.Analyzers/UninitializedDbSetDiagnosticSuppressor.cs b/src/EFCore.Analyzers/UninitializedDbSetDiagnosticSuppressor.cs
index 09c78dfce36..234ee5e3b69 100644
--- a/src/EFCore.Analyzers/UninitializedDbSetDiagnosticSuppressor.cs
+++ b/src/EFCore.Analyzers/UninitializedDbSetDiagnosticSuppressor.cs
@@ -17,8 +17,7 @@ public sealed class UninitializedDbSetDiagnosticSuppressor : DiagnosticSuppresso
justification: AnalyzerStrings.UninitializedDbSetWarningSuppressionJustification);
///
- public override ImmutableArray SupportedSuppressions { get; }
- = ImmutableArray.Create(SuppressUninitializedDbSetRule);
+ public override ImmutableArray SupportedSuppressions { get; } = [SuppressUninitializedDbSetRule];
///
public override void ReportSuppressions(SuppressionAnalysisContext context)
@@ -28,7 +27,7 @@ public override void ReportSuppressions(SuppressionAnalysisContext context)
foreach (var diagnostic in context.ReportedDiagnostics)
{
- // We have an warning about an uninitialized non-nullable property.
+ // We have a warning about an uninitialized non-nullable property.
// CS8618 contains the location of the uninitialized property in AdditionalLocations; note that if the class has a constructor,
// the diagnostic main Location points to the constructor rather than to the uninitialized property.
diff --git a/src/EFCore.Cosmos/Metadata/Conventions/CosmosDiscriminatorConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/CosmosDiscriminatorConvention.cs
index fd42271c82b..2990718e5c0 100644
--- a/src/EFCore.Cosmos/Metadata/Conventions/CosmosDiscriminatorConvention.cs
+++ b/src/EFCore.Cosmos/Metadata/Conventions/CosmosDiscriminatorConvention.cs
@@ -97,14 +97,14 @@ private static void ProcessEntityType(IConventionEntityTypeBuilder entityTypeBui
///
public override void ProcessDiscriminatorPropertySet(
- IConventionEntityTypeBuilder entityTypeBuilder,
+ IConventionTypeBaseBuilder structuralTypeBuilder,
string? name,
IConventionContext context)
{
- var entityType = entityTypeBuilder.Metadata;
- if (entityType.IsDocumentRoot())
+ if (structuralTypeBuilder.Metadata is not IConventionEntityType entityType
+ || entityType.IsDocumentRoot())
{
- base.ProcessDiscriminatorPropertySet(entityTypeBuilder, name, context);
+ base.ProcessDiscriminatorPropertySet(structuralTypeBuilder, name, context);
}
}
diff --git a/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs
index 45d69cae227..c3748e1974c 100644
--- a/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs
+++ b/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs
@@ -174,16 +174,10 @@ private void ProcessEntityType(IConventionEntityType entityType, IConventionCont
}
// Don't chain, because each of these could return null if the property has been explicitly configured with some other value.
- computedIdPropertyBuilder = computedIdPropertyBuilder.ToJsonProperty(IdPropertyJsonName)
- ?? computedIdPropertyBuilder;
-
- computedIdPropertyBuilder = computedIdPropertyBuilder.IsRequired(true)
- ?? computedIdPropertyBuilder;
-
- computedIdPropertyBuilder = computedIdPropertyBuilder.HasValueGeneratorFactory(typeof(IdValueGeneratorFactory))
- ?? computedIdPropertyBuilder;
-
+ computedIdPropertyBuilder.ToJsonProperty(IdPropertyJsonName);
+ computedIdPropertyBuilder.HasValueGeneratorFactory(typeof(IdValueGeneratorFactory));
computedIdPropertyBuilder.AfterSave(PropertySaveBehavior.Throw);
+ computedIdPropertyBuilder.IsRequired(true);
}
///
@@ -327,8 +321,13 @@ public virtual void ProcessModelAnnotationChanged(
///
public virtual void ProcessDiscriminatorPropertySet(
- IConventionEntityTypeBuilder entityTypeBuilder,
+ IConventionTypeBaseBuilder structuralTypeBuilder,
string? name,
IConventionContext context)
- => ProcessEntityType(entityTypeBuilder.Metadata, context);
+ {
+ if (structuralTypeBuilder is IConventionEntityTypeBuilder entityTypeBuilder)
+ {
+ ProcessEntityType(entityTypeBuilder.Metadata, context);
+ }
+ }
}
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs
index d349d73e609..6e434aee428 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs
@@ -691,7 +691,11 @@ private Expression CreateGetValueExpression(
&& !property.IsShadowProperty())
{
var readExpression = CreateGetValueExpression(
- jTokenExpression, storeName, type.MakeNullable(), property.GetTypeMapping());
+ jTokenExpression,
+ storeName,
+ type.MakeNullable(),
+ property.GetTypeMapping(),
+ isNonNullableScalar: false);
var nonNullReadExpression = readExpression;
if (nonNullReadExpression.Type != type)
@@ -712,7 +716,14 @@ private Expression CreateGetValueExpression(
}
return Convert(
- CreateGetValueExpression(jTokenExpression, storeName, type.MakeNullable(), property.GetTypeMapping()),
+ CreateGetValueExpression(
+ jTokenExpression,
+ storeName,
+ type.MakeNullable(),
+ property.GetTypeMapping(),
+ // special case keys - we check them for null to see if the entity needs to be materialized, so we want to keep the null, rather than non-nullable default
+ // returning defaults is supposed to help with evolving the schema - so this doesn't concern keys anyway (they shouldn't evolve)
+ isNonNullableScalar: !property.IsNullable && !property.IsKey()),
type);
}
@@ -720,7 +731,8 @@ private Expression CreateGetValueExpression(
Expression jTokenExpression,
string storeName,
Type type,
- CoreTypeMapping typeMapping = null)
+ CoreTypeMapping typeMapping = null,
+ bool isNonNullableScalar = false)
{
Check.DebugAssert(type.IsNullableType(), "Must read nullable type from JObject.");
@@ -763,6 +775,7 @@ var body
Constant(CosmosClientWrapper.Serializer)),
converter.ConvertFromProviderExpression.Body);
+ var originalBodyType = body.Type;
if (body.Type != type)
{
body = Convert(body, type);
@@ -783,7 +796,11 @@ var body
}
else
{
- replaceExpression = Default(type);
+ replaceExpression = isNonNullableScalar
+ ? Expression.Convert(
+ Default(originalBodyType),
+ type)
+ : Default(type);
}
body = Condition(
@@ -799,7 +816,11 @@ var body
}
else
{
- valueExpression = ConvertJTokenToType(jTokenExpression, typeMapping?.ClrType.MakeNullable() ?? type);
+ valueExpression = ConvertJTokenToType(
+ jTokenExpression,
+ (isNonNullableScalar
+ ? typeMapping?.ClrType
+ : typeMapping?.ClrType.MakeNullable()) ?? type);
if (valueExpression.Type != type)
{
diff --git a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosStringMethodTranslator.cs b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosStringMethodTranslator.cs
index 845409fdc99..a9d0b80b5f8 100644
--- a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosStringMethodTranslator.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosStringMethodTranslator.cs
@@ -13,31 +13,52 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
public class CosmosStringMethodTranslator(ISqlExpressionFactory sqlExpressionFactory) : IMethodCallTranslator
{
- private static readonly MethodInfo IndexOfMethodInfo
+ private static readonly MethodInfo IndexOfMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.IndexOf), [typeof(string)])!;
- private static readonly MethodInfo IndexOfMethodInfoWithStartingPosition
+ private static readonly MethodInfo IndexOfMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.IndexOf), [typeof(char)])!;
+
+ private static readonly MethodInfo IndexOfMethodInfoWithStartingPositionString
= typeof(string).GetRuntimeMethod(nameof(string.IndexOf), [typeof(string), typeof(int)])!;
- private static readonly MethodInfo ReplaceMethodInfo
+ private static readonly MethodInfo IndexOfMethodInfoWithStartingPositionChar
+ = typeof(string).GetRuntimeMethod(nameof(string.IndexOf), [typeof(char), typeof(int)])!;
+
+ private static readonly MethodInfo ReplaceMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.Replace), [typeof(string), typeof(string)])!;
- private static readonly MethodInfo ContainsMethodInfo
+ private static readonly MethodInfo ReplaceMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.Replace), [typeof(char), typeof(char)])!;
+
+ private static readonly MethodInfo ContainsMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)])!;
- private static readonly MethodInfo ContainsWithStringComparisonMethodInfo
+ private static readonly MethodInfo ContainsMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(char)])!;
+
+ private static readonly MethodInfo ContainsWithStringComparisonMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string), typeof(StringComparison)])!;
- private static readonly MethodInfo StartsWithMethodInfo
+ private static readonly MethodInfo ContainsWithStringComparisonMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(char), typeof(StringComparison)])!;
+
+ private static readonly MethodInfo StartsWithMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)])!;
- private static readonly MethodInfo StartsWithWithStringComparisonMethodInfo
+ private static readonly MethodInfo StartsWithMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(char)])!;
+
+ private static readonly MethodInfo StartsWithWithStringComparisonMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string), typeof(StringComparison)])!;
- private static readonly MethodInfo EndsWithMethodInfo
+ private static readonly MethodInfo EndsWithMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)])!;
- private static readonly MethodInfo EndsWithWithStringComparisonMethodInfo
+ private static readonly MethodInfo EndsWithMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(char)])!;
+
+ private static readonly MethodInfo EndsWithWithStringComparisonMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string), typeof(StringComparison)])!;
private static readonly MethodInfo ToLowerMethodInfo
@@ -109,27 +130,27 @@ private static readonly MethodInfo StringComparisonWithComparisonTypeArgumentSta
{
if (instance != null)
{
- if (IndexOfMethodInfo.Equals(method))
+ if (IndexOfMethodInfoString.Equals(method) || IndexOfMethodInfoChar.Equals(method))
{
return TranslateSystemFunction("INDEX_OF", typeof(int), instance, arguments[0]);
}
- if (IndexOfMethodInfoWithStartingPosition.Equals(method))
+ if (IndexOfMethodInfoWithStartingPositionString.Equals(method) || IndexOfMethodInfoWithStartingPositionChar.Equals(method))
{
return TranslateSystemFunction("INDEX_OF", typeof(int), instance, arguments[0], arguments[1]);
}
- if (ReplaceMethodInfo.Equals(method))
+ if (ReplaceMethodInfoString.Equals(method) || ReplaceMethodInfoChar.Equals(method))
{
return TranslateSystemFunction("REPLACE", method.ReturnType, instance, arguments[0], arguments[1]);
}
- if (ContainsMethodInfo.Equals(method))
+ if (ContainsMethodInfoString.Equals(method) || ContainsMethodInfoChar.Equals(method))
{
return TranslateSystemFunction("CONTAINS", typeof(bool), instance, arguments[0]);
}
- if (ContainsWithStringComparisonMethodInfo.Equals(method))
+ if (ContainsWithStringComparisonMethodInfoString.Equals(method) || ContainsWithStringComparisonMethodInfoChar.Equals(method))
{
if (arguments[1] is SqlConstantExpression { Value: StringComparison comparisonType })
{
@@ -150,12 +171,12 @@ private static readonly MethodInfo StringComparisonWithComparisonTypeArgumentSta
return null;
}
- if (StartsWithMethodInfo.Equals(method))
+ if (StartsWithMethodInfoString.Equals(method) || StartsWithMethodInfoChar.Equals(method))
{
return TranslateSystemFunction("STARTSWITH", typeof(bool), instance, arguments[0]);
}
- if (StartsWithWithStringComparisonMethodInfo.Equals(method))
+ if (StartsWithWithStringComparisonMethodInfoString.Equals(method))
{
if (arguments[1] is SqlConstantExpression { Value: StringComparison comparisonType })
{
@@ -176,12 +197,12 @@ private static readonly MethodInfo StringComparisonWithComparisonTypeArgumentSta
return null;
}
- if (EndsWithMethodInfo.Equals(method))
+ if (EndsWithMethodInfoString.Equals(method) || EndsWithMethodInfoChar.Equals(method))
{
return TranslateSystemFunction("ENDSWITH", typeof(bool), instance, arguments[0]);
}
- if (EndsWithWithStringComparisonMethodInfo.Equals(method))
+ if (EndsWithWithStringComparisonMethodInfoString.Equals(method))
{
if (arguments[1] is SqlConstantExpression { Value: StringComparison comparisonType })
{
diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs
index 9f2793deeaa..910a9a293de 100644
--- a/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs
@@ -8,7 +8,6 @@
using System.Text;
using Microsoft.EntityFrameworkCore.Cosmos.Diagnostics.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Infrastructure.Internal;
-using Microsoft.EntityFrameworkCore.Cosmos.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Internal;
using Newtonsoft.Json;
diff --git a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
index d5efd7f6a81..6eaa03404eb 100644
--- a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
+++ b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
@@ -628,9 +628,60 @@ protected virtual void GenerateComplexPropertyAnnotations(
IComplexProperty property,
IndentedStringBuilder stringBuilder)
{
+ var discriminatorProperty = property.ComplexType.FindDiscriminatorProperty();
+ if (discriminatorProperty != null)
+ {
+ stringBuilder
+ .AppendLine()
+ .Append(propertyBuilderName)
+ .Append('.')
+ .Append("HasDiscriminator");
+
+ if (discriminatorProperty.DeclaringType == property.ComplexType
+ && discriminatorProperty.Name != "Discriminator")
+ {
+ var propertyClrType = FindValueConverter(discriminatorProperty)?.ProviderClrType
+ .MakeNullable(discriminatorProperty.IsNullable)
+ ?? discriminatorProperty.ClrType;
+ stringBuilder
+ .Append('<')
+ .Append(Code.Reference(propertyClrType))
+ .Append(">(")
+ .Append(Code.Literal(discriminatorProperty.Name))
+ .Append(')');
+ }
+ else
+ {
+ stringBuilder
+ .Append("()");
+ }
+
+ var discriminatorValue = property.ComplexType.GetDiscriminatorValue();
+ if (discriminatorValue != null)
+ {
+ if (discriminatorProperty != null)
+ {
+ var valueConverter = FindValueConverter(discriminatorProperty);
+ if (valueConverter != null)
+ {
+ discriminatorValue = valueConverter.ConvertToProvider(discriminatorValue);
+ }
+ }
+
+ stringBuilder
+ .Append('.')
+ .Append("HasValue")
+ .Append('(')
+ .Append(Code.UnknownLiteral(discriminatorValue))
+ .Append(')');
+ }
+
+ stringBuilder.AppendLine(";");
+ }
+
var propertyAnnotations = Dependencies.AnnotationCodeGenerator
- .FilterIgnoredAnnotations(property.GetAnnotations())
- .ToDictionary(a => a.Name, a => a);
+ .FilterIgnoredAnnotations(property.GetAnnotations())
+ .ToDictionary(a => a.Name, a => a);
var typeAnnotations = Dependencies.AnnotationCodeGenerator
.FilterIgnoredAnnotations(property.ComplexType.GetAnnotations())
diff --git a/src/EFCore.Design/Properties/DesignStrings.Designer.cs b/src/EFCore.Design/Properties/DesignStrings.Designer.cs
index f213b989dd2..e245a42bcc8 100644
--- a/src/EFCore.Design/Properties/DesignStrings.Designer.cs
+++ b/src/EFCore.Design/Properties/DesignStrings.Designer.cs
@@ -129,14 +129,6 @@ public static string CompiledModelCustomCacheKeyFactory(object? factoryType)
GetString("CompiledModelCustomCacheKeyFactory", nameof(factoryType)),
factoryType);
- ///
- /// The entity type '{entityType}' has a defining query configured. Compiled model can't be generated, because defining queries are not supported.
- ///
- public static string CompiledModelDefiningQuery(object? entityType)
- => string.Format(
- GetString("CompiledModelDefiningQuery", nameof(entityType)),
- entityType);
-
///
/// Successfully generated a compiled model, it will be discovered automatically, but you can also call '{optionsCall}'. Run this command again when the model is modified.
///
diff --git a/src/EFCore.Design/Properties/DesignStrings.resx b/src/EFCore.Design/Properties/DesignStrings.resx
index 1303c7e4816..d0506266612 100644
--- a/src/EFCore.Design/Properties/DesignStrings.resx
+++ b/src/EFCore.Design/Properties/DesignStrings.resx
@@ -162,9 +162,6 @@
The context is configured to use a custom model cache key factory '{factoryType}', this usually indicates that the produced model can change between context instances. To preserve this behavior manually modify the generated compiled model source code.
-
- The entity type '{entityType}' has a defining query configured. Compiled model can't be generated, because defining queries are not supported.
-
Successfully generated a compiled model, it will be discovered automatically, but you can also call '{optionsCall}'. Run this command again when the model is modified.
diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
index 78b54d3c672..e18a43f317d 100644
--- a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
@@ -898,14 +898,6 @@ private void Create(IEntityType entityType, CSharpRuntimeAnnotationCodeGenerator
throw new InvalidOperationException(DesignStrings.CompiledModelQueryFilter(entityType.ShortName()));
}
-#pragma warning disable CS0618 // Type or member is obsolete
- if (entityType.GetDefiningQuery() != null)
- {
- // TODO: Move to InMemoryCSharpRuntimeAnnotationCodeGenerator, see #21624
- throw new InvalidOperationException(DesignStrings.CompiledModelDefiningQuery(entityType.ShortName()));
- }
-#pragma warning restore CS0618 // Type or member is obsolete
-
AddNamespace(entityType.ClrType, parameters.Namespaces);
var mainBuilder = parameters.MainBuilder;
@@ -925,14 +917,6 @@ private void Create(IEntityType entityType, CSharpRuntimeAnnotationCodeGenerator
.Append(_code.Literal(entityType.HasSharedClrType));
}
- var discriminatorProperty = entityType.GetDiscriminatorPropertyName();
- if (discriminatorProperty != null)
- {
- mainBuilder.AppendLine(",")
- .Append("discriminatorProperty: ")
- .Append(_code.Literal(discriminatorProperty));
- }
-
var changeTrackingStrategy = entityType.GetChangeTrackingStrategy();
if (changeTrackingStrategy != ChangeTrackingStrategy.Snapshot)
{
@@ -959,6 +943,14 @@ private void Create(IEntityType entityType, CSharpRuntimeAnnotationCodeGenerator
.Append(_code.Literal(true));
}
+ var discriminatorProperty = entityType.GetDiscriminatorPropertyName();
+ if (discriminatorProperty != null)
+ {
+ mainBuilder.AppendLine(",")
+ .Append("discriminatorProperty: ")
+ .Append(_code.Literal(discriminatorProperty));
+ }
+
var discriminatorValue = entityType.GetDiscriminatorValue();
if (discriminatorValue != null)
{
@@ -2182,6 +2174,24 @@ private void CreateComplexProperty(
.Append(_code.Literal(true));
}
+ var discriminatorPropertyName = complexType.GetDiscriminatorPropertyName();
+ if (discriminatorPropertyName != null)
+ {
+ mainBuilder.AppendLine(",")
+ .Append("discriminatorProperty: ")
+ .Append(_code.Literal(discriminatorPropertyName));
+ }
+
+ var discriminatorValue = complexType.GetDiscriminatorValue();
+ if (discriminatorValue != null)
+ {
+ AddNamespace(discriminatorValue.GetType(), parameters.Namespaces);
+
+ mainBuilder.AppendLine(",")
+ .Append("discriminatorValue: ")
+ .Append(_code.UnknownLiteral(discriminatorValue));
+ }
+
mainBuilder.AppendLine(",")
.Append("propertyCount: ")
.Append(_code.Literal(complexType.GetDeclaredProperties().Count()));
diff --git a/src/EFCore.InMemory/Design/Internal/InMemoryCSharpRuntimeAnnotationCodeGenerator.cs b/src/EFCore.InMemory/Design/Internal/InMemoryCSharpRuntimeAnnotationCodeGenerator.cs
index a9c7e56e256..0ea2c29c5ff 100644
--- a/src/EFCore.InMemory/Design/Internal/InMemoryCSharpRuntimeAnnotationCodeGenerator.cs
+++ b/src/EFCore.InMemory/Design/Internal/InMemoryCSharpRuntimeAnnotationCodeGenerator.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.EntityFrameworkCore.Design.Internal;
+using Microsoft.EntityFrameworkCore.InMemory.Internal;
namespace Microsoft.EntityFrameworkCore.InMemory.Design.Internal;
@@ -25,4 +26,20 @@ public InMemoryCSharpRuntimeAnnotationCodeGenerator(
: base(dependencies)
{
}
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override void Generate(IEntityType entityType, CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
+ {
+ if (entityType.GetInMemoryQuery() != null)
+ {
+ throw new InvalidOperationException(InMemoryStrings.CompiledModelDefiningQuery(entityType.DisplayName()));
+ }
+
+ base.Generate(entityType, parameters);
+ }
}
diff --git a/src/EFCore.InMemory/EFCore.InMemory.csproj b/src/EFCore.InMemory/EFCore.InMemory.csproj
index bf88275bc8f..17c05bf18d1 100644
--- a/src/EFCore.InMemory/EFCore.InMemory.csproj
+++ b/src/EFCore.InMemory/EFCore.InMemory.csproj
@@ -5,7 +5,7 @@
$(DefaultNetCoreTargetFramework)3.6Microsoft.EntityFrameworkCore.InMemory
- Microsoft.EntityFrameworkCore.InMemory
+ Microsoft.EntityFrameworkCoretrue$(PackageTags);In-Memorytrue
diff --git a/src/EFCore.InMemory/Extensions/InMemoryEntityTypeBuilderExtensions.cs b/src/EFCore.InMemory/Extensions/InMemoryEntityTypeBuilderExtensions.cs
index e0bc4d28569..9d7c8d9a93b 100644
--- a/src/EFCore.InMemory/Extensions/InMemoryEntityTypeBuilderExtensions.cs
+++ b/src/EFCore.InMemory/Extensions/InMemoryEntityTypeBuilderExtensions.cs
@@ -101,9 +101,5 @@ public static bool CanSetInMemoryQuery(
this IConventionEntityTypeBuilder entityTypeBuilder,
LambdaExpression? query,
bool fromDataAnnotation = false)
-#pragma warning disable EF1001 // Internal EF Core API usage.
-#pragma warning disable CS0612 // Type or member is obsolete
- => entityTypeBuilder.CanSetAnnotation(CoreAnnotationNames.DefiningQuery, query, fromDataAnnotation);
-#pragma warning restore CS0612 // Type or member is obsolete
-#pragma warning restore EF1001 // Internal EF Core API usage.
+ => entityTypeBuilder.CanSetAnnotation(InMemoryAnnotationNames.DefiningQuery, query, fromDataAnnotation);
}
diff --git a/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs b/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs
index 5045a664b85..0c42dc1ffa2 100644
--- a/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs
+++ b/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs
@@ -21,11 +21,7 @@ public static class InMemoryEntityTypeExtensions
/// The entity type to get the in-memory query for.
/// The LINQ query used as the default source.
public static LambdaExpression? GetInMemoryQuery(this IReadOnlyEntityType entityType)
-#pragma warning disable EF1001 // Internal EF Core API usage.
-#pragma warning disable CS0612 // Type or member is obsolete
- => (LambdaExpression?)entityType[CoreAnnotationNames.DefiningQuery];
-#pragma warning restore CS0612 // Type or member is obsolete
-#pragma warning restore EF1001 // Internal EF Core API usage.
+ => (LambdaExpression?)entityType[InMemoryAnnotationNames.DefiningQuery];
///
/// Sets the LINQ query used as the default source for queries of this type.
@@ -36,11 +32,7 @@ public static void SetInMemoryQuery(
this IMutableEntityType entityType,
LambdaExpression? inMemoryQuery)
=> entityType
-#pragma warning disable EF1001 // Internal EF Core API usage.
-#pragma warning disable CS0612 // Type or member is obsolete
- .SetOrRemoveAnnotation(CoreAnnotationNames.DefiningQuery, inMemoryQuery);
-#pragma warning restore CS0612 // Type or member is obsolete
-#pragma warning restore EF1001 // Internal EF Core API usage.
+ .SetOrRemoveAnnotation(InMemoryAnnotationNames.DefiningQuery, inMemoryQuery);
///
/// Sets the LINQ query used as the default source for queries of this type.
@@ -54,11 +46,7 @@ public static void SetInMemoryQuery(
LambdaExpression? inMemoryQuery,
bool fromDataAnnotation = false)
=> (LambdaExpression?)entityType
-#pragma warning disable EF1001 // Internal EF Core API usage.
-#pragma warning disable CS0612 // Type or member is obsolete
- .SetOrRemoveAnnotation(CoreAnnotationNames.DefiningQuery, inMemoryQuery, fromDataAnnotation)
-#pragma warning restore CS0612 // Type or member is obsolete
-#pragma warning restore EF1001 // Internal EF Core API usage.
+ .SetOrRemoveAnnotation(InMemoryAnnotationNames.DefiningQuery, inMemoryQuery, fromDataAnnotation)
?.Value;
///
@@ -66,10 +54,6 @@ public static void SetInMemoryQuery(
///
/// The entity type.
/// The configuration source for .
- public static ConfigurationSource? GetDefiningQueryConfigurationSource(this IConventionEntityType entityType)
-#pragma warning disable EF1001 // Internal EF Core API usage.
-#pragma warning disable CS0612 // Type or member is obsolete
- => entityType.FindAnnotation(CoreAnnotationNames.DefiningQuery)?.GetConfigurationSource();
-#pragma warning restore CS0612 // Type or member is obsolete
-#pragma warning restore EF1001 // Internal EF Core API usage.
+ public static ConfigurationSource? GetInMemoryQueryConfigurationSource(this IConventionEntityType entityType)
+ => entityType.FindAnnotation(InMemoryAnnotationNames.DefiningQuery)?.GetConfigurationSource();
}
diff --git a/src/EFCore.InMemory/Extensions/InMemoryServiceCollectionExtensions.cs b/src/EFCore.InMemory/Extensions/InMemoryServiceCollectionExtensions.cs
index 6b8d1c64e40..d3d50a93eb0 100644
--- a/src/EFCore.InMemory/Extensions/InMemoryServiceCollectionExtensions.cs
+++ b/src/EFCore.InMemory/Extensions/InMemoryServiceCollectionExtensions.cs
@@ -4,7 +4,6 @@
using System.ComponentModel;
using Microsoft.EntityFrameworkCore.InMemory.Diagnostics.Internal;
using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal;
-using Microsoft.EntityFrameworkCore.InMemory.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.InMemory.Query.Internal;
using Microsoft.EntityFrameworkCore.InMemory.Storage.Internal;
using Microsoft.EntityFrameworkCore.InMemory.ValueGeneration.Internal;
diff --git a/src/EFCore.InMemory/Metadata/Conventions/InMemoryConventionSetBuilder.cs b/src/EFCore.InMemory/Metadata/Conventions/InMemoryConventionSetBuilder.cs
index 0f13f38176f..7bfad928af8 100644
--- a/src/EFCore.InMemory/Metadata/Conventions/InMemoryConventionSetBuilder.cs
+++ b/src/EFCore.InMemory/Metadata/Conventions/InMemoryConventionSetBuilder.cs
@@ -1,7 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-namespace Microsoft.EntityFrameworkCore.InMemory.Metadata.Conventions;
+namespace Microsoft.EntityFrameworkCore.Metadata.Conventions;
///
/// A builder for building conventions for th in-memory provider.
diff --git a/src/EFCore.InMemory/Metadata/Internal/CosmosAnnotationNames.cs b/src/EFCore.InMemory/Metadata/Internal/CosmosAnnotationNames.cs
new file mode 100644
index 00000000000..ce1a599dfdc
--- /dev/null
+++ b/src/EFCore.InMemory/Metadata/Internal/CosmosAnnotationNames.cs
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+///
+/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+/// the same compatibility standards as public APIs. It may be changed or removed without notice in
+/// any release. You should only use it directly in your code with extreme caution and knowing that
+/// doing so can result in application failures when updating to a new Entity Framework Core release.
+///
+public static class InMemoryAnnotationNames
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public const string Prefix = "InMemory:";
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public const string DefiningQuery = Prefix + "DefiningQuery";
+}
diff --git a/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.cs b/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.cs
index 83f0de02464..4076751e735 100644
--- a/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.cs
+++ b/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.cs
@@ -21,7 +21,15 @@ namespace Microsoft.EntityFrameworkCore.InMemory.Internal
public static class InMemoryStrings
{
private static readonly ResourceManager _resourceManager
- = new ResourceManager("Microsoft.EntityFrameworkCore.InMemory.Properties.InMemoryStrings", typeof(InMemoryStrings).Assembly);
+ = new ResourceManager("Microsoft.EntityFrameworkCore.Properties.InMemoryStrings", typeof(InMemoryStrings).Assembly);
+
+ ///
+ /// The entity type '{entityType}' has a defining query configured. Compiled model can't be generated, because defining queries are not supported.
+ ///
+ public static string CompiledModelDefiningQuery(object? entityType)
+ => string.Format(
+ GetString("CompiledModelDefiningQuery", nameof(entityType)),
+ entityType);
///
/// Cannot apply 'DefaultIfEmpty' after a client-evaluated projection. Consider applying 'DefaultIfEmpty' before last 'Select' or use 'AsEnumerable' before 'DefaultIfEmpty' to apply it on client-side.
@@ -131,7 +139,7 @@ namespace Microsoft.EntityFrameworkCore.InMemory.Internal
public static class InMemoryResources
{
private static readonly ResourceManager _resourceManager
- = new ResourceManager("Microsoft.EntityFrameworkCore.InMemory.Properties.InMemoryStrings", typeof(InMemoryResources).Assembly);
+ = new ResourceManager("Microsoft.EntityFrameworkCore.Properties.InMemoryStrings", typeof(InMemoryResources).Assembly);
///
/// Saved {count} entities to in-memory store.
diff --git a/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.tt b/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.tt
index 8f9f997c866..349c4ed04e9 100644
--- a/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.tt
+++ b/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.tt
@@ -1,7 +1,7 @@
<#
Session = new System.Collections.Generic.Dictionary();
Session["ResourceFile"] = "InMemoryStrings.resx";
- Session["ResourceNamespace"] = "Microsoft.EntityFrameworkCore.InMemory.Properties";
+ Session["ResourceNamespace"] = "Microsoft.EntityFrameworkCore.Properties";
Session["LoggingDefinitionsClass"] = "Diagnostics.Internal.InMemoryLoggingDefinitions";
#>
<#@ include file="..\..\..\tools\Resources.tt" #>
\ No newline at end of file
diff --git a/src/EFCore.InMemory/Properties/InMemoryStrings.resx b/src/EFCore.InMemory/Properties/InMemoryStrings.resx
index 93e3f5cfd5d..ed8494a3500 100644
--- a/src/EFCore.InMemory/Properties/InMemoryStrings.resx
+++ b/src/EFCore.InMemory/Properties/InMemoryStrings.resx
@@ -117,6 +117,9 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ The entity type '{entityType}' has a defining query configured. Compiled model can't be generated, because defining queries are not supported.
+
Cannot apply 'DefaultIfEmpty' after a client-evaluated projection. Consider applying 'DefaultIfEmpty' before last 'Select' or use 'AsEnumerable' before 'DefaultIfEmpty' to apply it on client-side.
diff --git a/src/EFCore.Relational/Diagnostics/CommandEndEventData.cs b/src/EFCore.Relational/Diagnostics/CommandEndEventData.cs
index 52ab9d5c36c..4e61f915c4a 100644
--- a/src/EFCore.Relational/Diagnostics/CommandEndEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/CommandEndEventData.cs
@@ -19,6 +19,7 @@ public class CommandEndEventData : CommandEventData
/// A delegate that generates a log message for this event.
/// The being used.
/// The .
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The method.
/// A correlation ID that identifies the instance being used.
@@ -33,6 +34,7 @@ public CommandEndEventData(
Func messageGenerator,
DbConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -47,6 +49,7 @@ public CommandEndEventData(
messageGenerator,
connection,
command,
+ logCommandText,
context,
executeMethod,
commandId,
diff --git a/src/EFCore.Relational/Diagnostics/CommandErrorEventData.cs b/src/EFCore.Relational/Diagnostics/CommandErrorEventData.cs
index 9e9f6007b18..1f8cd114380 100644
--- a/src/EFCore.Relational/Diagnostics/CommandErrorEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/CommandErrorEventData.cs
@@ -18,6 +18,7 @@ public class CommandErrorEventData : CommandEndEventData, IErrorEventData
/// A delegate that generates a log message for this event.
/// The being used.
/// The that was executing when it failed.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The method that was used to execute the command.
/// A correlation ID that identifies the instance being used.
@@ -33,6 +34,7 @@ public CommandErrorEventData(
Func messageGenerator,
DbConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -48,6 +50,7 @@ public CommandErrorEventData(
messageGenerator,
connection,
command,
+ logCommandText,
context,
executeMethod,
commandId,
diff --git a/src/EFCore.Relational/Diagnostics/CommandEventData.cs b/src/EFCore.Relational/Diagnostics/CommandEventData.cs
index 0562c1961c1..03e06c7d49a 100644
--- a/src/EFCore.Relational/Diagnostics/CommandEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/CommandEventData.cs
@@ -19,6 +19,7 @@ public class CommandEventData : CommandCorrelatedEventData
/// A delegate that generates a log message for this event.
/// The being used.
/// The .
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The method.
/// A correlation ID that identifies the instance being used.
@@ -32,6 +33,7 @@ public CommandEventData(
Func messageGenerator,
DbConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -53,6 +55,7 @@ public CommandEventData(
commandSource)
{
Command = command;
+ LogCommandText = logCommandText;
LogParameterValues = logParameterValues;
}
@@ -61,6 +64,11 @@ public CommandEventData(
///
public virtual DbCommand Command { get; }
+ ///
+ /// The command text that can be logged.
+ ///
+ public virtual string LogCommandText { get; }
+
///
/// Indicates whether or not the application allows logging of parameter values.
///
diff --git a/src/EFCore.Relational/Diagnostics/CommandExecutedEventData.cs b/src/EFCore.Relational/Diagnostics/CommandExecutedEventData.cs
index 78fb02b4e5c..e7cfb0c7c8b 100644
--- a/src/EFCore.Relational/Diagnostics/CommandExecutedEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/CommandExecutedEventData.cs
@@ -18,6 +18,7 @@ public class CommandExecutedEventData : CommandEndEventData
/// A delegate that generates a log message for this event.
/// The being used.
/// The that was executing when it failed.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The method that was used to execute the command.
/// A correlation ID that identifies the instance being used.
@@ -33,6 +34,7 @@ public CommandExecutedEventData(
Func messageGenerator,
DbConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -48,6 +50,7 @@ public CommandExecutedEventData(
messageGenerator,
connection,
command,
+ logCommandText,
context,
executeMethod,
commandId,
diff --git a/src/EFCore.Relational/Diagnostics/DbConnectionInterceptor.cs b/src/EFCore.Relational/Diagnostics/DbConnectionInterceptor.cs
index a320c514b1f..464b5e428e0 100644
--- a/src/EFCore.Relational/Diagnostics/DbConnectionInterceptor.cs
+++ b/src/EFCore.Relational/Diagnostics/DbConnectionInterceptor.cs
@@ -97,4 +97,16 @@ public virtual Task ConnectionFailedAsync(
ConnectionErrorEventData eventData,
CancellationToken cancellationToken = default)
=> Task.CompletedTask;
+
+ ///
+ public virtual void ConnectionCanceled(DbConnection connection, ConnectionEndEventData eventData)
+ {
+ }
+
+ ///
+ public virtual Task ConnectionCanceledAsync(
+ DbConnection connection,
+ ConnectionEndEventData eventData,
+ CancellationToken cancellationToken = default)
+ => Task.CompletedTask;
}
diff --git a/src/EFCore.Relational/Diagnostics/IDbConnectionInterceptor.cs b/src/EFCore.Relational/Diagnostics/IDbConnectionInterceptor.cs
index b8b95043837..d5d982ab73b 100644
--- a/src/EFCore.Relational/Diagnostics/IDbConnectionInterceptor.cs
+++ b/src/EFCore.Relational/Diagnostics/IDbConnectionInterceptor.cs
@@ -261,7 +261,7 @@ Task ConnectionDisposedAsync(DbConnection connection, ConnectionEndEventData eve
=> Task.CompletedTask;
///
- /// Called when closing of a connection has failed with an exception.
+ /// Called when a connection operation (e.g. open) has failed with an exception.
///
/// The connection.
/// Contextual information about the connection.
@@ -270,7 +270,7 @@ void ConnectionFailed(DbConnection connection, ConnectionErrorEventData eventDat
}
///
- /// Called when closing of a connection has failed with an exception.
+ /// Called when a connection operation (e.g. open) has failed with an exception.
///
/// The connection.
/// Contextual information about the connection.
@@ -279,4 +279,24 @@ void ConnectionFailed(DbConnection connection, ConnectionErrorEventData eventDat
/// If the is canceled.
Task ConnectionFailedAsync(DbConnection connection, ConnectionErrorEventData eventData, CancellationToken cancellationToken = default)
=> Task.CompletedTask;
+
+ ///
+ /// Called when the opening of a connection was canceled.
+ ///
+ /// The connection.
+ /// Contextual information about the connection.
+ void ConnectionCanceled(DbConnection connection, ConnectionEndEventData eventData)
+ {
+ }
+
+ ///
+ /// Called when the opening of a connection was canceled.
+ ///
+ /// The connection.
+ /// Contextual information about the connection.
+ /// A to observe while waiting for the task to complete.
+ /// A representing the asynchronous operation.
+ /// If the is canceled.
+ Task ConnectionCanceledAsync(DbConnection connection, ConnectionEndEventData eventData, CancellationToken cancellationToken = default)
+ => Task.CompletedTask;
}
diff --git a/src/EFCore.Relational/Diagnostics/IRelationalCommandDiagnosticsLogger.cs b/src/EFCore.Relational/Diagnostics/IRelationalCommandDiagnosticsLogger.cs
index 8e1381dcbf2..1737a698620 100644
--- a/src/EFCore.Relational/Diagnostics/IRelationalCommandDiagnosticsLogger.cs
+++ b/src/EFCore.Relational/Diagnostics/IRelationalCommandDiagnosticsLogger.cs
@@ -93,6 +93,7 @@ DbCommand CommandInitialized(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The correlation ID associated with the given .
/// The correlation ID associated with the being used.
@@ -102,6 +103,7 @@ DbCommand CommandInitialized(
InterceptionResult CommandReaderExecuting(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -113,6 +115,7 @@ InterceptionResult CommandReaderExecuting(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The correlation ID associated with the given .
/// The correlation ID associated with the being used.
@@ -122,6 +125,7 @@ InterceptionResult CommandReaderExecuting(
InterceptionResult
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The correlation ID associated with the given .
/// The correlation ID associated with the being used.
@@ -142,6 +147,7 @@ InterceptionResult CommandScalarExecuting(
InterceptionResult CommandNonQueryExecuting(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -153,6 +159,7 @@ InterceptionResult CommandNonQueryExecuting(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The correlation ID associated with the given .
/// The correlation ID associated with the being used.
@@ -164,6 +171,7 @@ InterceptionResult CommandNonQueryExecuting(
ValueTask> CommandReaderExecutingAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -176,6 +184,7 @@ ValueTask> CommandReaderExecutingAsync(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The correlation ID associated with the given .
/// The correlation ID associated with the being used.
@@ -187,6 +196,7 @@ ValueTask> CommandReaderExecutingAsync(
ValueTask> CommandScalarExecutingAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -199,6 +209,7 @@ ValueTask> CommandScalarExecutingAsync(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The correlation ID associated with the given .
/// The correlation ID associated with the being used.
@@ -210,6 +221,7 @@ ValueTask> CommandScalarExecutingAsync(
ValueTask> CommandNonQueryExecutingAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -222,6 +234,7 @@ ValueTask> CommandNonQueryExecutingAsync(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The correlation ID associated with the given .
/// The correlation ID associated with the being used.
@@ -233,6 +246,7 @@ ValueTask> CommandNonQueryExecutingAsync(
DbDataReader CommandReaderExecuted(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -246,6 +260,7 @@ DbDataReader CommandReaderExecuted(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The correlation ID associated with the given .
/// The correlation ID associated with the being used.
@@ -257,6 +272,7 @@ DbDataReader CommandReaderExecuted(
object? CommandScalarExecuted(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -270,6 +286,7 @@ DbDataReader CommandReaderExecuted(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The correlation ID associated with the given .
/// The correlation ID associated with the being used.
@@ -281,6 +298,7 @@ DbDataReader CommandReaderExecuted(
int CommandNonQueryExecuted(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -294,6 +312,7 @@ int CommandNonQueryExecuted(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The correlation ID associated with the given .
/// The correlation ID associated with the being used.
@@ -307,6 +326,7 @@ int CommandNonQueryExecuted(
ValueTask CommandReaderExecutedAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -321,6 +341,7 @@ ValueTask CommandReaderExecutedAsync(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The correlation ID associated with the given .
/// The correlation ID associated with the being used.
@@ -334,6 +355,7 @@ ValueTask CommandReaderExecutedAsync(
ValueTask CommandScalarExecutedAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -348,6 +370,7 @@ ValueTask CommandReaderExecutedAsync(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// The correlation ID associated with the given .
/// The correlation ID associated with the being used.
@@ -361,6 +384,7 @@ ValueTask CommandReaderExecutedAsync(
ValueTask CommandNonQueryExecutedAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -375,6 +399,7 @@ ValueTask CommandNonQueryExecutedAsync(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// Represents the method that will be called to execute the command.
/// The correlation ID associated with the given .
@@ -386,6 +411,7 @@ ValueTask CommandNonQueryExecutedAsync(
void CommandError(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -400,6 +426,7 @@ void CommandError(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// Represents the method that will be called to execute the command.
/// The correlation ID associated with the given .
@@ -414,6 +441,7 @@ void CommandError(
Task CommandErrorAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -429,16 +457,18 @@ Task CommandErrorAsync(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// Represents the method that will be called to execute the command.
/// The correlation ID associated with the given .
/// The correlation ID associated with the being used.
/// The time that execution began.
- /// The amount of time that passed until the exception was raised.
+ /// The amount of time that passed until the command was canceled.
/// Source of the command.
void CommandCanceled(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -452,12 +482,13 @@ void CommandCanceled(
///
/// The connection.
/// The database command object.
+ /// The command text that can be logged.
/// The currently being used, to null if not known.
/// Represents the method that will be called to execute the command.
/// The correlation ID associated with the given .
/// The correlation ID associated with the being used.
/// The time that execution began.
- /// The amount of time that passed until the exception was raised.
+ /// The amount of time that passed until the command was canceled.
/// Source of the command.
/// A to observe while waiting for the task to complete.
/// A representing the async operation.
@@ -465,6 +496,7 @@ void CommandCanceled(
Task CommandCanceledAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
diff --git a/src/EFCore.Relational/Diagnostics/IRelationalConnectionDiagnosticsLogger.cs b/src/EFCore.Relational/Diagnostics/IRelationalConnectionDiagnosticsLogger.cs
index a30d6370b17..18a4d111d3e 100644
--- a/src/EFCore.Relational/Diagnostics/IRelationalConnectionDiagnosticsLogger.cs
+++ b/src/EFCore.Relational/Diagnostics/IRelationalConnectionDiagnosticsLogger.cs
@@ -210,6 +210,32 @@ Task ConnectionErrorAsync(
bool logErrorAsDebug,
CancellationToken cancellationToken = default);
+ ///
+ /// Logs for the event.
+ ///
+ /// The connection.
+ /// The time that the operation was started.
+ /// The amount of time that passed until the command was canceled.
+ void ConnectionCanceled(
+ IRelationalConnection connection,
+ DateTimeOffset startTime,
+ TimeSpan duration);
+
+ ///
+ /// Logs for the event.
+ ///
+ /// The connection.
+ /// The time that the operation was started.
+ /// The amount of time that passed until the command was canceled.
+ /// A to observe while waiting for the task to complete.
+ /// A representing the async operation.
+ /// If the is canceled.
+ Task ConnectionCanceledAsync(
+ IRelationalConnection connection,
+ DateTimeOffset startTime,
+ TimeSpan duration,
+ CancellationToken cancellationToken = default);
+
///
/// Whether or need
/// to be logged.
diff --git a/src/EFCore.Relational/Diagnostics/Internal/RelationalCommandDiagnosticsLogger.cs b/src/EFCore.Relational/Diagnostics/Internal/RelationalCommandDiagnosticsLogger.cs
index 67c00efb825..b6648cfbe76 100644
--- a/src/EFCore.Relational/Diagnostics/Internal/RelationalCommandDiagnosticsLogger.cs
+++ b/src/EFCore.Relational/Diagnostics/Internal/RelationalCommandDiagnosticsLogger.cs
@@ -219,6 +219,7 @@ private CommandEndEventData BroadcastCommandCreated(
CommandCreated,
connection,
command,
+ command.CommandText,
context,
executeMethod,
commandId,
@@ -320,6 +321,7 @@ private CommandEndEventData BroadcastCommandInitialized(
CommandInitialized,
connection,
command,
+ command.CommandText,
context,
executeMethod,
commandId,
@@ -355,6 +357,7 @@ static string CommandInitialized(EventDefinitionBase definition, EventData paylo
public virtual InterceptionResult CommandReaderExecuting(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -371,11 +374,11 @@ public virtual InterceptionResult CommandReaderExecuting(
definition.Log(
this,
- command.Parameters.FormatParameters(ShouldLogParameterValues(command)),
+ command.Parameters.FormatParameters(ShouldLogSensitiveData()),
command.CommandType,
command.CommandTimeout,
Environment.NewLine,
- command.CommandText.TrimEnd());
+ logCommandText.TrimEnd());
}
if (NeedsEventData(
@@ -386,6 +389,7 @@ public virtual InterceptionResult CommandReaderExecuting(
var eventData = BroadcastCommandExecuting(
connection.DbConnection,
command,
+ logCommandText,
context,
DbCommandMethod.ExecuteReader,
commandId,
@@ -415,6 +419,7 @@ public virtual InterceptionResult CommandReaderExecuting(
public virtual InterceptionResult CommandScalarExecuting(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -431,11 +436,11 @@ public virtual InterceptionResult CommandScalarExecuting(
definition.Log(
this,
- command.Parameters.FormatParameters(ShouldLogParameterValues(command)),
+ command.Parameters.FormatParameters(ShouldLogSensitiveData()),
command.CommandType,
command.CommandTimeout,
Environment.NewLine,
- command.CommandText.TrimEnd());
+ logCommandText.TrimEnd());
}
if (NeedsEventData(
@@ -446,6 +451,7 @@ public virtual InterceptionResult CommandScalarExecuting(
var eventData = BroadcastCommandExecuting(
connection.DbConnection,
command,
+ logCommandText,
context,
DbCommandMethod.ExecuteScalar,
commandId,
@@ -475,6 +481,7 @@ public virtual InterceptionResult CommandScalarExecuting(
public virtual InterceptionResult CommandNonQueryExecuting(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -491,11 +498,11 @@ public virtual InterceptionResult CommandNonQueryExecuting(
definition.Log(
this,
- command.Parameters.FormatParameters(ShouldLogParameterValues(command)),
+ command.Parameters.FormatParameters(ShouldLogSensitiveData()),
command.CommandType,
command.CommandTimeout,
Environment.NewLine,
- command.CommandText.TrimEnd());
+ logCommandText.TrimEnd());
}
if (NeedsEventData(
@@ -506,6 +513,7 @@ public virtual InterceptionResult CommandNonQueryExecuting(
var eventData = BroadcastCommandExecuting(
connection.DbConnection,
command,
+ logCommandText,
context,
DbCommandMethod.ExecuteNonQuery,
commandId,
@@ -535,6 +543,7 @@ public virtual InterceptionResult CommandNonQueryExecuting(
public virtual ValueTask> CommandReaderExecutingAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -552,11 +561,11 @@ public virtual ValueTask> CommandReaderExecutin
definition.Log(
this,
- command.Parameters.FormatParameters(ShouldLogParameterValues(command)),
+ command.Parameters.FormatParameters(ShouldLogSensitiveData()),
command.CommandType,
command.CommandTimeout,
Environment.NewLine,
- command.CommandText.TrimEnd());
+ logCommandText.TrimEnd());
}
if (NeedsEventData(
@@ -567,6 +576,7 @@ public virtual ValueTask> CommandReaderExecutin
var eventData = BroadcastCommandExecuting(
connection.DbConnection,
command,
+ logCommandText,
context,
DbCommandMethod.ExecuteReader,
commandId,
@@ -596,6 +606,7 @@ public virtual ValueTask> CommandReaderExecutin
public virtual ValueTask> CommandScalarExecutingAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -613,11 +624,11 @@ public virtual ValueTask> CommandScalarExecutingAsync
definition.Log(
this,
- command.Parameters.FormatParameters(ShouldLogParameterValues(command)),
+ command.Parameters.FormatParameters(ShouldLogSensitiveData()),
command.CommandType,
command.CommandTimeout,
Environment.NewLine,
- command.CommandText.TrimEnd());
+ logCommandText.TrimEnd());
}
if (NeedsEventData(
@@ -628,6 +639,7 @@ public virtual ValueTask> CommandScalarExecutingAsync
var eventData = BroadcastCommandExecuting(
connection.DbConnection,
command,
+ logCommandText,
context,
DbCommandMethod.ExecuteScalar,
commandId,
@@ -657,6 +669,7 @@ public virtual ValueTask> CommandScalarExecutingAsync
public virtual ValueTask> CommandNonQueryExecutingAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -674,11 +687,11 @@ public virtual ValueTask> CommandNonQueryExecutingAsync(
definition.Log(
this,
- command.Parameters.FormatParameters(ShouldLogParameterValues(command)),
+ command.Parameters.FormatParameters(ShouldLogSensitiveData()),
command.CommandType,
command.CommandTimeout,
Environment.NewLine,
- command.CommandText.TrimEnd());
+ logCommandText.TrimEnd());
}
if (NeedsEventData(
@@ -689,6 +702,7 @@ public virtual ValueTask> CommandNonQueryExecutingAsync(
var eventData = BroadcastCommandExecuting(
connection.DbConnection,
command,
+ logCommandText,
context,
DbCommandMethod.ExecuteNonQuery,
commandId,
@@ -712,6 +726,7 @@ public virtual ValueTask> CommandNonQueryExecutingAsync(
private CommandEventData BroadcastCommandExecuting(
DbConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -728,12 +743,13 @@ private CommandEventData BroadcastCommandExecuting(
CommandExecuting,
connection,
command,
+ logCommandText,
context,
executeMethod,
commandId,
connectionId,
async,
- ShouldLogParameterValues(command),
+ ShouldLogSensitiveData(),
startTime,
commandSource);
@@ -750,7 +766,7 @@ static string CommandExecuting(EventDefinitionBase definition, EventData payload
p.Command.CommandType,
p.Command.CommandTimeout,
Environment.NewLine,
- p.Command.CommandText.TrimEnd());
+ p.LogCommandText.TrimEnd());
}
}
@@ -767,6 +783,7 @@ static string CommandExecuting(EventDefinitionBase definition, EventData payload
public virtual DbDataReader CommandReaderExecuted(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -784,11 +801,11 @@ public virtual DbDataReader CommandReaderExecuted(
definition.Log(
this,
string.Format(CultureInfo.InvariantCulture, "{0:N0}", duration.TotalMilliseconds),
- command.Parameters.FormatParameters(ShouldLogParameterValues(command)),
+ command.Parameters.FormatParameters(ShouldLogSensitiveData()),
command.CommandType,
command.CommandTimeout,
Environment.NewLine,
- command.CommandText.TrimEnd());
+ logCommandText.TrimEnd());
}
if (NeedsEventData(
@@ -799,6 +816,7 @@ public virtual DbDataReader CommandReaderExecuted(
var eventData = BroadcastCommandExecuted(
connection.DbConnection,
command,
+ logCommandText,
context,
DbCommandMethod.ExecuteReader,
commandId,
@@ -830,6 +848,7 @@ public virtual DbDataReader CommandReaderExecuted(
public virtual object? CommandScalarExecuted(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -847,11 +866,11 @@ public virtual DbDataReader CommandReaderExecuted(
definition.Log(
this,
string.Format(CultureInfo.InvariantCulture, "{0:N0}", duration.TotalMilliseconds),
- command.Parameters.FormatParameters(ShouldLogParameterValues(command)),
+ command.Parameters.FormatParameters(ShouldLogSensitiveData()),
command.CommandType,
command.CommandTimeout,
Environment.NewLine,
- command.CommandText.TrimEnd());
+ logCommandText.TrimEnd());
}
if (NeedsEventData(
@@ -862,6 +881,7 @@ public virtual DbDataReader CommandReaderExecuted(
var eventData = BroadcastCommandExecuted(
connection.DbConnection,
command,
+ logCommandText,
context,
DbCommandMethod.ExecuteScalar,
commandId,
@@ -893,6 +913,7 @@ public virtual DbDataReader CommandReaderExecuted(
public virtual int CommandNonQueryExecuted(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -910,11 +931,11 @@ public virtual int CommandNonQueryExecuted(
definition.Log(
this,
string.Format(CultureInfo.InvariantCulture, "{0:N0}", duration.TotalMilliseconds),
- command.Parameters.FormatParameters(ShouldLogParameterValues(command)),
+ command.Parameters.FormatParameters(ShouldLogSensitiveData()),
command.CommandType,
command.CommandTimeout,
Environment.NewLine,
- command.CommandText.TrimEnd());
+ logCommandText.TrimEnd());
}
if (NeedsEventData(
@@ -925,6 +946,7 @@ public virtual int CommandNonQueryExecuted(
var eventData = BroadcastCommandExecuted(
connection.DbConnection,
command,
+ logCommandText,
context,
DbCommandMethod.ExecuteNonQuery,
commandId,
@@ -956,6 +978,7 @@ public virtual int CommandNonQueryExecuted(
public virtual ValueTask CommandReaderExecutedAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -974,11 +997,11 @@ public virtual ValueTask CommandReaderExecutedAsync(
definition.Log(
this,
string.Format(CultureInfo.InvariantCulture, "{0:N0}", duration.TotalMilliseconds),
- command.Parameters.FormatParameters(ShouldLogParameterValues(command)),
+ command.Parameters.FormatParameters(ShouldLogSensitiveData()),
command.CommandType,
command.CommandTimeout,
Environment.NewLine,
- command.CommandText.TrimEnd());
+ logCommandText.TrimEnd());
}
if (NeedsEventData(
@@ -989,6 +1012,7 @@ public virtual ValueTask CommandReaderExecutedAsync(
var eventData = BroadcastCommandExecuted(
connection.DbConnection,
command,
+ logCommandText,
context,
DbCommandMethod.ExecuteReader,
commandId,
@@ -1020,6 +1044,7 @@ public virtual ValueTask CommandReaderExecutedAsync(
public virtual ValueTask CommandScalarExecutedAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -1038,11 +1063,11 @@ public virtual ValueTask CommandReaderExecutedAsync(
definition.Log(
this,
string.Format(CultureInfo.InvariantCulture, "{0:N0}", duration.TotalMilliseconds),
- command.Parameters.FormatParameters(ShouldLogParameterValues(command)),
+ command.Parameters.FormatParameters(ShouldLogSensitiveData()),
command.CommandType,
command.CommandTimeout,
Environment.NewLine,
- command.CommandText.TrimEnd());
+ logCommandText.TrimEnd());
}
if (NeedsEventData(
@@ -1053,6 +1078,7 @@ public virtual ValueTask CommandReaderExecutedAsync(
var eventData = BroadcastCommandExecuted(
connection.DbConnection,
command,
+ logCommandText,
context,
DbCommandMethod.ExecuteScalar,
commandId,
@@ -1084,6 +1110,7 @@ public virtual ValueTask CommandReaderExecutedAsync(
public virtual ValueTask CommandNonQueryExecutedAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
Guid commandId,
Guid connectionId,
@@ -1102,11 +1129,11 @@ public virtual ValueTask CommandNonQueryExecutedAsync(
definition.Log(
this,
string.Format(CultureInfo.InvariantCulture, "{0:N0}", duration.TotalMilliseconds),
- command.Parameters.FormatParameters(ShouldLogParameterValues(command)),
+ command.Parameters.FormatParameters(ShouldLogSensitiveData()),
command.CommandType,
command.CommandTimeout,
Environment.NewLine,
- command.CommandText.TrimEnd());
+ logCommandText.TrimEnd());
}
if (NeedsEventData(
@@ -1117,6 +1144,7 @@ public virtual ValueTask CommandNonQueryExecutedAsync(
var eventData = BroadcastCommandExecuted(
connection.DbConnection,
command,
+ logCommandText,
context,
DbCommandMethod.ExecuteNonQuery,
commandId,
@@ -1142,6 +1170,7 @@ public virtual ValueTask CommandNonQueryExecutedAsync(
private CommandExecutedEventData BroadcastCommandExecuted(
DbConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -1160,13 +1189,14 @@ private CommandExecutedEventData BroadcastCommandExecuted(
CommandExecuted,
connection,
command,
+ logCommandText,
context,
executeMethod,
commandId,
connectionId,
methodResult,
async,
- ShouldLogParameterValues(command),
+ ShouldLogSensitiveData(),
startTime,
duration,
commandSource);
@@ -1185,7 +1215,7 @@ static string CommandExecuted(EventDefinitionBase definition, EventData payload)
p.Command.CommandType,
p.Command.CommandTimeout,
Environment.NewLine,
- p.Command.CommandText.TrimEnd());
+ p.LogCommandText.TrimEnd());
}
}
@@ -1202,6 +1232,7 @@ static string CommandExecuted(EventDefinitionBase definition, EventData payload)
public virtual void CommandError(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -1213,7 +1244,7 @@ public virtual void CommandError(
{
var definition = RelationalResources.LogCommandFailed(this);
- LogCommandError(command, duration, definition);
+ LogCommandError(command, logCommandText, duration, definition);
if (NeedsEventData(
definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -1221,6 +1252,7 @@ public virtual void CommandError(
var eventData = BroadcastCommandError(
connection.DbConnection,
command,
+ logCommandText,
context,
executeMethod,
commandId,
@@ -1240,6 +1272,7 @@ public virtual void CommandError(
private void LogCommandError(
DbCommand command,
+ string logCommandText,
TimeSpan duration,
EventDefinition definition)
{
@@ -1248,11 +1281,11 @@ private void LogCommandError(
definition.Log(
this,
string.Format(CultureInfo.InvariantCulture, "{0:N0}", duration.TotalMilliseconds),
- command.Parameters.FormatParameters(ShouldLogParameterValues(command)),
+ command.Parameters.FormatParameters(ShouldLogSensitiveData()),
command.CommandType,
command.CommandTimeout,
Environment.NewLine,
- command.CommandText.TrimEnd());
+ logCommandText.TrimEnd());
}
}
@@ -1265,6 +1298,7 @@ private void LogCommandError(
public virtual Task CommandErrorAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -1277,7 +1311,7 @@ public virtual Task CommandErrorAsync(
{
var definition = RelationalResources.LogCommandFailed(this);
- LogCommandError(command, duration, definition);
+ LogCommandError(command, logCommandText, duration, definition);
if (NeedsEventData(
definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -1285,6 +1319,7 @@ public virtual Task CommandErrorAsync(
var eventData = BroadcastCommandError(
connection.DbConnection,
command,
+ logCommandText,
context,
executeMethod,
commandId,
@@ -1310,6 +1345,7 @@ public virtual Task CommandErrorAsync(
private CommandErrorEventData BroadcastCommandError(
DbConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -1328,13 +1364,14 @@ private CommandErrorEventData BroadcastCommandError(
CommandError,
connection,
command,
+ logCommandText,
context,
executeMethod,
commandId,
connectionId,
exception,
async,
- ShouldLogParameterValues(command),
+ ShouldLogSensitiveData(),
startTime,
duration,
commandSource);
@@ -1353,7 +1390,7 @@ static string CommandError(EventDefinitionBase definition, EventData payload)
p.Command.CommandType,
p.Command.CommandTimeout,
Environment.NewLine,
- p.Command.CommandText.TrimEnd());
+ p.LogCommandText.TrimEnd());
}
}
@@ -1365,6 +1402,7 @@ static string CommandError(EventDefinitionBase definition, EventData payload)
public virtual void CommandCanceled(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -1375,7 +1413,7 @@ public virtual void CommandCanceled(
{
var definition = RelationalResources.LogCommandCanceled(this);
- LogCommandCanceled(command, duration, definition);
+ LogCommandCanceled(command, logCommandText, duration, definition);
if (NeedsEventData(
definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -1383,6 +1421,7 @@ public virtual void CommandCanceled(
var eventData = BroadcastCommandCanceled(
connection.DbConnection,
command,
+ logCommandText,
context,
executeMethod,
commandId,
@@ -1403,6 +1442,7 @@ public virtual void CommandCanceled(
public virtual Task CommandCanceledAsync(
IRelationalConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -1414,7 +1454,7 @@ public virtual Task CommandCanceledAsync(
{
var definition = RelationalResources.LogCommandCanceled(this);
- LogCommandCanceled(command, duration, definition);
+ LogCommandCanceled(command, logCommandText, duration, definition);
if (NeedsEventData(
definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
@@ -1422,6 +1462,7 @@ public virtual Task CommandCanceledAsync(
var eventData = BroadcastCommandCanceled(
connection.DbConnection,
command,
+ logCommandText,
context,
executeMethod,
commandId,
@@ -1445,6 +1486,7 @@ public virtual Task CommandCanceledAsync(
private void LogCommandCanceled(
DbCommand command,
+ string logCommandText,
TimeSpan duration,
EventDefinition definition)
{
@@ -1453,17 +1495,18 @@ private void LogCommandCanceled(
definition.Log(
this,
string.Format(CultureInfo.InvariantCulture, "{0:N0}", duration.TotalMilliseconds),
- command.Parameters.FormatParameters(ShouldLogParameterValues(command)),
+ command.Parameters.FormatParameters(ShouldLogSensitiveData()),
command.CommandType,
command.CommandTimeout,
Environment.NewLine,
- command.CommandText.TrimEnd());
+ logCommandText.TrimEnd());
}
}
private CommandEndEventData BroadcastCommandCanceled(
DbConnection connection,
DbCommand command,
+ string logCommandText,
DbContext? context,
DbCommandMethod executeMethod,
Guid commandId,
@@ -1481,12 +1524,13 @@ private CommandEndEventData BroadcastCommandCanceled(
CommandCanceled,
connection,
command,
+ logCommandText,
context,
executeMethod,
commandId,
connectionId,
async,
- ShouldLogParameterValues(command),
+ ShouldLogSensitiveData(),
startTime,
duration,
commandSource);
@@ -1505,7 +1549,7 @@ static string CommandCanceled(EventDefinitionBase definition, EventData payload)
p.Command.CommandType,
p.Command.CommandTimeout,
Environment.NewLine,
- p.Command.CommandText.TrimEnd());
+ p.LogCommandText.TrimEnd());
}
}
@@ -1738,8 +1782,5 @@ public virtual bool ShouldLogDataReaderClose(DateTimeOffset now)
public virtual bool ShouldLogDataReaderDispose(DateTimeOffset now)
=> now > _suppressDataReaderDisposingExpiration;
- private bool ShouldLogParameterValues(DbCommand command)
- => command.Parameters.Count > 0 && ShouldLogSensitiveData();
-
#endregion ShouldLog checks
}
diff --git a/src/EFCore.Relational/Diagnostics/Internal/RelationalConnectionDiagnosticsLogger.cs b/src/EFCore.Relational/Diagnostics/Internal/RelationalConnectionDiagnosticsLogger.cs
index 5777768e8a0..0418bdc277f 100644
--- a/src/EFCore.Relational/Diagnostics/Internal/RelationalConnectionDiagnosticsLogger.cs
+++ b/src/EFCore.Relational/Diagnostics/Internal/RelationalConnectionDiagnosticsLogger.cs
@@ -892,6 +892,121 @@ private void LogConnectionError(
#endregion ConnectionError
+ #region ConnectionCanceled
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual void ConnectionCanceled(
+ IRelationalConnection connection,
+ DateTimeOffset startTime,
+ TimeSpan duration)
+ {
+ var definition = RelationalResources.LogConnectionCanceled(this);
+
+ LogConnectionCanceled(connection, definition);
+
+ if (NeedsEventData(
+ definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = BroadcastConnectionCanceled(
+ connection,
+ startTime,
+ duration,
+ async: false,
+ definition,
+ diagnosticSourceEnabled,
+ simpleLogEnabled);
+
+ interceptor?.ConnectionCanceled(connection.DbConnection, eventData);
+ }
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual Task ConnectionCanceledAsync(
+ IRelationalConnection connection,
+ DateTimeOffset startTime,
+ TimeSpan duration,
+ CancellationToken cancellationToken = default)
+ {
+ var definition = RelationalResources.LogConnectionCanceled(this);
+
+ LogConnectionCanceled(connection, definition);
+
+ if (NeedsEventData(
+ definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = BroadcastConnectionCanceled(
+ connection,
+ startTime,
+ duration,
+ async: true,
+ definition,
+ diagnosticSourceEnabled,
+ simpleLogEnabled);
+
+ interceptor?.ConnectionCanceled(connection.DbConnection, eventData);
+ }
+
+ return Task.CompletedTask;
+ }
+
+ private ConnectionEndEventData BroadcastConnectionCanceled(
+ IRelationalConnection connection,
+ DateTimeOffset startTime,
+ TimeSpan duration,
+ bool async,
+ EventDefinition definition,
+ bool diagnosticSourceEnabled,
+ bool simpleLogEnabled)
+ {
+ var eventData = new ConnectionEndEventData(
+ definition,
+ ConnectionCanceled,
+ connection.DbConnection,
+ connection.Context,
+ connection.ConnectionId,
+ async,
+ startTime,
+ duration);
+
+ DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
+
+ return eventData;
+
+ static string ConnectionCanceled(EventDefinitionBase definition, EventData payload)
+ {
+ var d = (EventDefinition)definition;
+ var p = (ConnectionEndEventData)payload;
+ return d.GenerateMessage(
+ p.Connection.Database,
+ p.Connection.DataSource);
+ }
+ }
+
+ private void LogConnectionCanceled(
+ IRelationalConnection connection,
+ EventDefinition definition)
+ {
+ if (ShouldLog(definition))
+ {
+ definition.Log(
+ this,
+ connection.DbConnection.Database,
+ connection.DbConnection.DataSource);
+ }
+ }
+
+ #endregion ConnectionCanceled
+
#region ConnectionCreating
///
diff --git a/src/EFCore.Relational/Diagnostics/RelationalEventId.cs b/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
index 0b1f48c5275..e4bb8529c3f 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
@@ -33,6 +33,7 @@ private enum Id
ConnectionCreated,
ConnectionDisposing,
ConnectionDisposed,
+ ConnectionCanceled,
// Command events
CommandExecuting = CoreEventId.RelationalBaseId + 100,
@@ -217,6 +218,19 @@ private static EventId MakeConnectionId(Id id)
///
public static readonly EventId ConnectionError = MakeConnectionId(Id.ConnectionError);
+ ///
+ /// A open operation has been canceled.
+ ///
+ ///
+ ///
+ /// This event is in the category.
+ ///
+ ///
+ /// This event uses the payload when used with a .
+ ///
+ ///
+ public static readonly EventId ConnectionCanceled = MakeConnectionId(Id.ConnectionCanceled);
+
///
/// A is about to be created by EF.
///
diff --git a/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs b/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
index 9634f027bf7..a4c49f8b04f 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
@@ -133,6 +133,15 @@ public abstract class RelationalLoggingDefinitions : LoggingDefinitions
[EntityFrameworkInternal]
public EventDefinitionBase? LogConnectionError;
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ public EventDefinitionBase? LogConnectionCanceled;
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
index 517205fb735..2e0a92fb071 100644
--- a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
@@ -39,9 +39,6 @@ public static class RelationalEntityTypeExtensions
return ((entityType as IConventionEntityType)?.GetViewNameConfigurationSource() == null)
&& (entityType as IConventionEntityType)?.GetFunctionNameConfigurationSource() == null
-#pragma warning disable CS0618 // Type or member is obsolete
- && (entityType as IConventionEntityType)?.GetDefiningQueryConfigurationSource() == null
-#pragma warning restore CS0618 // Type or member is obsolete
&& (entityType as IConventionEntityType)?.GetSqlQueryConfigurationSource() == null
? GetDefaultTableName(entityType)
: null;
@@ -267,9 +264,6 @@ public static void SetSchema(this IMutableEntityType entityType, string? value)
}
return ((entityType as IConventionEntityType)?.GetFunctionNameConfigurationSource() == null)
-#pragma warning disable CS0618 // Type or member is obsolete
- && ((entityType as IConventionEntityType)?.GetDefiningQueryConfigurationSource() == null)
-#pragma warning restore CS0618 // Type or member is obsolete
&& ((entityType as IConventionEntityType)?.GetSqlQueryConfigurationSource() == null)
? GetDefaultViewName(entityType)
: null;
diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
index a2bd60aaecc..84bf260d1ec 100644
--- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
+++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
@@ -1957,6 +1957,11 @@ protected override void ValidateInheritanceMapping(
var discriminatorValues = new Dictionary();
foreach (var derivedType in derivedTypes)
{
+ foreach (var complexProperty in derivedType.GetDeclaredComplexProperties())
+ {
+ ValidateDiscriminatorValues(complexProperty.ComplexType);
+ }
+
var discriminatorValue = derivedType.GetDiscriminatorValue();
if (!derivedType.ClrType.IsInstantiable()
|| discriminatorValue is null)
diff --git a/src/EFCore.Relational/Metadata/Conventions/DiscriminatorLengthConvention.cs b/src/EFCore.Relational/Metadata/Conventions/DiscriminatorLengthConvention.cs
index cb149ff89f4..5c9fee094a2 100644
--- a/src/EFCore.Relational/Metadata/Conventions/DiscriminatorLengthConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/DiscriminatorLengthConvention.cs
@@ -54,7 +54,7 @@ public virtual void ProcessModelFinalizing(IConventionModelBuilder modelBuilder,
&& !discriminatorProperty.IsForeignKey())
{
var maxDiscriminatorValueLength =
- entityType.GetDerivedTypesInclusive().Select(e => ((string)e.GetDiscriminatorValue()!).Length).Max();
+ entityType.GetDerivedTypesInclusive().Select(e => (e.GetDiscriminatorValue() as string)?.Length ?? 0).Max();
var previous = 1;
var current = 1;
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalQueryFilterRewritingConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalQueryFilterRewritingConvention.cs
index 8453e441377..1cb87baf3c2 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalQueryFilterRewritingConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalQueryFilterRewritingConvention.cs
@@ -39,14 +39,6 @@ public override void ProcessModelFinalizing(
{
entityType.SetQueryFilter((LambdaExpression)DbSetAccessRewriter.Rewrite(modelBuilder.Metadata, queryFilter));
}
-
-#pragma warning disable CS0618 // Type or member is obsolete
- var definingQuery = ((IEntityType)entityType).GetDefiningQuery();
- if (definingQuery != null)
- {
- entityType.SetDefiningQuery((LambdaExpression)DbSetAccessRewriter.Rewrite(modelBuilder.Metadata, definingQuery));
- }
-#pragma warning restore CS0618 // Type or member is obsolete
}
}
diff --git a/src/EFCore.Relational/Metadata/Internal/JsonColumn.cs b/src/EFCore.Relational/Metadata/Internal/JsonColumn.cs
index 3ad9900707c..10354167307 100644
--- a/src/EFCore.Relational/Metadata/Internal/JsonColumn.cs
+++ b/src/EFCore.Relational/Metadata/Internal/JsonColumn.cs
@@ -36,7 +36,7 @@ public JsonColumn(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected override RelationalTypeMapping GetDefaultStoreTypeMapping()
- => (RelationalTypeMapping)Table.Model.Model.GetModelDependencies().TypeMappingSource.FindMapping(typeof(JsonElement))!;
+ => (RelationalTypeMapping)Table.Model.Model.GetModelDependencies().TypeMappingSource.FindMapping(typeof(JsonTypePlaceholder))!;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Internal/JsonColumnBase.cs b/src/EFCore.Relational/Metadata/Internal/JsonColumnBase.cs
index 3209bf64e74..acc1bf09fe9 100644
--- a/src/EFCore.Relational/Metadata/Internal/JsonColumnBase.cs
+++ b/src/EFCore.Relational/Metadata/Internal/JsonColumnBase.cs
@@ -36,5 +36,5 @@ public JsonColumnBase(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected override RelationalTypeMapping GetDefaultStoreTypeMapping()
- => (RelationalTypeMapping)Table.Model.Model.GetModelDependencies().TypeMappingSource.FindMapping(typeof(JsonElement))!;
+ => (RelationalTypeMapping)Table.Model.Model.GetModelDependencies().TypeMappingSource.FindMapping(typeof(JsonTypePlaceholder))!;
}
diff --git a/src/EFCore.Relational/Metadata/Internal/JsonViewColumn.cs b/src/EFCore.Relational/Metadata/Internal/JsonViewColumn.cs
index 77c327332e3..4b57ad45b70 100644
--- a/src/EFCore.Relational/Metadata/Internal/JsonViewColumn.cs
+++ b/src/EFCore.Relational/Metadata/Internal/JsonViewColumn.cs
@@ -36,5 +36,5 @@ public JsonViewColumn(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected override RelationalTypeMapping GetDefaultStoreTypeMapping()
- => (RelationalTypeMapping)Table.Model.Model.GetModelDependencies().TypeMappingSource.FindMapping(typeof(JsonElement))!;
+ => (RelationalTypeMapping)Table.Model.Model.GetModelDependencies().TypeMappingSource.FindMapping(typeof(JsonTypePlaceholder))!;
}
diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
index 8e2294fe404..27709772f4c 100644
--- a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
+++ b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
@@ -582,7 +582,7 @@ private static void CreateContainerColumn(
{
Check.DebugAssert(tableBase.FindColumn(containerColumnName) == null, $"Table does not have column '{containerColumnName}'.");
- var jsonColumnTypeMapping = relationalTypeMappingSource.FindMapping(typeof(JsonElement), storeTypeName: containerColumnType)!;
+ var jsonColumnTypeMapping = relationalTypeMappingSource.FindMapping(typeof(JsonTypePlaceholder), storeTypeName: containerColumnType)!;
var jsonColumn = createColumn(containerColumnName, containerColumnType, tableBase, jsonColumnTypeMapping);
tableBase.Columns.Add(containerColumnName, jsonColumn);
jsonColumn.IsNullable = !ownership.IsRequiredDependent || !ownership.IsUnique;
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
index 5220ac5bddc..13c8cbcf2a3 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
+++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
@@ -2146,7 +2146,7 @@ public static string UnsupportedOperatorForSqlExpression(object? nodeType, objec
nodeType, expressionType);
///
- /// No relational type mapping can be found for property '{entity}.{property}' and the current provider doesn't specify a default store type for the properties of type '{clrType}'.
+ /// No relational type mapping can be found for property '{entity}.{property}' and the current provider doesn't specify a default store type for the properties of type '{clrType}'.
///
public static string UnsupportedPropertyType(object? entity, object? property, object? clrType)
=> string.Format(
@@ -2971,6 +2971,31 @@ public static EventDefinition LogConnectionErrorAsDebug(IDiagnos
return (EventDefinition)definition;
}
+ ///
+ /// A connection open was canceled to database '{database}' on server '{server}'.
+ ///
+ public static EventDefinition LogConnectionCanceled(IDiagnosticsLogger logger)
+ {
+ var definition = ((RelationalLoggingDefinitions)logger.Definitions).LogConnectionCanceled;
+ if (definition == null)
+ {
+ definition = NonCapturingLazyInitializer.EnsureInitialized(
+ ref ((RelationalLoggingDefinitions)logger.Definitions).LogConnectionCanceled,
+ logger,
+ static logger => new EventDefinition(
+ logger.Options,
+ RelationalEventId.ConnectionCanceled,
+ LogLevel.Debug,
+ "RelationalEventId.ConnectionCanceled",
+ level => LoggerMessage.Define(
+ level,
+ RelationalEventId.ConnectionCanceled,
+ _resourceManager.GetString("LogConnectionCanceled")!)));
+ }
+
+ return (EventDefinition)definition;
+ }
+
///
/// Created transaction savepoint.
///
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx
index 2641f26d37e..c5604c77a73 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.resx
+++ b/src/EFCore.Relational/Properties/RelationalStrings.resx
@@ -716,6 +716,10 @@
An error occurred using the connection to database '{database}' on server '{server}'.Debug RelationalEventId.ConnectionError string string
+
+ A connection open was canceled to database '{database}' on server '{server}'.
+ Debug RelationalEventId.ConnectionCanceled string string string
+
Created transaction savepoint.Debug RelationalEventId.CreatedTransactionSavepoint
diff --git a/src/EFCore.Relational/Query/ISqlExpressionFactory.cs b/src/EFCore.Relational/Query/ISqlExpressionFactory.cs
index 2712bec47ed..d60a38c5a02 100644
--- a/src/EFCore.Relational/Query/ISqlExpressionFactory.cs
+++ b/src/EFCore.Relational/Query/ISqlExpressionFactory.cs
@@ -431,6 +431,25 @@ SqlExpression NiladicFunction(
/// An expression representing a constant in a SQL tree.
SqlExpression Constant(object? value, Type type, RelationalTypeMapping? typeMapping = null);
+ ///
+ /// Creates a new which represents a constant in a SQL tree.
+ ///
+ /// A value.
+ /// if the expression contains sensitive values; otherwise, .
+ /// The associated with the expression.
+ /// An expression representing a constant in a SQL tree.
+ SqlExpression Constant(object value, bool sensitive, RelationalTypeMapping? typeMapping = null);
+
+ ///
+ /// Creates a new which represents a constant in a SQL tree.
+ ///
+ /// A value.
+ /// The type for the constant. Useful when value is null.
+ /// if the expression contains sensitive values; otherwise, .
+ /// The associated with the expression.
+ /// An expression representing a constant in a SQL tree.
+ SqlExpression Constant(object? value, Type type, bool sensitive, RelationalTypeMapping? typeMapping = null);
+
///
/// Creates a new which represents a SQL token.
///
diff --git a/src/EFCore.Relational/Query/QuerySqlGenerator.cs b/src/EFCore.Relational/Query/QuerySqlGenerator.cs
index b0a7ad8fb7c..8abac687511 100644
--- a/src/EFCore.Relational/Query/QuerySqlGenerator.cs
+++ b/src/EFCore.Relational/Query/QuerySqlGenerator.cs
@@ -628,7 +628,7 @@ protected override Expression VisitSqlBinary(SqlBinaryExpression sqlBinaryExpres
protected override Expression VisitSqlConstant(SqlConstantExpression sqlConstantExpression)
{
_relationalCommandBuilder
- .Append(sqlConstantExpression.TypeMapping!.GenerateSqlLiteral(sqlConstantExpression.Value));
+ .Append(sqlConstantExpression.TypeMapping!.GenerateSqlLiteral(sqlConstantExpression.Value), sqlConstantExpression.IsSensitive);
return sqlConstantExpression;
}
diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs
index ea11f3b5dd8..fa5f9d7323a 100644
--- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs
@@ -678,6 +678,7 @@ Expression GenerateRelationalCommandExpression(IReadOnlyDictionary)
])!,
Property(
@@ -685,6 +686,7 @@ Expression GenerateRelationalCommandExpression(IReadOnlyDictionary().Select(
diff --git a/src/EFCore.Relational/Query/SqlExpressionFactory.cs b/src/EFCore.Relational/Query/SqlExpressionFactory.cs
index 362bba301d1..ff0ec799f8a 100644
--- a/src/EFCore.Relational/Query/SqlExpressionFactory.cs
+++ b/src/EFCore.Relational/Query/SqlExpressionFactory.cs
@@ -976,4 +976,12 @@ public virtual SqlExpression Constant(object value, RelationalTypeMapping? typeM
///
public virtual SqlExpression Constant(object? value, Type type, RelationalTypeMapping? typeMapping = null)
=> new SqlConstantExpression(value, type, typeMapping);
+
+ ///
+ public virtual SqlExpression Constant(object value, bool sensitive, RelationalTypeMapping? typeMapping = null)
+ => new SqlConstantExpression(value, sensitive, typeMapping);
+
+ ///
+ public virtual SqlExpression Constant(object? value, Type type, bool sensitive, RelationalTypeMapping? typeMapping = null)
+ => new SqlConstantExpression(value, type, sensitive, typeMapping);
}
diff --git a/src/EFCore.Relational/Query/SqlExpressions/SqlConstantExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SqlConstantExpression.cs
index 84a4480caf0..dd7279f0927 100644
--- a/src/EFCore.Relational/Query/SqlExpressions/SqlConstantExpression.cs
+++ b/src/EFCore.Relational/Query/SqlExpressions/SqlConstantExpression.cs
@@ -25,8 +25,9 @@ public class SqlConstantExpression : SqlExpression
/// The of the expression.
/// The associated with the expression.
public SqlConstantExpression(object? value, Type type, RelationalTypeMapping? typeMapping)
- : base(type.UnwrapNullableType(), typeMapping)
- => Value = value;
+ : this(value, type, false, typeMapping)
+ {
+ }
///
/// Creates a new instance of the class.
@@ -34,7 +35,32 @@ public SqlConstantExpression(object? value, Type type, RelationalTypeMapping? ty
/// An to set the property equal to.
/// The associated with the expression.
public SqlConstantExpression(object value, RelationalTypeMapping? typeMapping)
- : this(value, value.GetType(), typeMapping)
+ : this(value, false, typeMapping)
+ {
+ }
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// An to set the property equal to.
+ /// The of the expression.
+ /// if the expression contains sensitive values; otherwise, .
+ /// The associated with the expression.
+ public SqlConstantExpression(object? value, Type type, bool sensitive, RelationalTypeMapping? typeMapping)
+ : base(type.UnwrapNullableType(), typeMapping)
+ {
+ Value = value;
+ IsSensitive = sensitive;
+ }
+
+ ///
+ /// Creates a new instance of the class.
+ ///
+ /// An to set the property equal to.
+ /// if the expression contains sensitive values; otherwise, .
+ /// The associated with the expression.
+ public SqlConstantExpression(object value, bool sensitive, RelationalTypeMapping? typeMapping)
+ : this(value, value.GetType(), sensitive, typeMapping)
{
}
@@ -45,7 +71,7 @@ public SqlConstantExpression(object value, RelationalTypeMapping? typeMapping)
/// The associated with the expression.
[Obsolete("Call the constructor accepting a value (and possibly a Type) instead")]
public SqlConstantExpression(ConstantExpression constantExpression, RelationalTypeMapping? typeMapping)
- : this(constantExpression.Value, constantExpression.Type, typeMapping)
+ : this(constantExpression.Value, constantExpression.Type, sensitive: false, typeMapping)
{
}
@@ -54,13 +80,18 @@ public SqlConstantExpression(ConstantExpression constantExpression, RelationalTy
///
public virtual object? Value { get; }
+ ///
+ /// Whether the expression contains sensitive values.
+ ///
+ public virtual bool IsSensitive { get; }
+
///
/// Applies supplied type mapping to this expression.
///
/// A relational type mapping to apply.
/// A new expression which has supplied type mapping.
public virtual SqlExpression ApplyTypeMapping(RelationalTypeMapping? typeMapping)
- => new SqlConstantExpression(Value, Type, typeMapping);
+ => new SqlConstantExpression(Value, Type, IsSensitive, typeMapping);
///
protected override Expression VisitChildren(ExpressionVisitor visitor)
@@ -70,12 +101,13 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
public override Expression Quote()
=> New(
_quotingConstructor ??= typeof(SqlConstantExpression)
- .GetConstructor([typeof(object), typeof(Type), typeof(RelationalTypeMapping)])!,
+ .GetConstructor([typeof(object), typeof(Type), typeof(bool), typeof(RelationalTypeMapping)])!,
Type.IsValueType
? Convert(
Constant(Value, Type), typeof(object))
: Constant(Value, Type),
Constant(Type),
+ Constant(IsSensitive),
RelationalExpressionQuotingUtilities.QuoteTypeMapping(TypeMapping));
///
diff --git a/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs b/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs
index ef0e6280cb8..a26ca0af260 100644
--- a/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs
+++ b/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs
@@ -129,7 +129,7 @@ protected override Expression VisitExtension(Expression node)
processedValues.Add(
new RowValueExpression(
[
- _sqlExpressionFactory.Constant(value, value?.GetType() ?? typeof(object), typeMapping)
+ _sqlExpressionFactory.Constant(value, value?.GetType() ?? typeof(object), sensitive: true, typeMapping)
]));
}
@@ -765,7 +765,7 @@ InExpression ProcessInExpressionValues(
continue;
}
- processedValues.Add(_sqlExpressionFactory.Constant(value, value?.GetType() ?? typeof(object), typeMapping));
+ processedValues.Add(_sqlExpressionFactory.Constant(value, value?.GetType() ?? typeof(object), sensitive: true, typeMapping));
}
}
else
@@ -1329,6 +1329,7 @@ protected virtual SqlExpression VisitSqlParameter(
return _sqlExpressionFactory.Constant(
parameterValue,
sqlParameterExpression.Type,
+ sensitive: true,
sqlParameterExpression.TypeMapping);
}
diff --git a/src/EFCore.Relational/Storage/IRelationalCommandBuilder.cs b/src/EFCore.Relational/Storage/IRelationalCommandBuilder.cs
index c147558f126..002fc9ea641 100644
--- a/src/EFCore.Relational/Storage/IRelationalCommandBuilder.cs
+++ b/src/EFCore.Relational/Storage/IRelationalCommandBuilder.cs
@@ -53,15 +53,17 @@ public interface IRelationalCommandBuilder
/// Appends an object to the command text.
///
/// The object to be written.
+ /// Whether the value should be redacted (i.e. in log).
/// The same builder instance so that multiple calls can be chained.
- IRelationalCommandBuilder Append(string value);
+ IRelationalCommandBuilder Append(string value, bool redact = false);
///
/// Appends an object to the command text.
///
/// The object to be written.
+ /// Whether the value should be redacted (i.e. in log).
/// The same builder instance so that multiple calls can be chained.
- IRelationalCommandBuilder Append(FormattableString value);
+ IRelationalCommandBuilder Append(FormattableString value, bool redact = false);
///
/// Appends a blank line to the command text.
diff --git a/src/EFCore.Relational/Storage/IRelationalCommandTemplate.cs b/src/EFCore.Relational/Storage/IRelationalCommandTemplate.cs
index 3f184eee826..22d83c04afd 100644
--- a/src/EFCore.Relational/Storage/IRelationalCommandTemplate.cs
+++ b/src/EFCore.Relational/Storage/IRelationalCommandTemplate.cs
@@ -23,6 +23,11 @@ public interface IRelationalCommandTemplate
///
string CommandText { get; }
+ ///
+ /// Gets the command text to be logged.
+ ///
+ string LogCommandText { get; }
+
///
/// Gets the parameters to be copied to the destination command.
///
diff --git a/src/EFCore.Relational/Storage/JsonTypeMapping.cs b/src/EFCore.Relational/Storage/JsonTypeMapping.cs
index 289bd85144c..9a14f340bae 100644
--- a/src/EFCore.Relational/Storage/JsonTypeMapping.cs
+++ b/src/EFCore.Relational/Storage/JsonTypeMapping.cs
@@ -2,13 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Data;
-using System.Text.Json;
namespace Microsoft.EntityFrameworkCore.Storage;
///
///
-/// Represents the mapping between a type and a database type.
+/// Represents the mapping between a JSON object and a database type.
///
///
/// This type is typically used by database providers (and other extensions). It is generally
diff --git a/src/EFCore.Relational/Storage/JsonTypePlaceholder.cs b/src/EFCore.Relational/Storage/JsonTypePlaceholder.cs
new file mode 100644
index 00000000000..027ee3465d7
--- /dev/null
+++ b/src/EFCore.Relational/Storage/JsonTypePlaceholder.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Storage;
+
+///
+/// A type representing CLR type of the JsonTypeMapping.
+///
+public sealed class JsonTypePlaceholder
+{
+ private JsonTypePlaceholder()
+ {
+ }
+}
diff --git a/src/EFCore.Relational/Storage/RelationalCommand.cs b/src/EFCore.Relational/Storage/RelationalCommand.cs
index 3d271f5cd73..20e81d269ed 100644
--- a/src/EFCore.Relational/Storage/RelationalCommand.cs
+++ b/src/EFCore.Relational/Storage/RelationalCommand.cs
@@ -33,14 +33,17 @@ public class RelationalCommand : IRelationalCommand
///
/// Service dependencies.
/// The text of the command to be executed.
+ /// Text to be logged for the command.
/// Parameters for the command.
public RelationalCommand(
RelationalCommandBuilderDependencies dependencies,
string commandText,
+ string logCommandText,
IReadOnlyList parameters)
{
Dependencies = dependencies;
CommandText = commandText;
+ LogCommandText = logCommandText;
Parameters = parameters;
}
@@ -54,6 +57,11 @@ public RelationalCommand(
///
public virtual string CommandText { get; private set; }
+ ///
+ /// Gets the command text to be logged.
+ ///
+ public virtual string LogCommandText { get; private set; }
+
///
/// Gets the parameters for the command.
///
@@ -89,6 +97,7 @@ public virtual int ExecuteNonQuery(RelationalCommandParameterObject parameterObj
var interceptionResult = logger?.CommandNonQueryExecuting(
connection,
command,
+ LogCommandText,
context,
commandId,
connection.ConnectionId,
@@ -103,6 +112,7 @@ public virtual int ExecuteNonQuery(RelationalCommandParameterObject parameterObj
return logger?.CommandNonQueryExecuted(
connection,
command,
+ LogCommandText,
context,
commandId,
connection.ConnectionId,
@@ -124,6 +134,7 @@ public virtual int ExecuteNonQuery(RelationalCommandParameterObject parameterObj
logger?.CommandCanceled(
connection,
command,
+ LogCommandText,
context,
DbCommandMethod.ExecuteNonQuery,
commandId,
@@ -137,6 +148,7 @@ public virtual int ExecuteNonQuery(RelationalCommandParameterObject parameterObj
logger?.CommandError(
connection,
command,
+ LogCommandText,
context,
DbCommandMethod.ExecuteNonQuery,
commandId,
@@ -193,6 +205,7 @@ public virtual async Task ExecuteNonQueryAsync(
: await logger.CommandNonQueryExecutingAsync(
connection,
command,
+ LogCommandText,
context,
commandId,
connection.ConnectionId,
@@ -210,6 +223,7 @@ public virtual async Task ExecuteNonQueryAsync(
result = await logger.CommandNonQueryExecutedAsync(
connection,
command,
+ LogCommandText,
context,
commandId,
connection.ConnectionId,
@@ -237,6 +251,7 @@ public virtual async Task ExecuteNonQueryAsync(
await logger.CommandCanceledAsync(
connection,
command,
+ LogCommandText,
context,
DbCommandMethod.ExecuteNonQuery,
commandId,
@@ -252,6 +267,7 @@ await logger.CommandCanceledAsync(
await logger.CommandErrorAsync(
connection,
command,
+ LogCommandText,
context,
DbCommandMethod.ExecuteNonQuery,
commandId,
@@ -303,6 +319,7 @@ await logger.CommandErrorAsync(
var interceptionResult = logger?.CommandScalarExecuting(
connection,
command,
+ LogCommandText,
context,
commandId,
connection.ConnectionId,
@@ -317,6 +334,7 @@ await logger.CommandErrorAsync(
return logger?.CommandScalarExecuted(
connection,
command,
+ LogCommandText,
context,
commandId,
connection.ConnectionId,
@@ -338,6 +356,7 @@ await logger.CommandErrorAsync(
logger?.CommandCanceled(
connection,
command,
+ LogCommandText,
context,
DbCommandMethod.ExecuteScalar,
commandId,
@@ -351,6 +370,7 @@ await logger.CommandErrorAsync(
logger?.CommandError(
connection,
command,
+ LogCommandText,
context,
DbCommandMethod.ExecuteScalar,
commandId,
@@ -407,6 +427,7 @@ await logger.CommandErrorAsync(
: await logger.CommandScalarExecutingAsync(
connection,
command,
+ LogCommandText,
context,
commandId,
connection.ConnectionId,
@@ -424,6 +445,7 @@ await logger.CommandErrorAsync(
result = await logger.CommandScalarExecutedAsync(
connection,
command,
+ LogCommandText,
context,
commandId,
connection.ConnectionId,
@@ -450,6 +472,7 @@ await logger.CommandErrorAsync(
await logger.CommandCanceledAsync(
connection,
command,
+ LogCommandText,
context,
DbCommandMethod.ExecuteScalar,
commandId,
@@ -465,6 +488,7 @@ await logger.CommandCanceledAsync(
await logger.CommandErrorAsync(
connection,
command,
+ LogCommandText,
context,
DbCommandMethod.ExecuteScalar,
commandId,
@@ -521,6 +545,7 @@ public virtual RelationalDataReader ExecuteReader(RelationalCommandParameterObje
var interceptionResult = logger!.CommandReaderExecuting(
connection,
command,
+ LogCommandText,
context,
commandId,
connection.ConnectionId,
@@ -534,6 +559,7 @@ public virtual RelationalDataReader ExecuteReader(RelationalCommandParameterObje
reader = logger.CommandReaderExecuted(
connection,
command,
+ LogCommandText,
context,
commandId,
connection.ConnectionId,
@@ -554,6 +580,7 @@ public virtual RelationalDataReader ExecuteReader(RelationalCommandParameterObje
logger?.CommandCanceled(
connection,
command,
+ LogCommandText,
context,
DbCommandMethod.ExecuteReader,
commandId,
@@ -567,6 +594,7 @@ public virtual RelationalDataReader ExecuteReader(RelationalCommandParameterObje
logger?.CommandError(
connection,
command,
+ LogCommandText,
context,
DbCommandMethod.ExecuteReader,
commandId,
@@ -647,6 +675,7 @@ public virtual async Task ExecuteReaderAsync(
var interceptionResult = await logger!.CommandReaderExecutingAsync(
connection,
command,
+ LogCommandText,
context,
commandId,
connection.ConnectionId,
@@ -662,6 +691,7 @@ public virtual async Task ExecuteReaderAsync(
reader = await logger.CommandReaderExecutedAsync(
connection,
command,
+ LogCommandText,
context,
commandId,
connection.ConnectionId,
@@ -686,6 +716,7 @@ public virtual async Task ExecuteReaderAsync(
await logger.CommandCanceledAsync(
connection,
command,
+ LogCommandText,
context,
DbCommandMethod.ExecuteReader,
commandId,
@@ -701,6 +732,7 @@ await logger.CommandCanceledAsync(
await logger.CommandErrorAsync(
connection,
command,
+ LogCommandText,
context,
DbCommandMethod.ExecuteReader,
commandId,
@@ -874,6 +906,7 @@ protected virtual RelationalDataReader CreateRelationalDataReader()
public virtual void PopulateFrom(IRelationalCommandTemplate commandTemplate)
{
CommandText = commandTemplate.CommandText;
+ LogCommandText = commandTemplate.LogCommandText;
Parameters = commandTemplate.Parameters;
}
}
diff --git a/src/EFCore.Relational/Storage/RelationalCommandBuilder.cs b/src/EFCore.Relational/Storage/RelationalCommandBuilder.cs
index 079cf521883..6a05c14b430 100644
--- a/src/EFCore.Relational/Storage/RelationalCommandBuilder.cs
+++ b/src/EFCore.Relational/Storage/RelationalCommandBuilder.cs
@@ -10,6 +10,7 @@ public class RelationalCommandBuilder : IRelationalCommandBuilder
{
private readonly List _parameters = [];
private readonly IndentedStringBuilder _commandTextBuilder = new();
+ private IndentedStringBuilder? _logCommandTextBuilder;
///
///
@@ -37,7 +38,11 @@ public virtual IRelationalTypeMappingSource TypeMappingSource
///
public virtual IRelationalCommand Build()
- => new RelationalCommand(Dependencies, _commandTextBuilder.ToString(), Parameters);
+ {
+ var commandText = _commandTextBuilder.ToString();
+ var logCommandText = _logCommandTextBuilder?.ToString() ?? commandText;
+ return new RelationalCommand(Dependencies, commandText, logCommandText, Parameters);
+ }
///
/// Gets the command text.
@@ -66,17 +71,21 @@ public virtual IRelationalCommandBuilder RemoveParameterAt(int index)
}
///
- public virtual IRelationalCommandBuilder Append(string value)
+ public virtual IRelationalCommandBuilder Append(string value, bool redact = false)
{
+ InitializeLogCommandTextBuilderIfNeeded(redact);
_commandTextBuilder.Append(value);
+ _logCommandTextBuilder?.Append(redact ? "?" : value);
return this;
}
///
- public virtual IRelationalCommandBuilder Append(FormattableString value)
+ public virtual IRelationalCommandBuilder Append(FormattableString value, bool redact = false)
{
+ InitializeLogCommandTextBuilderIfNeeded(redact);
_commandTextBuilder.Append(value);
+ _logCommandTextBuilder?.Append(redact ? $"?" : value);
return this;
}
@@ -85,6 +94,7 @@ public virtual IRelationalCommandBuilder Append(FormattableString value)
public virtual IRelationalCommandBuilder AppendLine()
{
_commandTextBuilder.AppendLine();
+ _logCommandTextBuilder?.AppendLine();
return this;
}
@@ -93,6 +103,7 @@ public virtual IRelationalCommandBuilder AppendLine()
public virtual IRelationalCommandBuilder IncrementIndent()
{
_commandTextBuilder.IncrementIndent();
+ _logCommandTextBuilder?.IncrementIndent();
return this;
}
@@ -101,6 +112,7 @@ public virtual IRelationalCommandBuilder IncrementIndent()
public virtual IRelationalCommandBuilder DecrementIndent()
{
_commandTextBuilder.DecrementIndent();
+ _logCommandTextBuilder?.DecrementIndent();
return this;
}
@@ -108,4 +120,14 @@ public virtual IRelationalCommandBuilder DecrementIndent()
///
public virtual int CommandTextLength
=> _commandTextBuilder.Length;
+
+ private void InitializeLogCommandTextBuilderIfNeeded(bool redact)
+ {
+ if (redact
+ && _logCommandTextBuilder is null
+ && !Dependencies.LoggingOptions.IsSensitiveDataLoggingEnabled)
+ {
+ _logCommandTextBuilder = _commandTextBuilder.Clone();
+ }
+ }
}
diff --git a/src/EFCore.Relational/Storage/RelationalCommandBuilderDependencies.cs b/src/EFCore.Relational/Storage/RelationalCommandBuilderDependencies.cs
index 79bbd85c859..3db84e30c0c 100644
--- a/src/EFCore.Relational/Storage/RelationalCommandBuilderDependencies.cs
+++ b/src/EFCore.Relational/Storage/RelationalCommandBuilderDependencies.cs
@@ -46,9 +46,11 @@ public sealed record RelationalCommandBuilderDependencies
[EntityFrameworkInternal]
public RelationalCommandBuilderDependencies(
IRelationalTypeMappingSource typeMappingSource,
- IExceptionDetector exceptionDetector)
+ IExceptionDetector exceptionDetector,
+ ILoggingOptions loggingOptions)
{
ExceptionDetector = exceptionDetector;
+ LoggingOptions = loggingOptions;
#pragma warning disable CS0618 // Type or member is obsolete
TypeMappingSource = typeMappingSource;
@@ -60,6 +62,11 @@ public RelationalCommandBuilderDependencies(
///
public IExceptionDetector ExceptionDetector { get; init; }
+ ///
+ /// The logging options.
+ ///
+ public ILoggingOptions LoggingOptions { get; init; }
+
///
/// The source for s to use.
///
diff --git a/src/EFCore.Relational/Storage/RelationalConnection.cs b/src/EFCore.Relational/Storage/RelationalConnection.cs
index 8b1169d3641..f5507648334 100644
--- a/src/EFCore.Relational/Storage/RelationalConnection.cs
+++ b/src/EFCore.Relational/Storage/RelationalConnection.cs
@@ -42,6 +42,7 @@ public abstract class RelationalConnection : IRelationalConnection, ITransaction
private readonly ConcurrentStack _ambientTransactions = new();
private DbConnection? _connection;
private readonly IRelationalCommandBuilder _relationalCommandBuilder;
+ private readonly IExceptionDetector _exceptionDetector;
private IRelationalCommand? _cachedRelationalCommand;
///
@@ -74,6 +75,8 @@ protected RelationalConnection(RelationalConnectionDependencies dependencies)
{
_connectionOwned = true;
}
+
+ _exceptionDetector = dependencies.ExceptionDetector;
}
///
@@ -726,7 +729,14 @@ private void OpenInternal(bool errorsExpected)
}
catch (Exception e)
{
- logger.ConnectionError(this, e, startTime, stopwatch.Elapsed, errorsExpected);
+ if (_exceptionDetector.IsCancellation(e, CancellationToken.None))
+ {
+ logger.ConnectionCanceled(this, startTime, stopwatch.Elapsed);
+ }
+ else
+ {
+ logger.ConnectionError(this, e, startTime, stopwatch.Elapsed, errorsExpected);
+ }
throw;
}
@@ -773,14 +783,16 @@ await logger.ConnectionOpenedAsync(this, startTime, stopwatch.Elapsed, cancellat
}
catch (Exception e)
{
- await logger.ConnectionErrorAsync(
- this,
- e,
- startTime,
- stopwatch.Elapsed,
- errorsExpected,
- cancellationToken)
- .ConfigureAwait(false);
+ if (_exceptionDetector.IsCancellation(e, CancellationToken.None))
+ {
+ await logger.ConnectionCanceledAsync(this, startTime, stopwatch.Elapsed, cancellationToken)
+ .ConfigureAwait(false);
+ }
+ else
+ {
+ await logger.ConnectionErrorAsync(this, e, startTime, stopwatch.Elapsed, errorsExpected, cancellationToken)
+ .ConfigureAwait(false);
+ }
throw;
}
diff --git a/src/EFCore.Relational/Storage/RelationalConnectionDependencies.cs b/src/EFCore.Relational/Storage/RelationalConnectionDependencies.cs
index c6797be24f1..714b30e0a18 100644
--- a/src/EFCore.Relational/Storage/RelationalConnectionDependencies.cs
+++ b/src/EFCore.Relational/Storage/RelationalConnectionDependencies.cs
@@ -54,7 +54,8 @@ public RelationalConnectionDependencies(
INamedConnectionStringResolver connectionStringResolver,
IRelationalTransactionFactory relationalTransactionFactory,
ICurrentDbContext currentContext,
- IRelationalCommandBuilderFactory relationalCommandBuilderFactory)
+ IRelationalCommandBuilderFactory relationalCommandBuilderFactory,
+ IExceptionDetector exceptionDetector)
{
ContextOptions = contextOptions;
TransactionLogger = transactionLogger;
@@ -63,6 +64,7 @@ public RelationalConnectionDependencies(
RelationalTransactionFactory = relationalTransactionFactory;
CurrentContext = currentContext;
RelationalCommandBuilderFactory = relationalCommandBuilderFactory;
+ ExceptionDetector = exceptionDetector;
}
///
@@ -100,4 +102,9 @@ public RelationalConnectionDependencies(
/// Contains the instance currently in use.
///
public IRelationalCommandBuilderFactory RelationalCommandBuilderFactory { get; init; }
+
+ ///
+ /// The exception detector.
+ ///
+ public IExceptionDetector ExceptionDetector { get; init; }
}
diff --git a/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs b/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs
index 9cf220045a6..10ad36fcdb4 100644
--- a/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs
+++ b/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs
@@ -2577,10 +2577,16 @@ private IReadOnlyList RewriteOperations(
var newRawSchema = renameTableOperation.NewSchema;
var newSchema = newRawSchema ?? model?.GetDefaultSchema();
+ var temporalTableInformation = BuildTemporalInformationFromMigrationOperation(schema, renameTableOperation);
if (!temporalTableInformationMap.ContainsKey((tableName, rawSchema)))
{
- var temporalTableInformation = BuildTemporalInformationFromMigrationOperation(schema, renameTableOperation);
temporalTableInformationMap[(tableName, rawSchema)] = temporalTableInformation;
+ }
+
+ // we still need to check here - table with the new name could have existed before and have been deleted
+ // we want to preserve the original temporal info of that deleted table
+ if (!temporalTableInformationMap.ContainsKey((newTableName, newRawSchema)))
+ {
temporalTableInformationMap[(newTableName, newRawSchema)] = temporalTableInformation;
}
@@ -2675,10 +2681,19 @@ private IReadOnlyList RewriteOperations(
var schema = rawSchema ?? model?.GetDefaultSchema();
- // we are guaranteed to find entry here - we looped through all the operations earlier,
- // info missing from operations we got from the model
- // and in case of no/incomplete model we created dummy (non-temporal) entries
- var temporalInformation = temporalTableInformationMap[(tableName, rawSchema)];
+ TemporalOperationInformation temporalInformation;
+ if (operation is CreateTableOperation)
+ {
+ // for create table we always generate new temporal information from the operation itself
+ // just in case there was a table with that name before that got deleted/renamed
+ // also, temporal state (disabled versioning etc.) should always reset when creating a table
+ temporalInformation = BuildTemporalInformationFromMigrationOperation(schema, operation);
+ temporalTableInformationMap[(tableName, rawSchema)] = temporalInformation;
+ }
+ else
+ {
+ temporalInformation = temporalTableInformationMap[(tableName, rawSchema)];
+ }
switch (operation)
{
diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs
index c0a7d72d43c..8e4edd47f06 100644
--- a/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs
+++ b/src/EFCore.SqlServer/Query/Internal/SqlServerSqlTranslatingExpressionVisitor.cs
@@ -53,15 +53,24 @@ private static readonly HashSet ArithmeticOperatorTypes
ExpressionType.Modulo
];
- private static readonly MethodInfo StringStartsWithMethodInfo
+ private static readonly MethodInfo StringStartsWithMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)])!;
- private static readonly MethodInfo StringEndsWithMethodInfo
+ private static readonly MethodInfo StringStartsWithMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(char)])!;
+
+ private static readonly MethodInfo StringEndsWithMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)])!;
- private static readonly MethodInfo StringContainsMethodInfo
+ private static readonly MethodInfo StringEndsWithMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(char)])!;
+
+ private static readonly MethodInfo StringContainsMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)])!;
+ private static readonly MethodInfo StringContainsMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(char)])!;
+
private static readonly MethodInfo StringJoinMethodInfo
= typeof(string).GetRuntimeMethod(nameof(string.Join), [typeof(string), typeof(string[])])!;
@@ -187,21 +196,21 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
methodCallExpression.Type);
}
- if (method == StringStartsWithMethodInfo
+ if ((method == StringStartsWithMethodInfoString || method == StringStartsWithMethodInfoChar)
&& TryTranslateStartsEndsWithContains(
methodCallExpression.Object!, methodCallExpression.Arguments[0], StartsEndsWithContains.StartsWith, out var translation1))
{
return translation1;
}
- if (method == StringEndsWithMethodInfo
+ if ((method == StringEndsWithMethodInfoString || method == StringEndsWithMethodInfoChar)
&& TryTranslateStartsEndsWithContains(
methodCallExpression.Object!, methodCallExpression.Arguments[0], StartsEndsWithContains.EndsWith, out var translation2))
{
return translation2;
}
- if (method == StringContainsMethodInfo
+ if ((method == StringContainsMethodInfoString || method == StringContainsMethodInfoChar)
&& TryTranslateStartsEndsWithContains(
methodCallExpression.Object!, methodCallExpression.Arguments[0], StartsEndsWithContains.Contains, out var translation3))
{
@@ -328,6 +337,32 @@ string when _sqlServerSingletonOptions.EngineType is SqlServerEngineType.AzureSy
}),
_sqlExpressionFactory.Constant(LikeEscapeString)),
+ char s when !IsLikeWildChar(s)
+ => _sqlExpressionFactory.Like(
+ translatedInstance,
+ _sqlExpressionFactory.Constant(
+ methodType switch
+ {
+ StartsEndsWithContains.StartsWith => s + "%",
+ StartsEndsWithContains.EndsWith => "%" + s,
+ StartsEndsWithContains.Contains => $"%{s}%",
+
+ _ => throw new ArgumentOutOfRangeException(nameof(methodType), methodType, null)
+ })),
+
+ char s => _sqlExpressionFactory.Like(
+ translatedInstance,
+ _sqlExpressionFactory.Constant(
+ methodType switch
+ {
+ StartsEndsWithContains.StartsWith => LikeEscapeChar + s + "%",
+ StartsEndsWithContains.EndsWith => "%" + LikeEscapeChar + s,
+ StartsEndsWithContains.Contains => $"%{LikeEscapeChar}{s}%",
+
+ _ => throw new ArgumentOutOfRangeException(nameof(methodType), methodType, null)
+ }),
+ _sqlExpressionFactory.Constant(LikeEscapeString)),
+
_ => throw new UnreachableException()
};
@@ -463,6 +498,22 @@ SqlExpression CharIndexGreaterThanZero()
_ => throw new ArgumentOutOfRangeException(nameof(methodType), methodType, null)
},
+ char s when !IsLikeWildChar(s) => methodType switch
+ {
+ StartsEndsWithContains.StartsWith => s + "%",
+ StartsEndsWithContains.EndsWith => "%" + s,
+ StartsEndsWithContains.Contains => $"%{s}%",
+ _ => throw new ArgumentOutOfRangeException(nameof(methodType), methodType, null)
+ },
+
+ char s => methodType switch
+ {
+ StartsEndsWithContains.StartsWith => LikeEscapeChar + s + "%",
+ StartsEndsWithContains.EndsWith => "%" + LikeEscapeChar + s,
+ StartsEndsWithContains.Contains => $"%{LikeEscapeChar}{s}%",
+ _ => throw new ArgumentOutOfRangeException(nameof(methodType), methodType, null)
+ },
+
_ => throw new UnreachableException()
};
diff --git a/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerStringMethodTranslator.cs b/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerStringMethodTranslator.cs
index 6d0a410bb73..db7c88a1e02 100644
--- a/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerStringMethodTranslator.cs
+++ b/src/EFCore.SqlServer/Query/Internal/Translators/SqlServerStringMethodTranslator.cs
@@ -3,6 +3,7 @@
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal;
+using CharTypeMapping = Microsoft.EntityFrameworkCore.Storage.CharTypeMapping;
using ExpressionExtensions = Microsoft.EntityFrameworkCore.Query.ExpressionExtensions;
// ReSharper disable once CheckNamespace
@@ -16,15 +17,24 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
///
public class SqlServerStringMethodTranslator : IMethodCallTranslator
{
- private static readonly MethodInfo IndexOfMethodInfo
+ private static readonly MethodInfo IndexOfMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.IndexOf), [typeof(string)])!;
- private static readonly MethodInfo IndexOfMethodInfoWithStartingPosition
+ private static readonly MethodInfo IndexOfMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.IndexOf), [typeof(char)])!;
+
+ private static readonly MethodInfo IndexOfMethodInfoWithStartingPositionString
= typeof(string).GetRuntimeMethod(nameof(string.IndexOf), [typeof(string), typeof(int)])!;
- private static readonly MethodInfo ReplaceMethodInfo
+ private static readonly MethodInfo IndexOfMethodInfoWithStartingPositionChar
+ = typeof(string).GetRuntimeMethod(nameof(string.IndexOf), [typeof(char), typeof(int)])!;
+
+ private static readonly MethodInfo ReplaceMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.Replace), [typeof(string), typeof(string)])!;
+ private static readonly MethodInfo ReplaceMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.Replace), [typeof(char), typeof(char)])!;
+
private static readonly MethodInfo ToLowerMethodInfo
= typeof(string).GetRuntimeMethod(nameof(string.ToLower), Type.EmptyTypes)!;
@@ -115,25 +125,25 @@ public SqlServerStringMethodTranslator(ISqlExpressionFactory sqlExpressionFactor
{
if (instance != null)
{
- if (IndexOfMethodInfo.Equals(method))
+ if (IndexOfMethodInfoString.Equals(method) || IndexOfMethodInfoChar.Equals(method))
{
return TranslateIndexOf(instance, method, arguments[0], null);
}
- if (IndexOfMethodInfoWithStartingPosition.Equals(method))
+ if (IndexOfMethodInfoWithStartingPositionString.Equals(method) || IndexOfMethodInfoWithStartingPositionChar.Equals(method))
{
return TranslateIndexOf(instance, method, arguments[0], arguments[1]);
}
- if (ReplaceMethodInfo.Equals(method))
+ if (ReplaceMethodInfoString.Equals(method) || ReplaceMethodInfoChar.Equals(method))
{
var firstArgument = arguments[0];
var secondArgument = arguments[1];
var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, firstArgument, secondArgument);
instance = _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping);
- firstArgument = _sqlExpressionFactory.ApplyTypeMapping(firstArgument, stringTypeMapping);
- secondArgument = _sqlExpressionFactory.ApplyTypeMapping(secondArgument, stringTypeMapping);
+ firstArgument = _sqlExpressionFactory.ApplyTypeMapping(firstArgument, firstArgument.Type == typeof(char) ? CharTypeMapping.Default : stringTypeMapping);
+ secondArgument = _sqlExpressionFactory.ApplyTypeMapping(secondArgument, secondArgument.Type == typeof(char) ? CharTypeMapping.Default : stringTypeMapping);
return _sqlExpressionFactory.Function(
"REPLACE",
@@ -323,7 +333,8 @@ private SqlExpression TranslateIndexOf(
SqlExpression? startIndex)
{
var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, searchExpression)!;
- searchExpression = _sqlExpressionFactory.ApplyTypeMapping(searchExpression, stringTypeMapping);
+ searchExpression = _sqlExpressionFactory.ApplyTypeMapping(searchExpression, searchExpression.Type == typeof(char) ? CharTypeMapping.Default : stringTypeMapping);
+
instance = _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping);
var charIndexArguments = new List { searchExpression, instance };
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerOwnedJsonTypeMapping.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerOwnedJsonTypeMapping.cs
index 6bd4cfeffb0..801ac044755 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerOwnedJsonTypeMapping.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerOwnedJsonTypeMapping.cs
@@ -45,7 +45,7 @@ private static readonly MethodInfo GetStringMethod
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public SqlServerOwnedJsonTypeMapping(string storeType)
- : base(storeType, typeof(JsonElement), System.Data.DbType.String)
+ : base(storeType, typeof(JsonTypePlaceholder), System.Data.DbType.String)
{
}
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs
index 50d50413c28..b89cfbc9f60 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs
@@ -3,7 +3,6 @@
using System.Collections;
using System.Data;
-using System.Text.Json;
namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
@@ -239,7 +238,7 @@ public SqlServerTypeMappingSource(
var clrType = mappingInfo.ClrType;
var storeTypeName = mappingInfo.StoreTypeName;
- if (clrType == typeof(JsonElement))
+ if (clrType == typeof(JsonTypePlaceholder))
{
return storeTypeName == "json"
? SqlServerOwnedJsonTypeMapping.OwnedJsonTypeDefault
diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs
index 48aa49e1c2c..0d04114979f 100644
--- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteSqlTranslatingExpressionVisitor.cs
@@ -19,12 +19,18 @@ public class SqliteSqlTranslatingExpressionVisitor : RelationalSqlTranslatingExp
private readonly QueryCompilationContext _queryCompilationContext;
private readonly ISqlExpressionFactory _sqlExpressionFactory;
- private static readonly MethodInfo StringStartsWithMethodInfo
+ private static readonly MethodInfo StringStartsWithMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)])!;
- private static readonly MethodInfo StringEndsWithMethodInfo
+ private static readonly MethodInfo StringStartsWithMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(char)])!;
+
+ private static readonly MethodInfo StringEndsWithMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)])!;
+ private static readonly MethodInfo StringEndsWithMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(char)])!;
+
private static readonly MethodInfo EscapeLikePatternParameterMethod =
typeof(SqliteSqlTranslatingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(ConstructLikePatternParameter))!;
@@ -255,14 +261,14 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
{
var method = methodCallExpression.Method;
- if (method == StringStartsWithMethodInfo
+ if ((method == StringStartsWithMethodInfoString || method == StringStartsWithMethodInfoChar)
&& TryTranslateStartsEndsWith(
methodCallExpression.Object!, methodCallExpression.Arguments[0], startsWith: true, out var translation1))
{
return translation1;
}
- if (method == StringEndsWithMethodInfo
+ if ((method == StringEndsWithMethodInfoString || method == StringEndsWithMethodInfoChar)
&& TryTranslateStartsEndsWith(
methodCallExpression.Object!, methodCallExpression.Arguments[0], startsWith: false, out var translation2))
{
@@ -316,6 +322,15 @@ bool TryTranslateStartsEndsWith(
translatedInstance,
_sqlExpressionFactory.Constant(startsWith ? s + '%' : '%' + s)),
+ char s => IsLikeWildChar(s)
+ ? _sqlExpressionFactory.Like(
+ translatedInstance,
+ _sqlExpressionFactory.Constant(startsWith ? LikeEscapeString + s + "%" : '%' + LikeEscapeString + s),
+ _sqlExpressionFactory.Constant(LikeEscapeString))
+ : _sqlExpressionFactory.Like(
+ translatedInstance,
+ _sqlExpressionFactory.Constant(startsWith ? s + "%" : "%" + s)),
+
_ => throw new UnreachableException()
};
@@ -443,6 +458,10 @@ bool TryTranslateStartsEndsWith(
string s => startsWith ? EscapeLikePattern(s) + '%' : '%' + EscapeLikePattern(s),
+ char s when IsLikeWildChar(s )=> startsWith ? LikeEscapeString + s + '%' : '%' + LikeEscapeString + s,
+
+ char s => startsWith ? s + "%" : "%" + s,
+
_ => throw new UnreachableException()
};
diff --git a/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteStringMethodTranslator.cs b/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteStringMethodTranslator.cs
index bc38e4af196..7297982a0e6 100644
--- a/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteStringMethodTranslator.cs
+++ b/src/EFCore.Sqlite.Core/Query/Internal/Translators/SqliteStringMethodTranslator.cs
@@ -15,12 +15,24 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal;
///
public class SqliteStringMethodTranslator : IMethodCallTranslator
{
- private static readonly MethodInfo IndexOfMethodInfo
+ private static readonly MethodInfo IndexOfMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.IndexOf), [typeof(string)])!;
- private static readonly MethodInfo ReplaceMethodInfo
+ private static readonly MethodInfo IndexOfMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.IndexOf), [typeof(char)])!;
+
+ private static readonly MethodInfo IndexOfMethodInfoWithStartingPositionString
+ = typeof(string).GetRuntimeMethod(nameof(string.IndexOf), [typeof(string), typeof(int)])!;
+
+ private static readonly MethodInfo IndexOfMethodInfoWithStartingPositionChar
+ = typeof(string).GetRuntimeMethod(nameof(string.IndexOf), [typeof(char), typeof(int)])!;
+
+ private static readonly MethodInfo ReplaceMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.Replace), [typeof(string), typeof(string)])!;
+ private static readonly MethodInfo ReplaceMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.Replace), [typeof(char), typeof(char)])!;
+
private static readonly MethodInfo ToLowerMethodInfo
= typeof(string).GetRuntimeMethod(nameof(string.ToLower), Type.EmptyTypes)!;
@@ -65,8 +77,10 @@ private static readonly MethodInfo TrimEndMethodInfoWithCharArrayArg
private static readonly MethodInfo TrimMethodInfoWithCharArrayArg
= typeof(string).GetRuntimeMethod(nameof(string.Trim), [typeof(char[])])!;
- private static readonly MethodInfo ContainsMethodInfo
+ private static readonly MethodInfo ContainsMethodInfoString
= typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)])!;
+ private static readonly MethodInfo ContainsMethodInfoChar
+ = typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(char)])!;
private static readonly MethodInfo FirstOrDefaultMethodInfoWithoutArgs
= typeof(Enumerable).GetRuntimeMethods().Single(
@@ -103,7 +117,7 @@ public SqliteStringMethodTranslator(ISqlExpressionFactory sqlExpressionFactory)
{
if (instance != null)
{
- if (IndexOfMethodInfo.Equals(method))
+ if (IndexOfMethodInfoString.Equals(method) || IndexOfMethodInfoChar.Equals(method))
{
var argument = arguments[0];
var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, argument);
@@ -114,7 +128,7 @@ public SqliteStringMethodTranslator(ISqlExpressionFactory sqlExpressionFactory)
new[]
{
_sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping),
- _sqlExpressionFactory.ApplyTypeMapping(argument, stringTypeMapping)
+ _sqlExpressionFactory.ApplyTypeMapping(argument, argument.Type == typeof(char) ? CharTypeMapping.Default : stringTypeMapping)
},
nullable: true,
argumentsPropagateNullability: Statics.TrueArrays[2],
@@ -122,7 +136,35 @@ public SqliteStringMethodTranslator(ISqlExpressionFactory sqlExpressionFactory)
_sqlExpressionFactory.Constant(1));
}
- if (ReplaceMethodInfo.Equals(method))
+ if (IndexOfMethodInfoWithStartingPositionString.Equals(method) || IndexOfMethodInfoWithStartingPositionChar.Equals(method))
+ {
+ var argument = arguments[0];
+ var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, argument);
+ instance = _sqlExpressionFactory.Function(
+ "substr",
+ new[] { instance, _sqlExpressionFactory.Add(arguments[1], _sqlExpressionFactory.Constant(1)) },
+ nullable: true,
+ argumentsPropagateNullability: Statics.TrueArrays[2],
+ method.ReturnType,
+ instance.TypeMapping);
+
+ return _sqlExpressionFactory.Add(
+ _sqlExpressionFactory.Subtract(
+ _sqlExpressionFactory.Function(
+ "instr",
+ new[]
+ {
+ _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping),
+ _sqlExpressionFactory.ApplyTypeMapping(argument, argument.Type == typeof(char) ? CharTypeMapping.Default : stringTypeMapping)
+ },
+ nullable: true,
+ argumentsPropagateNullability: Statics.TrueArrays[2],
+ method.ReturnType),
+ _sqlExpressionFactory.Constant(1)),
+ arguments[1]);
+ }
+
+ if (ReplaceMethodInfoString.Equals(method) || ReplaceMethodInfoChar.Equals(method))
{
var firstArgument = arguments[0];
var secondArgument = arguments[1];
@@ -133,8 +175,8 @@ public SqliteStringMethodTranslator(ISqlExpressionFactory sqlExpressionFactory)
new[]
{
_sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping),
- _sqlExpressionFactory.ApplyTypeMapping(firstArgument, stringTypeMapping),
- _sqlExpressionFactory.ApplyTypeMapping(secondArgument, stringTypeMapping)
+ _sqlExpressionFactory.ApplyTypeMapping(firstArgument, firstArgument.Type == typeof(char) ? CharTypeMapping.Default : stringTypeMapping),
+ _sqlExpressionFactory.ApplyTypeMapping(secondArgument, secondArgument.Type == typeof(char) ? CharTypeMapping.Default : stringTypeMapping)
},
nullable: true,
argumentsPropagateNullability: Statics.TrueArrays[3],
@@ -197,13 +239,13 @@ public SqliteStringMethodTranslator(ISqlExpressionFactory sqlExpressionFactory)
return ProcessTrimMethod(instance, arguments, "trim");
}
- if (ContainsMethodInfo.Equals(method))
+ if (ContainsMethodInfoString.Equals(method) || ContainsMethodInfoChar.Equals(method))
{
var pattern = arguments[0];
var stringTypeMapping = ExpressionExtensions.InferTypeMapping(instance, pattern);
instance = _sqlExpressionFactory.ApplyTypeMapping(instance, stringTypeMapping);
- pattern = _sqlExpressionFactory.ApplyTypeMapping(pattern, stringTypeMapping);
+ pattern = _sqlExpressionFactory.ApplyTypeMapping(pattern, pattern.Type == typeof(char) ? CharTypeMapping.Default : stringTypeMapping);
return
_sqlExpressionFactory.GreaterThan(
diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteJsonTypeMapping.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteJsonTypeMapping.cs
index 2b40d34a556..51925eb4d9e 100644
--- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteJsonTypeMapping.cs
+++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteJsonTypeMapping.cs
@@ -39,7 +39,7 @@ private static readonly ConstructorInfo MemoryStreamConstructor
///
/// The name of the database type.
public SqliteJsonTypeMapping(string storeType)
- : base(storeType, typeof(JsonElement), System.Data.DbType.String)
+ : base(storeType, typeof(JsonTypePlaceholder), System.Data.DbType.String)
{
}
diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs
index 8e9e80978f0..2cdc07fefe9 100644
--- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs
+++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs
@@ -83,7 +83,7 @@ private static readonly HashSet SpatialiteTypes
{ typeof(double), Real },
{ typeof(float), new FloatTypeMapping(RealTypeName) },
{ typeof(Guid), SqliteGuidTypeMapping.Default },
- { typeof(JsonElement), SqliteJsonTypeMapping.Default }
+ { typeof(JsonTypePlaceholder), SqliteJsonTypeMapping.Default }
};
private readonly Dictionary _storeTypeMappings = new(StringComparer.OrdinalIgnoreCase)
diff --git a/src/EFCore.Sqlite/PACKAGE.md b/src/EFCore.Sqlite/PACKAGE.md
index 27712755653..c861db6ff34 100644
--- a/src/EFCore.Sqlite/PACKAGE.md
+++ b/src/EFCore.Sqlite/PACKAGE.md
@@ -7,7 +7,7 @@ Call the `UseSqlite` method to choose the SQLite database provider for your `DbC
```csharp
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
- optionsBuilder.UseSqlite("Data Source=databse.dat");
+ optionsBuilder.UseSqlite("Data Source=database.db");
}
```
diff --git a/src/EFCore.Tasks/EFCore.Tasks.csproj b/src/EFCore.Tasks/EFCore.Tasks.csproj
index eb72b2ae76a..6e4d77a6802 100644
--- a/src/EFCore.Tasks/EFCore.Tasks.csproj
+++ b/src/EFCore.Tasks/EFCore.Tasks.csproj
@@ -49,7 +49,6 @@
-
diff --git a/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs b/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs
deleted file mode 100644
index e6b66ad06b9..00000000000
--- a/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using Microsoft.EntityFrameworkCore.Metadata.Internal;
-
-// ReSharper disable once CheckNamespace
-namespace Microsoft.EntityFrameworkCore;
-
-///
-/// Extension methods for .
-///
-[Obsolete("Use IConventionEntityType")] // Delete with defining query
-public static class ConventionEntityTypeExtensions
-{
- ///
- /// Sets the LINQ query used as the default source for queries of this type.
- ///
- /// The entity type.
- /// The LINQ query used as the default source.
- /// Indicates whether the configuration was specified using a data annotation.
- [Obsolete("Use InMemoryEntityTypeExtensions.SetInMemoryQuery")]
- public static void SetDefiningQuery(
- this IConventionEntityType entityType,
- LambdaExpression? definingQuery,
- bool fromDataAnnotation = false)
- => ((EntityType)entityType).SetDefiningQuery(
- definingQuery,
- fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
-
- ///
- /// Returns the configuration source for .
- ///
- /// The entity type.
- /// The configuration source for .
- [Obsolete("Use InMemoryEntityTypeExtensions.GetInMemoryQueryConfigurationSource")]
- public static ConfigurationSource? GetDefiningQueryConfigurationSource(this IConventionEntityType entityType)
- => entityType.FindAnnotation(CoreAnnotationNames.DefiningQuery)?.GetConfigurationSource();
-}
diff --git a/src/EFCore/Extensions/EntityTypeExtensions.cs b/src/EFCore/Extensions/EntityTypeExtensions.cs
deleted file mode 100644
index b99fbb127ce..00000000000
--- a/src/EFCore/Extensions/EntityTypeExtensions.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using Microsoft.EntityFrameworkCore.Metadata.Internal;
-
-// ReSharper disable once CheckNamespace
-namespace Microsoft.EntityFrameworkCore;
-
-///
-/// Entity type extension methods for .
-///
-[Obsolete("Use IReadOnlyEntityType")] // Delete with defining query
-public static class EntityTypeExtensions
-{
- ///
- /// Gets the LINQ query used as the default source for queries of this type.
- ///
- /// The entity type to get the defining query for.
- /// The LINQ query used as the default source.
- [Obsolete("Use InMemoryEntityTypeExtensions.GetInMemoryQuery")]
- public static LambdaExpression? GetDefiningQuery(this IEntityType entityType)
- => (LambdaExpression?)entityType[CoreAnnotationNames.DefiningQuery];
-}
diff --git a/src/EFCore/Extensions/MutableEntityTypeExtensions.cs b/src/EFCore/Extensions/MutableEntityTypeExtensions.cs
deleted file mode 100644
index 395b1aa72eb..00000000000
--- a/src/EFCore/Extensions/MutableEntityTypeExtensions.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using Microsoft.EntityFrameworkCore.Metadata.Internal;
-
-// ReSharper disable once CheckNamespace
-namespace Microsoft.EntityFrameworkCore;
-
-///
-/// Extension methods for .
-///
-[Obsolete("Use IMutableEntityType")] // Delete with defining query
-public static class MutableEntityTypeExtensions
-{
- ///
- /// Sets the LINQ query used as the default source for queries of this type.
- ///
- /// The entity type.
- /// The LINQ query used as the default source.
- [Obsolete("Use InMemoryEntityTypeExtensions.SetInMemoryQuery")]
- public static void SetDefiningQuery(
- this IMutableEntityType entityType,
- LambdaExpression? definingQuery)
- => ((EntityType)entityType).SetDefiningQuery(definingQuery, ConfigurationSource.Explicit);
-}
diff --git a/src/EFCore/Infrastructure/IndentedStringBuilder.cs b/src/EFCore/Infrastructure/IndentedStringBuilder.cs
index c2dd4523ed9..c77d21c3954 100644
--- a/src/EFCore/Infrastructure/IndentedStringBuilder.cs
+++ b/src/EFCore/Infrastructure/IndentedStringBuilder.cs
@@ -290,6 +290,19 @@ public virtual IDisposable Indent()
public virtual IDisposable SuspendIndent()
=> new IndentSuspender(this);
+ ///
+ /// Clones this , copying the built string and current indent level.
+ ///
+ /// New instance of .
+ public virtual IndentedStringBuilder Clone()
+ {
+ var result = new IndentedStringBuilder();
+ result._stringBuilder.Append(_stringBuilder);
+ result._indent = _indent;
+ result._indentPending = _indentPending;
+ return result;
+ }
+
///
/// Returns the built string.
///
diff --git a/src/EFCore/Infrastructure/Internal/LazyLoader.cs b/src/EFCore/Infrastructure/Internal/LazyLoader.cs
index 8215392c8d8..c575cd4f957 100644
--- a/src/EFCore/Infrastructure/Internal/LazyLoader.cs
+++ b/src/EFCore/Infrastructure/Internal/LazyLoader.cs
@@ -23,7 +23,8 @@ public class LazyLoader : ILazyLoader, IInjectableService
private bool _detached;
private IDictionary? _loadedStates;
private readonly Lock _isLoadingLock = new Lock();
- private readonly Dictionary<(object Entity, string NavigationName), (TaskCompletionSource TaskCompletionSource, AsyncLocal Depth)> _isLoading = new(NavEntryEqualityComparer.Instance);
+ private readonly Dictionary<(object Entity, string NavigationName), TaskCompletionSource> _isLoading = new(NavEntryEqualityComparer.Instance);
+ private static readonly AsyncLocal _isLoadingCallDepth = new();
private HashSet? _nonLazyNavigations;
///
@@ -112,26 +113,28 @@ public virtual void Load(object entity, [CallerMemberName] string navigationName
var navEntry = (entity, navigationName);
bool exists;
- (TaskCompletionSource TaskCompletionSource, AsyncLocal Depth) isLoadingValue;
+ TaskCompletionSource isLoadingValue;
lock (_isLoadingLock)
{
ref var refIsLoadingValue = ref CollectionsMarshal.GetValueRefOrAddDefault(_isLoading, navEntry, out exists);
if (!exists)
{
- refIsLoadingValue = (new(), new());
+ refIsLoadingValue = new();
}
+ _isLoadingCallDepth.Value++;
isLoadingValue = refIsLoadingValue!;
- isLoadingValue.Depth.Value++;
}
if (exists)
{
- // Only waits for the outermost call on the call stack. See #35528.
- if (isLoadingValue.Depth.Value == 1)
+ // Only waits for the outermost call on the call stack. See #35528.
+ // if _isLoadingCallDepth.Value > 1 the call is recursive, waiting probably generates a deadlock See #35832.
+ if (_isLoadingCallDepth.Value == 1)
{
- isLoadingValue.TaskCompletionSource.Task.Wait();
+ isLoadingValue.Task.Wait();
}
+ _isLoadingCallDepth.Value--;
return;
}
@@ -156,7 +159,8 @@ public virtual void Load(object entity, [CallerMemberName] string navigationName
}
finally
{
- isLoadingValue.TaskCompletionSource.TrySetResult();
+ isLoadingValue.TrySetResult();
+ _isLoadingCallDepth.Value--;
lock (_isLoadingLock)
{
_isLoading.Remove(navEntry);
@@ -181,26 +185,28 @@ public virtual async Task LoadAsync(
var navEntry = (entity, navigationName);
bool exists;
- (TaskCompletionSource TaskCompletionSource, AsyncLocal Depth) isLoadingValue;
+ TaskCompletionSource isLoadingValue;
lock (_isLoadingLock)
{
ref var refIsLoadingValue = ref CollectionsMarshal.GetValueRefOrAddDefault(_isLoading, navEntry, out exists);
if (!exists)
{
- refIsLoadingValue = (new(), new());
+ refIsLoadingValue = new();
}
+ _isLoadingCallDepth.Value++;
isLoadingValue = refIsLoadingValue!;
- isLoadingValue.Depth.Value++;
}
if (exists)
{
- // Only waits for the outermost call on the call stack. See #35528.
- if (isLoadingValue.Depth.Value == 1)
+ // Only waits for the outermost call on the call stack. See #35528.
+ // if _isLoadingCallDepth.Value > 1 the call is recursive, waiting probably generates a deadlock See #35832.
+ if (_isLoadingCallDepth.Value == 1)
{
- await isLoadingValue.TaskCompletionSource.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
+ await isLoadingValue.Task.WaitAsync(cancellationToken).ConfigureAwait(false);
}
+ _isLoadingCallDepth.Value--;
return;
}
@@ -226,7 +232,8 @@ await entry.LoadAsync(
}
finally
{
- isLoadingValue.TaskCompletionSource.TrySetResult();
+ isLoadingValue.TrySetResult();
+ _isLoadingCallDepth.Value--;
lock (_isLoadingLock)
{
_isLoading.Remove(navEntry);
diff --git a/src/EFCore/Infrastructure/ModelValidator.cs b/src/EFCore/Infrastructure/ModelValidator.cs
index 9012bc3a4bc..a6b08037638 100644
--- a/src/EFCore/Infrastructure/ModelValidator.cs
+++ b/src/EFCore/Infrastructure/ModelValidator.cs
@@ -640,12 +640,16 @@ protected virtual void ValidateInheritanceMapping(
/// The entity type to validate.
protected virtual void ValidateDiscriminatorValues(IEntityType rootEntityType)
{
- var derivedTypes = rootEntityType.GetDerivedTypesInclusive().ToList();
+ var derivedTypes = rootEntityType.GetDerivedTypesInclusive();
var discriminatorProperty = rootEntityType.FindDiscriminatorProperty();
if (discriminatorProperty == null)
{
- if (derivedTypes.Count == 1)
+ if (!derivedTypes.Skip(1).Any())
{
+ foreach (var complexProperty in rootEntityType.GetDeclaredComplexProperties())
+ {
+ ValidateDiscriminatorValues(complexProperty.ComplexType);
+ }
return;
}
@@ -654,6 +658,68 @@ protected virtual void ValidateDiscriminatorValues(IEntityType rootEntityType)
}
var discriminatorValues = new Dictionary(discriminatorProperty.GetKeyValueComparer());
+ foreach (var derivedType in derivedTypes)
+ {
+ foreach (var complexProperty in derivedType.GetDeclaredComplexProperties())
+ {
+ ValidateDiscriminatorValues(complexProperty.ComplexType);
+ }
+
+ if (!derivedType.ClrType.IsInstantiable())
+ {
+ continue;
+ }
+
+ var discriminatorValue = derivedType[CoreAnnotationNames.DiscriminatorValue];
+ if (discriminatorValue == null)
+ {
+ throw new InvalidOperationException(
+ CoreStrings.NoDiscriminatorValue(derivedType.DisplayName()));
+ }
+
+ if (!discriminatorProperty.ClrType.IsInstanceOfType(discriminatorValue))
+ {
+ throw new InvalidOperationException(
+ CoreStrings.DiscriminatorValueIncompatible(
+ discriminatorValue, derivedType.DisplayName(), discriminatorProperty.ClrType.DisplayName()));
+ }
+
+ if (discriminatorValues.TryGetValue(discriminatorValue, out var duplicateEntityType))
+ {
+ throw new InvalidOperationException(
+ CoreStrings.DuplicateDiscriminatorValue(
+ derivedType.DisplayName(), discriminatorValue, duplicateEntityType.DisplayName()));
+ }
+
+ discriminatorValues[discriminatorValue] = derivedType;
+ }
+ }
+
+ ///
+ /// Validates the discriminator and values for the given complex type and nested ones.
+ ///
+ /// The entity type to validate.
+ protected virtual void ValidateDiscriminatorValues(IComplexType complexType)
+ {
+ foreach (var complexProperty in complexType.GetComplexProperties())
+ {
+ ValidateDiscriminatorValues(complexProperty.ComplexType);
+ }
+
+ var derivedTypes = complexType.GetDerivedTypesInclusive();
+ var discriminatorProperty = complexType.FindDiscriminatorProperty();
+ if (discriminatorProperty == null)
+ {
+ if (!derivedTypes.Skip(1).Any())
+ {
+ return;
+ }
+
+ throw new InvalidOperationException(
+ CoreStrings.NoDiscriminatorProperty(complexType.DisplayName()));
+ }
+
+ var discriminatorValues = new Dictionary(discriminatorProperty.GetKeyValueComparer());
foreach (var derivedType in derivedTypes)
{
diff --git a/src/EFCore/Metadata/Builders/ComplexPropertyBuilder.cs b/src/EFCore/Metadata/Builders/ComplexPropertyBuilder.cs
index 5bc54a973d2..aa8be6da059 100644
--- a/src/EFCore/Metadata/Builders/ComplexPropertyBuilder.cs
+++ b/src/EFCore/Metadata/Builders/ComplexPropertyBuilder.cs
@@ -168,11 +168,11 @@ public virtual ComplexTypePropertyBuilder Property(string propertyName)
/// If no property with the given name exists, then a new property will be added.
///
///
- /// When adding a new property, if a property with the same name exists in the entity class
- /// then it will be added to the model. If no property exists in the entity class, then
+ /// When adding a new property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
/// a new shadow state property will be added. A shadow state property is one that does not have a
- /// corresponding property in the entity class. The current value for the property is stored in
- /// the rather than being stored in instances of the entity class.
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
///
/// The type of the property to be configured.
/// The name of the property to be configured.
@@ -188,11 +188,11 @@ public virtual ComplexTypePropertyBuilder Property(string
/// If no property with the given name exists, then a new property will be added.
///
///
- /// When adding a new property, if a property with the same name exists in the entity class
- /// then it will be added to the model. If no property exists in the entity class, then
+ /// When adding a new property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
/// a new shadow state property will be added. A shadow state property is one that does not have a
- /// corresponding property in the entity class. The current value for the property is stored in
- /// the rather than being stored in instances of the entity class.
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
///
/// The type of the property to be configured.
/// The name of the property to be configured.
@@ -225,11 +225,11 @@ public virtual ComplexTypePrimitiveCollectionBuilder PrimitiveCollection(string
/// If no property with the given name exists, then a new property will be added.
///
///
- /// When adding a new property, if a property with the same name exists in the entity class
- /// then it will be added to the model. If no property exists in the entity class, then
+ /// When adding a new property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
/// a new shadow state property will be added. A shadow state property is one that does not have a
- /// corresponding property in the entity class. The current value for the property is stored in
- /// the rather than being stored in instances of the entity class.
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
///
/// The type of the property to be configured.
/// The name of the property to be configured.
@@ -245,11 +245,11 @@ public virtual ComplexTypePrimitiveCollectionBuilder PrimitiveCollect
/// If no property with the given name exists, then a new property will be added.
///
///
- /// When adding a new property, if a property with the same name exists in the entity class
- /// then it will be added to the model. If no property exists in the entity class, then
+ /// When adding a new property, if a property with the same name exists in the complex class
+ /// then it will be added to the model. If no property exists in the complex class, then
/// a new shadow state property will be added. A shadow state property is one that does not have a
- /// corresponding property in the entity class. The current value for the property is stored in
- /// the rather than being stored in instances of the entity class.
+ /// corresponding property in the complex class. The current value for the property is stored in
+ /// the rather than being stored in instances of the complex class.
///
/// The type of the property to be configured.
/// The name of the property to be configured.
@@ -265,7 +265,7 @@ public virtual ComplexTypePrimitiveCollectionBuilder PrimitiveCollection(Type pr
/// If no property with the given name exists, then a new property will be added.
///
///
- /// Indexer properties are stored in the entity using
+ /// Indexer properties are stored in the complex type using
/// an indexer
/// supplying the provided property name.
///
@@ -284,7 +284,7 @@ public virtual ComplexTypePropertyBuilder IndexerProperty
/// If no property with the given name exists, then a new property will be added.
///
///
- /// Indexer properties are stored in the entity using
+ /// Indexer properties are stored in the complex type using
/// an indexer
/// supplying the provided property name.
///
@@ -563,8 +563,8 @@ public virtual ComplexPropertyBuilder Ignore(string propertyName)
}
///
- /// Configures the to be used for this entity type.
- /// This strategy indicates how the context detects changes to properties for an instance of the entity type.
+ /// Configures the to be used for this complex type.
+ /// This strategy indicates how the context detects changes to properties for an instance of the complex type.
///
/// The change tracking strategy to be used.
/// The same builder instance so that multiple configuration calls can be chained.
@@ -623,6 +623,53 @@ public virtual ComplexPropertyBuilder UseDefaultPropertyAccessMode(PropertyAcces
return this;
}
+ ///
+ /// Configures the discriminator property used to identify the complex type in the store.
+ ///
+ /// A builder that allows the discriminator property to be configured.
+ public virtual ComplexTypeDiscriminatorBuilder HasDiscriminator()
+ => TypeBuilder.HasDiscriminator(ConfigurationSource.Explicit)!;
+
+ ///
+ /// Configures the discriminator property used to identify the complex type in the store.
+ ///
+ /// The name of the discriminator property.
+ /// The type of values stored in the discriminator property.
+ /// A builder that allows the discriminator property to be configured.
+ public virtual ComplexTypeDiscriminatorBuilder HasDiscriminator(
+ string name,
+ Type type)
+ {
+ Check.NotEmpty(name, nameof(name));
+ Check.NotNull(type, nameof(type));
+
+ return TypeBuilder.HasDiscriminator(name, type, ConfigurationSource.Explicit)!;
+ }
+
+ ///
+ /// Configures the discriminator property used to identify the complex type in the store.
+ ///
+ /// The type of values stored in the discriminator property.
+ /// The name of the discriminator property.
+ /// A builder that allows the discriminator property to be configured.
+ public virtual ComplexTypeDiscriminatorBuilder HasDiscriminator(string name)
+ {
+ Check.NotEmpty(name, nameof(name));
+
+ return new ComplexTypeDiscriminatorBuilder(
+ TypeBuilder.HasDiscriminator(name, typeof(TDiscriminator), ConfigurationSource.Explicit)!);
+ }
+
+ ///
+ /// Configures the complex type as having no discriminator property.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public virtual ComplexPropertyBuilder HasNoDiscriminator()
+ {
+ TypeBuilder.HasNoDiscriminator(ConfigurationSource.Explicit);
+ return this;
+ }
+
#region Hidden System.Object members
///
diff --git a/src/EFCore/Metadata/Builders/ComplexPropertyBuilder`.cs b/src/EFCore/Metadata/Builders/ComplexPropertyBuilder`.cs
index 7997d990656..9a08411918b 100644
--- a/src/EFCore/Metadata/Builders/ComplexPropertyBuilder`.cs
+++ b/src/EFCore/Metadata/Builders/ComplexPropertyBuilder`.cs
@@ -32,7 +32,7 @@ public ComplexPropertyBuilder(IMutableComplexProperty complexProperty)
}
///
- /// Adds or updates an annotation on the entity type. If an annotation with the key specified in
+ /// Adds or updates an annotation on the complex property. If an annotation with the key specified in
/// already exists its value will be updated.
///
/// The key of the annotation to be added or updated.
@@ -42,7 +42,7 @@ public ComplexPropertyBuilder(IMutableComplexProperty complexProperty)
=> (ComplexPropertyBuilder)base.HasPropertyAnnotation(annotation, value);
///
- /// Adds or updates an annotation on the entity type. If an annotation with the key specified in
+ /// Adds or updates an annotation on the complex type. If an annotation with the key specified in
/// already exists its value will be updated.
///
/// The key of the annotation to be added or updated.
@@ -86,7 +86,7 @@ public ComplexPropertyBuilder(IMutableComplexProperty complexProperty)
=> (ComplexPropertyBuilder)base.HasField(fieldName);
///
- /// Returns an object that can be used to configure a property of the entity type.
+ /// Returns an object that can be used to configure a property of the complex type.
/// If the specified property is not already part of the model, it will be added.
///
///
@@ -101,7 +101,7 @@ public virtual ComplexTypePropertyBuilder Property(Express
.Metadata);
///
- /// Returns an object that can be used to configure a primitive collection property of the entity type.
+ /// Returns an object that can be used to configure a primitive collection property of the complex type.
/// If the specified property is not already part of the model, it will be added.
///
///
@@ -332,8 +332,8 @@ public virtual ComplexPropertyBuilder ComplexProperty(
}
///
- /// Excludes the given property from the entity type. This method is typically used to remove properties
- /// or navigations from the entity type that were added by convention.
+ /// Excludes the given property from the complex type. This method is typically used to remove properties
+ /// or navigations from the complex type that were added by convention.
///
///
/// A lambda expression representing the property to be ignored
@@ -344,16 +344,16 @@ public virtual ComplexPropertyBuilder Ignore(Expression
- /// Excludes the given property from the entity type. This method is typically used to remove properties
- /// or navigations from the entity type that were added by convention.
+ /// Excludes the given property from the complex type. This method is typically used to remove properties
+ /// or navigations from the complex type that were added by convention.
///
- /// The name of the property to be removed from the entity type.
+ /// The name of the property to be removed from the complex type.
public new virtual ComplexPropertyBuilder Ignore(string propertyName)
=> (ComplexPropertyBuilder)base.Ignore(propertyName);
///
- /// Configures the to be used for this entity type.
- /// This strategy indicates how the context detects changes to properties for an instance of the entity type.
+ /// Configures the to be used for this complex type.
+ /// This strategy indicates how the context detects changes to properties for an instance of the complex type.
///
/// The change tracking strategy to be used.
/// The same builder instance so that multiple configuration calls can be chained.
@@ -381,22 +381,47 @@ public virtual ComplexPropertyBuilder Ignore(Expression (ComplexPropertyBuilder)base.UsePropertyAccessMode(propertyAccessMode);
///
- /// Sets the to use for all properties of this entity type.
+ /// Sets the to use for all properties of this complex type.
///
///
///
/// By default, the backing field, if one is found by convention or has been specified, is used when
/// new objects are constructed, typically when entities are queried from the database.
/// Properties are used for all other accesses. Calling this method will change that behavior
- /// for all properties of this entity type as described in the enum.
+ /// for all properties of this complex type as described in the enum.
///
///
- /// Calling this method overrides for all properties of this entity type any access mode that was
+ /// Calling this method overrides for all properties of this complex type any access mode that was
/// set on the model.
///
///
- /// The to use for properties of this entity type.
+ /// The to use for properties of this complex type.
/// The same builder instance so that multiple configuration calls can be chained.
public new virtual ComplexPropertyBuilder UseDefaultPropertyAccessMode(PropertyAccessMode propertyAccessMode)
=> (ComplexPropertyBuilder)base.UseDefaultPropertyAccessMode(propertyAccessMode);
+
+ ///
+ /// Configures the discriminator property used to identify the complex type in the store.
+ ///
+ /// The type of values stored in the discriminator property.
+ ///
+ /// A lambda expression representing the property to be used as the discriminator (
+ /// blog => blog.Discriminator).
+ ///
+ /// A builder that allows the discriminator property to be configured.
+ public virtual ComplexTypeDiscriminatorBuilder HasDiscriminator(
+ Expression> propertyExpression)
+ {
+ Check.NotNull(propertyExpression, nameof(propertyExpression));
+
+ return new ComplexTypeDiscriminatorBuilder(
+ TypeBuilder.HasDiscriminator(propertyExpression.GetMemberAccess(), ConfigurationSource.Explicit)!);
+ }
+
+ ///
+ /// Configures the entity type as having no discriminator property.
+ ///
+ /// The same builder instance so that multiple configuration calls can be chained.
+ public new virtual ComplexPropertyBuilder HasNoDiscriminator()
+ => (ComplexPropertyBuilder)base.HasNoDiscriminator();
}
diff --git a/src/EFCore/Metadata/Builders/ComplexTypeDiscriminatorBuilder.cs b/src/EFCore/Metadata/Builders/ComplexTypeDiscriminatorBuilder.cs
new file mode 100644
index 00000000000..3f352a33375
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/ComplexTypeDiscriminatorBuilder.cs
@@ -0,0 +1,133 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.ComponentModel;
+using Microsoft.EntityFrameworkCore.Internal;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+///
+/// Provides a simple API surface for setting discriminator values.
+///
+///
+/// See Modeling entity types and relationships for more information and examples.
+///
+public class ComplexTypeDiscriminatorBuilder : IConventionComplexTypeDiscriminatorBuilder
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ public ComplexTypeDiscriminatorBuilder(IMutableComplexType complexType)
+ => ComplexTypeBuilder = ((ComplexType)complexType).Builder;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ protected virtual InternalComplexTypeBuilder ComplexTypeBuilder { get; }
+
+ ///
+ /// Configures the default discriminator value to use.
+ ///
+ /// The discriminator value.
+ /// The same builder so that multiple calls can be chained.
+ public virtual ComplexTypeDiscriminatorBuilder HasValue(object? value)
+ => HasValue(ComplexTypeBuilder, value, ConfigurationSource.Explicit)!;
+
+ private ComplexTypeDiscriminatorBuilder? HasValue(
+ InternalComplexTypeBuilder? complexTypeBuilder,
+ object? value,
+ ConfigurationSource configurationSource)
+ {
+ if (complexTypeBuilder == null)
+ {
+ return null;
+ }
+
+ var baseComplexTypeBuilder = ComplexTypeBuilder;
+ if (!baseComplexTypeBuilder.Metadata.IsAssignableFrom(complexTypeBuilder.Metadata)
+ && (!baseComplexTypeBuilder.Metadata.ClrType.IsAssignableFrom(complexTypeBuilder.Metadata.ClrType)
+ || complexTypeBuilder.HasBaseType(baseComplexTypeBuilder.Metadata, configurationSource) == null))
+ {
+ throw new InvalidOperationException(
+ CoreStrings.DiscriminatorEntityTypeNotDerived(
+ complexTypeBuilder.Metadata.DisplayName(),
+ baseComplexTypeBuilder.Metadata.DisplayName()));
+ }
+
+ if (configurationSource == ConfigurationSource.Explicit)
+ {
+ ((IMutableComplexType)complexTypeBuilder.Metadata).SetDiscriminatorValue(value);
+ }
+ else
+ {
+ if (!((IConventionComplexTypeDiscriminatorBuilder)this).CanSetValue(
+ value, configurationSource == ConfigurationSource.DataAnnotation))
+ {
+ return null;
+ }
+
+ ((IConventionComplexType)complexTypeBuilder.Metadata)
+ .SetDiscriminatorValue(value, configurationSource == ConfigurationSource.DataAnnotation);
+ }
+
+ return this;
+ }
+
+ ///
+ IConventionComplexType IConventionComplexTypeDiscriminatorBuilder.ComplexType
+ {
+ [DebuggerStepThrough]
+ get => ComplexTypeBuilder.Metadata;
+ }
+
+ ///
+ [DebuggerStepThrough]
+ IConventionComplexTypeDiscriminatorBuilder? IConventionComplexTypeDiscriminatorBuilder.HasValue(object? value, bool fromDataAnnotation)
+ => HasValue(
+ ComplexTypeBuilder, value,
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ bool IConventionComplexTypeDiscriminatorBuilder.CanSetValue(object? value, bool fromDataAnnotation)
+ => ((IConventionComplexTypeBuilder)ComplexTypeBuilder).CanSetAnnotation(CoreAnnotationNames.DiscriminatorValue, value, fromDataAnnotation);
+
+ #region Hidden System.Object members
+
+ ///
+ /// Returns a string that represents the current object.
+ ///
+ /// A string that represents the current object.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public override string? ToString()
+ => base.ToString();
+
+ ///
+ /// Determines whether the specified object is equal to the current object.
+ ///
+ /// The object to compare with the current object.
+ /// if the specified object is equal to the current object; otherwise, .
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ // ReSharper disable once BaseObjectEqualsIsObjectEquals
+ public override bool Equals(object? obj)
+ => base.Equals(obj);
+
+ ///
+ /// Serves as the default hash function.
+ ///
+ /// A hash code for the current object.
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ // ReSharper disable once BaseObjectGetHashCodeCallInGetHashCode
+ public override int GetHashCode()
+ => base.GetHashCode();
+
+ #endregion
+}
diff --git a/src/EFCore/Metadata/Builders/ComplexTypeDiscriminatorBuilder`.cs b/src/EFCore/Metadata/Builders/ComplexTypeDiscriminatorBuilder`.cs
new file mode 100644
index 00000000000..f5d1fd95ac3
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/ComplexTypeDiscriminatorBuilder`.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+///
+/// Provides a simple API surface for setting discriminator values.
+///
+///
+/// See Modeling entity types and relationships for more information and examples.
+///
+/// The type of the discriminator property.
+public class ComplexTypeDiscriminatorBuilder
+{
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [EntityFrameworkInternal]
+ public ComplexTypeDiscriminatorBuilder(ComplexTypeDiscriminatorBuilder builder)
+ => Builder = builder;
+
+ private ComplexTypeDiscriminatorBuilder Builder { get; }
+
+ ///
+ /// Configures the default discriminator value to use.
+ ///
+ /// The discriminator value.
+ /// The same builder so that multiple calls can be chained.
+ public virtual ComplexTypeDiscriminatorBuilder HasValue(TDiscriminator value)
+ => new(Builder.HasValue(value));
+}
diff --git a/src/EFCore/Metadata/Builders/EntityTypeBuilder`.cs b/src/EFCore/Metadata/Builders/EntityTypeBuilder`.cs
index 3cee16b13e4..b84fb98a562 100644
--- a/src/EFCore/Metadata/Builders/EntityTypeBuilder`.cs
+++ b/src/EFCore/Metadata/Builders/EntityTypeBuilder`.cs
@@ -425,21 +425,6 @@ public virtual EntityTypeBuilder Ignore(Expression HasQueryFilter(Expression>? filter)
=> (EntityTypeBuilder)base.HasQueryFilter(filter);
- ///
- /// Configures a query used to provide data for a keyless entity type.
- ///
- /// The query that will provide the underlying data for the keyless entity type.
- /// The same builder instance so that multiple calls can be chained.
- [Obsolete("Use InMemoryEntityTypeBuilderExtensions.ToInMemoryQuery")]
- public virtual EntityTypeBuilder ToQuery(Expression>> query)
- {
- Check.NotNull(query, nameof(query));
-
- Builder.HasDefiningQuery(query, ConfigurationSource.Explicit);
-
- return this;
- }
-
///
/// Configures an unnamed index on the specified properties.
/// If there is an existing index on the given list of properties,
diff --git a/src/EFCore/Metadata/Builders/IConventionComplexTypeBuilder.cs b/src/EFCore/Metadata/Builders/IConventionComplexTypeBuilder.cs
index f2558141ad4..eebef69dc3e 100644
--- a/src/EFCore/Metadata/Builders/IConventionComplexTypeBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionComplexTypeBuilder.cs
@@ -134,4 +134,61 @@ public interface IConventionComplexTypeBuilder : IConventionTypeBaseBuilder
new IConventionComplexTypeBuilder? UsePropertyAccessMode(
PropertyAccessMode? propertyAccessMode,
bool fromDataAnnotation = false);
+
+ ///
+ /// Configures the discriminator property used to identify which complex type each row in a table represents
+ /// when an inheritance hierarchy is mapped to a single table in a relational database.
+ ///
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// A builder that allows the discriminator property to be configured.
+ IConventionComplexTypeDiscriminatorBuilder? HasDiscriminator(bool fromDataAnnotation = false);
+
+ ///
+ /// Configures the discriminator property used to identify which complex type each row in a table represents
+ /// when an inheritance hierarchy is mapped to a single table in a relational database.
+ ///
+ /// The type of values stored in the discriminator property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// A builder that allows the discriminator property to be configured.
+ IConventionComplexTypeDiscriminatorBuilder? HasDiscriminator(Type type, bool fromDataAnnotation = false);
+
+ ///
+ /// Configures the discriminator property used to identify which complex type each row in a table represents
+ /// when an inheritance hierarchy is mapped to a single table in a relational database.
+ ///
+ /// The name of the discriminator property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// A builder that allows the discriminator property to be configured.
+ IConventionComplexTypeDiscriminatorBuilder? HasDiscriminator(string name, bool fromDataAnnotation = false);
+
+ ///
+ /// Configures the discriminator property used to identify which complex type each row in a table represents
+ /// when an inheritance hierarchy is mapped to a single table in a relational database.
+ ///
+ /// The name of the discriminator property.
+ /// The type of values stored in the discriminator property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// A builder that allows the discriminator property to be configured.
+ IConventionComplexTypeDiscriminatorBuilder? HasDiscriminator(string name, Type type, bool fromDataAnnotation = false);
+
+ ///
+ /// Configures the discriminator property used to identify which complex type each row in a table represents
+ /// when an inheritance hierarchy is mapped to a single table in a relational database.
+ ///
+ /// The property mapped to the discriminator property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// A builder that allows the discriminator property to be configured.
+ IConventionComplexTypeDiscriminatorBuilder? HasDiscriminator(MemberInfo memberInfo, bool fromDataAnnotation = false);
+
+ ///
+ /// Removes the discriminator property from this complex type.
+ /// This method is usually called when the complex type is no longer mapped to the same table as any other type in
+ /// the hierarchy or when this complex type is no longer the root type.
+ ///
+ /// Indicates whether the configuration was specified using a data annotation.
+ ///
+ /// The same builder instance if the discriminator was configured,
+ /// otherwise.
+ ///
+ IConventionComplexTypeBuilder? HasNoDiscriminator(bool fromDataAnnotation = false);
}
diff --git a/src/EFCore/Metadata/Builders/IConventionComplexTypeDiscriminatorBuilder.cs b/src/EFCore/Metadata/Builders/IConventionComplexTypeDiscriminatorBuilder.cs
new file mode 100644
index 00000000000..84f3309c4a5
--- /dev/null
+++ b/src/EFCore/Metadata/Builders/IConventionComplexTypeDiscriminatorBuilder.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+///
+/// Provides a simple API surface for setting discriminator values from conventions.
+///
+///
+/// See Model building conventions for more information and examples.
+///
+public interface IConventionComplexTypeDiscriminatorBuilder
+{
+ ///
+ /// Gets the complex type on which the discriminator is being configured.
+ ///
+ IConventionComplexType ComplexType { get; }
+
+ ///
+ /// Configures the discriminator value to use.
+ ///
+ /// The discriminator value.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// The same builder so that multiple calls can be chained.
+ IConventionComplexTypeDiscriminatorBuilder? HasValue(object? value, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the discriminator value can be set from this configuration source.
+ ///
+ /// The discriminator value.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the discriminator value can be set from this configuration source.
+ bool CanSetValue(object? value, bool fromDataAnnotation = false);
+}
diff --git a/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs b/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs
index 97310be44a7..e7014919485 100644
--- a/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs
@@ -878,26 +878,6 @@ bool CanHaveTrigger(
/// if the given query filter can be set.
bool CanSetQueryFilter(LambdaExpression? filter, bool fromDataAnnotation = false);
- ///
- /// Configures a query used to provide data for a keyless entity type.
- ///
- /// The query that will provide the underlying data for the keyless entity type.
- /// Indicates whether the configuration was specified using a data annotation.
- ///
- /// The same builder instance if the query was set, otherwise.
- ///
- [Obsolete("Use InMemoryEntityTypeBuilderExtensions.ToInMemoryQuery")]
- IConventionEntityTypeBuilder? HasDefiningQuery(LambdaExpression? query, bool fromDataAnnotation = false);
-
- ///
- /// Returns a value indicating whether the given defining query can be set from the current configuration source.
- ///
- /// The query that will provide the underlying data for the keyless entity type.
- /// Indicates whether the configuration was specified using a data annotation.
- /// if the given defining query can be set.
- [Obsolete("Use InMemoryEntityTypeBuilderExtensions.CanSetInMemoryQuery")]
- bool CanSetDefiningQuery(LambdaExpression? query, bool fromDataAnnotation = false);
-
///
/// Configures the to be used for this entity type.
/// This strategy indicates how the context detects changes to properties for an instance of the entity type.
@@ -989,46 +969,6 @@ bool CanHaveTrigger(
///
IConventionEntityTypeBuilder? HasNoDiscriminator(bool fromDataAnnotation = false);
- ///
- /// Returns a value indicating whether the discriminator property can be configured.
- ///
- /// The name of the discriminator property.
- /// Indicates whether the configuration was specified using a data annotation.
- /// if the configuration can be applied.
- bool CanSetDiscriminator(string name, bool fromDataAnnotation = false);
-
- ///
- /// Returns a value indicating whether the discriminator property can be configured.
- ///
- /// The type of values stored in the discriminator property.
- /// Indicates whether the configuration was specified using a data annotation.
- /// if the configuration can be applied.
- bool CanSetDiscriminator(Type type, bool fromDataAnnotation = false);
-
- ///
- /// Returns a value indicating whether the discriminator property can be configured.
- ///
- /// The type of values stored in the discriminator property.
- /// The name of the discriminator property.
- /// Indicates whether the configuration was specified using a data annotation.
- /// if the configuration can be applied.
- bool CanSetDiscriminator(string name, Type type, bool fromDataAnnotation = false);
-
- ///
- /// Returns a value indicating whether the discriminator property can be configured.
- ///
- /// The property mapped to the discriminator property.
- /// Indicates whether the configuration was specified using a data annotation.
- /// if the configuration can be applied.
- bool CanSetDiscriminator(MemberInfo memberInfo, bool fromDataAnnotation = false);
-
- ///
- /// Returns a value indicating whether the discriminator property can be removed.
- ///
- /// Indicates whether the configuration was specified using a data annotation.
- /// if the discriminator property can be removed.
- bool CanRemoveDiscriminator(bool fromDataAnnotation = false);
-
///
/// Gets or creates a builder for the target of a potential navigation.
///
diff --git a/src/EFCore/Metadata/Builders/IConventionTypeBaseBuilder.cs b/src/EFCore/Metadata/Builders/IConventionTypeBaseBuilder.cs
index 26ce5a70949..93975fbc427 100644
--- a/src/EFCore/Metadata/Builders/IConventionTypeBaseBuilder.cs
+++ b/src/EFCore/Metadata/Builders/IConventionTypeBaseBuilder.cs
@@ -399,4 +399,44 @@ bool CanHaveComplexIndexerProperty(
/// Indicates whether the configuration was specified using a data annotation.
/// if the given can be set.
bool CanSetPropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the discriminator property can be configured.
+ ///
+ /// The name of the discriminator property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the configuration can be applied.
+ bool CanSetDiscriminator(string name, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the discriminator property can be configured.
+ ///
+ /// The type of values stored in the discriminator property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the configuration can be applied.
+ bool CanSetDiscriminator(Type type, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the discriminator property can be configured.
+ ///
+ /// The type of values stored in the discriminator property.
+ /// The name of the discriminator property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the configuration can be applied.
+ bool CanSetDiscriminator(string name, Type type, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the discriminator property can be configured.
+ ///
+ /// The property mapped to the discriminator property.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the configuration can be applied.
+ bool CanSetDiscriminator(MemberInfo memberInfo, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns a value indicating whether the discriminator property can be removed.
+ ///
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// if the discriminator property can be removed.
+ bool CanRemoveDiscriminator(bool fromDataAnnotation = false);
}
diff --git a/src/EFCore/Metadata/Conventions/DiscriminatorConvention.cs b/src/EFCore/Metadata/Conventions/DiscriminatorConvention.cs
index 67b68bbfdb0..4d93d6cfe7f 100644
--- a/src/EFCore/Metadata/Conventions/DiscriminatorConvention.cs
+++ b/src/EFCore/Metadata/Conventions/DiscriminatorConvention.cs
@@ -75,7 +75,7 @@ public virtual void ProcessEntityTypeBaseTypeChanged(
///
public virtual void ProcessDiscriminatorPropertySet(
- IConventionEntityTypeBuilder entityTypeBuilder,
+ IConventionTypeBaseBuilder structuralTypeBuilder,
string? name,
IConventionContext context)
{
@@ -84,10 +84,22 @@ public virtual void ProcessDiscriminatorPropertySet(
return;
}
- var discriminator = entityTypeBuilder.HasDiscriminator(name, typeof(string));
- if (discriminator != null)
+ if (structuralTypeBuilder is IConventionEntityTypeBuilder entityTypeBuilder)
{
- SetDefaultDiscriminatorValues(entityTypeBuilder.Metadata.GetDerivedTypesInclusive(), discriminator);
+ var discriminator = entityTypeBuilder.HasDiscriminator(name, typeof(string));
+ if (discriminator != null)
+ {
+ SetDefaultDiscriminatorValues(entityTypeBuilder.Metadata.GetDerivedTypesInclusive(), discriminator);
+ }
+ }
+ else
+ {
+ var complexTypeBuilder = (IConventionComplexTypeBuilder)structuralTypeBuilder;
+ var discriminator = complexTypeBuilder.HasDiscriminator(name, typeof(string));
+ if (discriminator != null)
+ {
+ SetDefaultDiscriminatorValue(complexTypeBuilder.Metadata, discriminator);
+ }
}
}
@@ -121,4 +133,16 @@ protected virtual void SetDefaultDiscriminatorValues(
discriminatorBuilder.HasValue(entityType, entityType.GetDefaultDiscriminatorValue());
}
}
+
+ ///
+ /// Configures the discriminator value for the given complex type.
+ ///
+ /// The complex type to configure.
+ /// The discriminator builder.
+ protected virtual void SetDefaultDiscriminatorValue(
+ IConventionComplexType complexType,
+ IConventionComplexTypeDiscriminatorBuilder discriminatorBuilder)
+ {
+ discriminatorBuilder.HasValue(complexType.GetDefaultDiscriminatorValue());
+ }
}
diff --git a/src/EFCore/Metadata/Conventions/IDiscriminatorPropertySetConvention.cs b/src/EFCore/Metadata/Conventions/IDiscriminatorPropertySetConvention.cs
index 85e3bf295d1..63ca3b4847b 100644
--- a/src/EFCore/Metadata/Conventions/IDiscriminatorPropertySetConvention.cs
+++ b/src/EFCore/Metadata/Conventions/IDiscriminatorPropertySetConvention.cs
@@ -4,7 +4,7 @@
namespace Microsoft.EntityFrameworkCore.Metadata.Conventions;
///
-/// Represents an operation that should be performed when a discriminator property is set for an entity type.
+/// Represents an operation that should be performed when a discriminator property is set for a type.
///
///
/// See Model building conventions for more information and examples.
@@ -14,11 +14,11 @@ public interface IDiscriminatorPropertySetConvention : IConvention
///
/// Called after a discriminator property is set.
///
- /// The builder for the entity type.
+ /// The builder for the type.
/// The name of the discriminator property.
/// Additional information associated with convention execution.
void ProcessDiscriminatorPropertySet(
- IConventionEntityTypeBuilder entityTypeBuilder,
+ IConventionTypeBaseBuilder structuralTypeBuilder,
string? name,
IConventionContext context);
}
diff --git a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ConventionScope.cs b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ConventionScope.cs
index 6ccb4600a06..5a179129c29 100644
--- a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ConventionScope.cs
+++ b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ConventionScope.cs
@@ -70,7 +70,7 @@ public int GetLeafCount()
string name);
public abstract string? OnDiscriminatorPropertySet(
- IConventionEntityTypeBuilder entityTypeBuilder,
+ IConventionTypeBaseBuilder structuralTypeBuilder,
string? name);
public abstract IConventionKey? OnEntityTypePrimaryKeyChanged(
diff --git a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.DelayedConventionScope.cs b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.DelayedConventionScope.cs
index e5195facd77..1a91165384b 100644
--- a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.DelayedConventionScope.cs
+++ b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.DelayedConventionScope.cs
@@ -80,9 +80,9 @@ public override string OnEntityTypeMemberIgnored(IConventionEntityTypeBuilder en
return name;
}
- public override string? OnDiscriminatorPropertySet(IConventionEntityTypeBuilder entityTypeBuilder, string? name)
+ public override string? OnDiscriminatorPropertySet(IConventionTypeBaseBuilder structuralTypeBuilder, string? name)
{
- Add(new OnDiscriminatorPropertySetNode(entityTypeBuilder, name));
+ Add(new OnDiscriminatorPropertySetNode(structuralTypeBuilder, name));
return name;
}
@@ -523,13 +523,13 @@ public override void Run(ConventionDispatcher dispatcher)
=> dispatcher._immediateConventionScope.OnEntityTypeMemberIgnored(EntityTypeBuilder, Name);
}
- private sealed class OnDiscriminatorPropertySetNode(IConventionEntityTypeBuilder entityTypeBuilder, string? name) : ConventionNode
+ private sealed class OnDiscriminatorPropertySetNode(IConventionTypeBaseBuilder structuralTypeBuilder, string? name) : ConventionNode
{
- public IConventionEntityTypeBuilder EntityTypeBuilder { get; } = entityTypeBuilder;
+ public IConventionTypeBaseBuilder StructuralTypeBuilder { get; } = structuralTypeBuilder;
public string? Name { get; } = name;
public override void Run(ConventionDispatcher dispatcher)
- => dispatcher._immediateConventionScope.OnDiscriminatorPropertySet(EntityTypeBuilder, Name);
+ => dispatcher._immediateConventionScope.OnDiscriminatorPropertySet(StructuralTypeBuilder, Name);
}
private sealed class OnEntityTypeBaseTypeChangedNode(
diff --git a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs
index ca81bc4da76..aefed1db3d9 100644
--- a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs
+++ b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs
@@ -256,15 +256,15 @@ public IConventionModelBuilder OnModelInitialized(IConventionModelBuilder modelB
return !entityTypeBuilder.Metadata.IsIgnored(name) ? null : name;
}
- public override string? OnDiscriminatorPropertySet(IConventionEntityTypeBuilder entityTypeBuilder, string? name)
+ public override string? OnDiscriminatorPropertySet(IConventionTypeBaseBuilder structuralTypeBuilder, string? name)
{
- if (!entityTypeBuilder.Metadata.IsInModel)
+ if (!structuralTypeBuilder.Metadata.IsInModel)
{
return null;
}
#if DEBUG
- var initialValue = entityTypeBuilder.Metadata.GetDiscriminatorPropertyName();
+ var initialValue = structuralTypeBuilder.Metadata.GetDiscriminatorPropertyName();
#endif
using (dispatcher.DelayConventions())
{
@@ -273,20 +273,20 @@ public IConventionModelBuilder OnModelInitialized(IConventionModelBuilder modelB
foreach (var entityTypeConvention in conventionSet.DiscriminatorPropertySetConventions)
{
entityTypeConvention.ProcessDiscriminatorPropertySet(
- entityTypeBuilder, name, _nullableStringConventionContext);
+ structuralTypeBuilder, name, _nullableStringConventionContext);
if (_nullableStringConventionContext.ShouldStopProcessing())
{
return _nullableStringConventionContext.Result;
}
#if DEBUG
Check.DebugAssert(
- initialValue == entityTypeBuilder.Metadata.GetDiscriminatorPropertyName(),
+ initialValue == structuralTypeBuilder.Metadata.GetDiscriminatorPropertyName(),
$"Convention {entityTypeConvention.GetType().Name} changed value without terminating");
#endif
}
}
- return entityTypeBuilder.Metadata.GetDiscriminatorPropertyName();
+ return structuralTypeBuilder.Metadata.GetDiscriminatorPropertyName();
}
public override IConventionEntityType? OnEntityTypeBaseTypeChanged(
diff --git a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs
index a223d479e2c..7fad1e7d49c 100644
--- a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs
+++ b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs
@@ -136,9 +136,9 @@ public virtual IConventionModelBuilder OnModelFinalizing(IConventionModelBuilder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual string? OnDiscriminatorPropertySet(
- IConventionEntityTypeBuilder entityTypeBuilder,
+ IConventionTypeBaseBuilder structuralTypeBuilder,
string? name)
- => _scope.OnDiscriminatorPropertySet(entityTypeBuilder, name);
+ => _scope.OnDiscriminatorPropertySet(structuralTypeBuilder, name);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs b/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs
index 3e4c032235b..e3684e0b68f 100644
--- a/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs
+++ b/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs
@@ -245,10 +245,10 @@ private static RuntimeEntityType Create(IEntityType entityType, RuntimeModel mod
entityType.ClrType,
entityType.BaseType == null ? null : model.FindEntityType(entityType.BaseType.Name)!,
entityType.HasSharedClrType,
- entityType.GetDiscriminatorPropertyName(),
entityType.GetChangeTrackingStrategy(),
entityType.FindIndexerPropertyInfo(),
entityType.IsPropertyBag,
+ entityType.GetDiscriminatorPropertyName(),
entityType.GetDiscriminatorValue(),
derivedTypesCount: entityType.GetDirectlyDerivedTypes().Count(),
propertyCount: entityType.GetDeclaredProperties().Count(),
@@ -293,9 +293,6 @@ protected virtual void ProcessEntityTypeAnnotations(
{
if (CoreAnnotationNames.AllNames.Contains(key)
&& key != CoreAnnotationNames.QueryFilter
-#pragma warning disable CS0612 // Type or member is obsolete
- && key != CoreAnnotationNames.DefiningQuery
-#pragma warning restore CS0612 // Type or member is obsolete
&& key != CoreAnnotationNames.DiscriminatorMappingComplete)
{
annotations.Remove(key);
@@ -307,14 +304,6 @@ protected virtual void ProcessEntityTypeAnnotations(
annotations[CoreAnnotationNames.QueryFilter] =
new QueryRootRewritingExpressionVisitor(runtimeEntityType.Model).Rewrite((Expression)queryFilter!);
}
-
-#pragma warning disable CS0612 // Type or member is obsolete
- if (annotations.TryGetValue(CoreAnnotationNames.DefiningQuery, out var definingQuery))
- {
- annotations[CoreAnnotationNames.DefiningQuery] =
-#pragma warning restore CS0612 // Type or member is obsolete
- new QueryRootRewritingExpressionVisitor(runtimeEntityType.Model).Rewrite((Expression)definingQuery!);
- }
}
}
@@ -510,23 +499,25 @@ protected virtual void ProcessServicePropertyAnnotations(
private RuntimeComplexProperty Create(IComplexProperty complexProperty, RuntimeTypeBase runtimeStructuralType)
{
+ var complexType = complexProperty.ComplexType;
var runtimeComplexProperty = runtimeStructuralType.AddComplexProperty(
complexProperty.Name,
complexProperty.ClrType,
- complexProperty.ComplexType.Name,
- complexProperty.ComplexType.ClrType,
+ complexType.Name,
+ complexType.ClrType,
complexProperty.PropertyInfo,
complexProperty.FieldInfo,
complexProperty.GetPropertyAccessMode(),
complexProperty.IsNullable,
complexProperty.IsCollection,
- complexProperty.ComplexType.GetChangeTrackingStrategy(),
- complexProperty.ComplexType.FindIndexerPropertyInfo(),
- complexProperty.ComplexType.IsPropertyBag,
- propertyCount: complexProperty.ComplexType.GetDeclaredProperties().Count(),
- complexPropertyCount: complexProperty.ComplexType.GetDeclaredComplexProperties().Count());
+ complexType.GetChangeTrackingStrategy(),
+ complexType.FindIndexerPropertyInfo(),
+ complexType.IsPropertyBag,
+ complexType.GetDiscriminatorPropertyName(),
+ complexType.GetDiscriminatorValue(),
+ propertyCount: complexType.GetDeclaredProperties().Count(),
+ complexPropertyCount: complexType.GetDeclaredComplexProperties().Count());
- var complexType = complexProperty.ComplexType;
var runtimeComplexType = runtimeComplexProperty.ComplexType;
foreach (var property in complexType.GetProperties())
diff --git a/src/EFCore/Metadata/IComplexType.cs b/src/EFCore/Metadata/IComplexType.cs
index cc26dde9b0e..2afdce621e1 100644
--- a/src/EFCore/Metadata/IComplexType.cs
+++ b/src/EFCore/Metadata/IComplexType.cs
@@ -16,6 +16,32 @@ public interface IComplexType : IReadOnlyComplexType, ITypeBase
///
new IComplexProperty ComplexProperty { get; }
+ ///
+ /// Gets the base type of this complex type. Returns if this is not a derived type in an inheritance
+ /// hierarchy.
+ ///
+ new IComplexType? BaseType { get; }
+
+ ///
+ /// Gets all types in the model that derive from this complex type.
+ ///
+ /// The derived types.
+ new IEnumerable GetDerivedTypes()
+ => ((IReadOnlyTypeBase)this).GetDerivedTypes().Cast();
+
+ ///
+ /// Returns all derived types of this complex type, including the type itself.
+ ///
+ /// Derived types.
+ new IEnumerable GetDerivedTypesInclusive()
+ => ((IReadOnlyTypeBase)this).GetDerivedTypesInclusive().Cast();
+
+ ///
+ /// Gets all types in the model that directly derive from this complex type.
+ ///
+ /// The derived types.
+ new IEnumerable GetDirectlyDerivedTypes();
+
///
/// Gets the entity type on which the complex property chain is declared.
///
diff --git a/src/EFCore/Metadata/IConventionComplexType.cs b/src/EFCore/Metadata/IConventionComplexType.cs
index bd2dcb09a38..7745994f11a 100644
--- a/src/EFCore/Metadata/IConventionComplexType.cs
+++ b/src/EFCore/Metadata/IConventionComplexType.cs
@@ -27,4 +27,39 @@ public interface IConventionComplexType : IReadOnlyComplexType, IConventionTypeB
/// Gets the associated property.
///
new IConventionComplexProperty ComplexProperty { get; }
+
+ ///
+ /// Gets the base type of this type. Returns if this is not a derived type in an inheritance hierarchy.
+ ///
+ new IConventionComplexType? BaseType { get; }
+
+ ///
+ /// Gets the root base type for a given type.
+ ///
+ ///
+ /// The root base type. If the given type is not a derived type, then the same type is returned.
+ ///
+ new IConventionComplexType GetRootType()
+ => (IConventionComplexType)((IReadOnlyTypeBase)this).GetRootType();
+
+ ///
+ /// Gets all types in the model that derive from this type.
+ ///
+ /// The derived types.
+ new IEnumerable GetDerivedTypes()
+ => ((IReadOnlyTypeBase)this).GetDerivedTypes().Cast();
+
+ ///
+ /// Returns all derived types of this type, including the type itself.
+ ///
+ /// Derived types.
+ new IEnumerable GetDerivedTypesInclusive()
+ => ((IReadOnlyTypeBase)this).GetDerivedTypesInclusive().Cast();
+
+ ///
+ /// Gets all types in the model that directly derive from this type.
+ ///
+ /// The derived types.
+ new IEnumerable GetDirectlyDerivedTypes()
+ => ((IReadOnlyTypeBase)this).GetDirectlyDerivedTypes().Cast();
}
diff --git a/src/EFCore/Metadata/IConventionEntityType.cs b/src/EFCore/Metadata/IConventionEntityType.cs
index 0e56d116b9a..254311839f9 100644
--- a/src/EFCore/Metadata/IConventionEntityType.cs
+++ b/src/EFCore/Metadata/IConventionEntityType.cs
@@ -56,27 +56,6 @@ public interface IConventionEntityType : IReadOnlyEntityType, IConventionTypeBas
/// The configuration source for .
ConfigurationSource? GetQueryFilterConfigurationSource();
- ///
- /// Returns the property that will be used for storing a discriminator value.
- ///
- /// The property that will be used for storing a discriminator value.
- new IConventionProperty? FindDiscriminatorProperty()
- => (IConventionProperty?)((IReadOnlyEntityType)this).FindDiscriminatorProperty();
-
- ///
- /// Sets the that will be used for storing a discriminator value.
- ///
- /// The property to set.
- /// Indicates whether the configuration was specified using a data annotation.
- /// The discriminator property.
- IConventionProperty? SetDiscriminatorProperty(IReadOnlyProperty? property, bool fromDataAnnotation = false);
-
- ///
- /// Gets the for the discriminator property.
- ///
- /// The or if no discriminator property has been set.
- ConfigurationSource? GetDiscriminatorPropertyConfigurationSource();
-
///
/// Sets the value indicating whether the discriminator mapping is complete.
///
@@ -93,31 +72,6 @@ public interface IConventionEntityType : IReadOnlyEntityType, IConventionTypeBas
ConfigurationSource? GetDiscriminatorMappingCompleteConfigurationSource()
=> FindAnnotation(CoreAnnotationNames.DiscriminatorMappingComplete)?.GetConfigurationSource();
- ///
- /// Sets the discriminator value for this entity type.
- ///
- /// The value to set.
- /// Indicates whether the configuration was specified using a data annotation.
- /// The configured value.
- object? SetDiscriminatorValue(object? value, bool fromDataAnnotation = false)
- => SetAnnotation(CoreAnnotationNames.DiscriminatorValue, value, fromDataAnnotation)
- ?.Value;
-
- ///
- /// Removes the discriminator value for this entity type.
- ///
- /// The removed discriminator value.
- object? RemoveDiscriminatorValue()
- => RemoveAnnotation(CoreAnnotationNames.DiscriminatorValue)?.Value;
-
- ///
- /// Gets the for the discriminator value.
- ///
- /// The or if no discriminator value has been set.
- ConfigurationSource? GetDiscriminatorValueConfigurationSource()
- => FindAnnotation(CoreAnnotationNames.DiscriminatorValue)
- ?.GetConfigurationSource();
-
///
/// Sets the base type of this entity type. Returns if this is not a derived type in an inheritance hierarchy.
///
@@ -126,12 +80,6 @@ public interface IConventionEntityType : IReadOnlyEntityType, IConventionTypeBas
/// The new base type.
IConventionEntityType? SetBaseType(IConventionEntityType? entityType, bool fromDataAnnotation = false);
- ///
- /// Returns the configuration source for the property.
- ///
- /// The configuration source for the property.
- ConfigurationSource? GetBaseTypeConfigurationSource();
-
///
/// Gets all types in the model from which this entity type derives, starting with the root.
///
diff --git a/src/EFCore/Metadata/IConventionModel.cs b/src/EFCore/Metadata/IConventionModel.cs
index c31023226d8..6a149ee47c3 100644
--- a/src/EFCore/Metadata/IConventionModel.cs
+++ b/src/EFCore/Metadata/IConventionModel.cs
@@ -133,6 +133,7 @@ public interface IConventionModel : IReadOnlyModel, IConventionAnnotatable
/// The defining entity type.
/// Indicates whether the configuration was specified using a data annotation.
/// The new entity type.
+ [Obsolete]
IConventionEntityType? AddEntityType(
string name,
string definingNavigationName,
@@ -147,6 +148,7 @@ public interface IConventionModel : IReadOnlyModel, IConventionAnnotatable
/// The defining entity type.
/// Indicates whether the configuration was specified using a data annotation.
/// The new entity type.
+ [Obsolete]
IConventionEntityType? AddEntityType(
[DynamicallyAccessedMembers(IEntityType.DynamicallyAccessedMemberTypes)] Type type,
string definingNavigationName,
diff --git a/src/EFCore/Metadata/IConventionTypeBase.cs b/src/EFCore/Metadata/IConventionTypeBase.cs
index 01904e20875..2e20cb235ee 100644
--- a/src/EFCore/Metadata/IConventionTypeBase.cs
+++ b/src/EFCore/Metadata/IConventionTypeBase.cs
@@ -37,6 +37,101 @@ public interface IConventionTypeBase : IReadOnlyTypeBase, IConventionAnnotatable
new IConventionEntityType ContainingEntityType
=> (IConventionEntityType)this;
+ ///
+ /// Gets the base type of this type. Returns if this is not a derived type in an inheritance hierarchy.
+ ///
+ new IConventionTypeBase? BaseType { get; }
+
+ ///
+ /// Gets the root base type for a given type.
+ ///
+ ///
+ /// The root base type. If the given type is not a derived type, then the same type is returned.
+ ///
+ new IConventionTypeBase GetRootType()
+ => (IConventionTypeBase)((IReadOnlyTypeBase)this).GetRootType();
+
+ ///
+ /// Gets all types in the model that derive from this type.
+ ///
+ /// The derived types.
+ new IEnumerable GetDerivedTypes()
+ => ((IReadOnlyTypeBase)this).GetDerivedTypes().Cast();
+
+ ///
+ /// Returns all derived types of this type, including the type itself.
+ ///
+ /// Derived types.
+ new IEnumerable GetDerivedTypesInclusive()
+ => ((IReadOnlyTypeBase)this).GetDerivedTypesInclusive().Cast();
+
+ ///
+ /// Gets all types in the model that directly derive from this type.
+ ///
+ /// The derived types.
+ new IEnumerable GetDirectlyDerivedTypes()
+ => ((IReadOnlyTypeBase)this).GetDirectlyDerivedTypes().Cast();
+
+ ///
+ /// Returns the property that will be used for storing a discriminator value.
+ ///
+ /// The property that will be used for storing a discriminator value.
+ new IConventionProperty? FindDiscriminatorProperty()
+ => (IConventionProperty?)((IReadOnlyEntityType)this).FindDiscriminatorProperty();
+
+ ///
+ /// Sets the that will be used for storing a discriminator value.
+ ///
+ /// The property to set.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// The discriminator property.
+ IConventionProperty? SetDiscriminatorProperty(IReadOnlyProperty? property, bool fromDataAnnotation = false);
+
+ ///
+ /// Gets the for the discriminator property.
+ ///
+ /// The or if no discriminator property has been set.
+ ConfigurationSource? GetDiscriminatorPropertyConfigurationSource();
+
+ ///
+ /// Sets the discriminator value for this type.
+ ///
+ /// The value to set.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// The configured value.
+ object? SetDiscriminatorValue(object? value, bool fromDataAnnotation = false)
+ => SetAnnotation(CoreAnnotationNames.DiscriminatorValue, value, fromDataAnnotation)
+ ?.Value;
+
+ ///
+ /// Removes the discriminator value for this type.
+ ///
+ /// The removed discriminator value.
+ object? RemoveDiscriminatorValue()
+ => RemoveAnnotation(CoreAnnotationNames.DiscriminatorValue)?.Value;
+
+ ///
+ /// Gets the for the discriminator value.
+ ///
+ /// The or if no discriminator value has been set.
+ ConfigurationSource? GetDiscriminatorValueConfigurationSource()
+ => FindAnnotation(CoreAnnotationNames.DiscriminatorValue)
+ ?.GetConfigurationSource();
+
+ ///
+ /// Sets the base type of this type. Returns if this is not a derived type in an inheritance hierarchy.
+ ///
+ /// The base type.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// The new base type.
+ IConventionTypeBase? SetBaseType(IConventionTypeBase? structuralType, bool fromDataAnnotation = false);
+
+ ///
+ /// Returns the configuration source for the property.
+ ///
+ /// The configuration source for the property.
+ ConfigurationSource? GetBaseTypeConfigurationSource();
+
///
/// Marks the given member name as ignored, preventing conventions from adding a matching property
/// or navigation to the type.
diff --git a/src/EFCore/Metadata/IEntityType.cs b/src/EFCore/Metadata/IEntityType.cs
index 9bd3499963f..53a3bb83c9c 100644
--- a/src/EFCore/Metadata/IEntityType.cs
+++ b/src/EFCore/Metadata/IEntityType.cs
@@ -25,12 +25,6 @@ public interface IEntityType : IReadOnlyEntityType, ITypeBase
///
InstantiationBinding? ServiceOnlyConstructorBinding { get; }
- ///
- /// Returns the that will be used for storing a discriminator value.
- ///
- new IProperty? FindDiscriminatorProperty()
- => (IProperty?)((IReadOnlyEntityType)this).FindDiscriminatorProperty();
-
///
/// Gets the root base type for a given entity type.
///
@@ -73,14 +67,14 @@ public interface IEntityType : IReadOnlyEntityType, ITypeBase
///
/// The derived types.
new IEnumerable GetDerivedTypes()
- => ((IReadOnlyEntityType)this).GetDerivedTypes().Cast();
+ => ((IReadOnlyTypeBase)this).GetDerivedTypes().Cast();
///
/// Returns all derived types of this entity type, including the type itself.
///
/// Derived types.
new IEnumerable GetDerivedTypesInclusive()
- => ((IReadOnlyEntityType)this).GetDerivedTypesInclusive().Cast();
+ => ((IReadOnlyTypeBase)this).GetDerivedTypesInclusive().Cast();
///
/// Gets all types in the model that directly derive from this entity type.
diff --git a/src/EFCore/Metadata/IMutableComplexType.cs b/src/EFCore/Metadata/IMutableComplexType.cs
index 4e9056ba685..0053729426b 100644
--- a/src/EFCore/Metadata/IMutableComplexType.cs
+++ b/src/EFCore/Metadata/IMutableComplexType.cs
@@ -22,4 +22,40 @@ public interface IMutableComplexType : IReadOnlyComplexType, IMutableTypeBase
/// Gets the associated property.
///
new IMutableComplexProperty ComplexProperty { get; }
+
+ ///
+ /// Gets or sets the base type of this complex type. Returns if this is not a derived type in an inheritance
+ /// hierarchy.
+ ///
+ new IMutableComplexType? BaseType { get; }
+
+ ///
+ /// Gets the root base type for a given complex type.
+ ///
+ ///
+ /// The root base type. If the given complex type is not a derived type, then the same complex type is returned.
+ ///
+ new IMutableComplexType GetRootType()
+ => (IMutableComplexType)((IReadOnlyTypeBase)this).GetRootType();
+
+ ///
+ /// Gets all types in the model that derive from this complex type.
+ ///
+ /// The derived types.
+ new IEnumerable GetDerivedTypes()
+ => ((IReadOnlyTypeBase)this).GetDerivedTypes().Cast();
+
+ ///
+ /// Returns all derived types of this complex type, including the type itself.
+ ///
+ /// Derived types.
+ new IEnumerable GetDerivedTypesInclusive()
+ => ((IReadOnlyTypeBase)this).GetDerivedTypesInclusive().Cast();
+
+ ///
+ /// Gets all types in the model that directly derive from this complex type.
+ ///
+ /// The derived types.
+ new IEnumerable GetDirectlyDerivedTypes()
+ => ((IReadOnlyTypeBase)this).GetDirectlyDerivedTypes().Cast();
}
diff --git a/src/EFCore/Metadata/IMutableEntityType.cs b/src/EFCore/Metadata/IMutableEntityType.cs
index d2321aa2b9a..53d118fd707 100644
--- a/src/EFCore/Metadata/IMutableEntityType.cs
+++ b/src/EFCore/Metadata/IMutableEntityType.cs
@@ -43,19 +43,6 @@ public interface IMutableEntityType : IReadOnlyEntityType, IMutableTypeBase
/// The LINQ expression filter.
void SetQueryFilter(LambdaExpression? queryFilter);
- ///
- /// Returns the property that will be used for storing a discriminator value.
- ///
- /// The property that will be used for storing a discriminator value.
- new IMutableProperty? FindDiscriminatorProperty()
- => (IMutableProperty?)((IReadOnlyEntityType)this).FindDiscriminatorProperty();
-
- ///
- /// Sets the that will be used for storing a discriminator value.
- ///
- /// The property to set.
- void SetDiscriminatorProperty(IReadOnlyProperty? property);
-
///
/// Sets the value indicating whether the discriminator mapping is complete.
///
@@ -63,19 +50,6 @@ public interface IMutableEntityType : IReadOnlyEntityType, IMutableTypeBase
void SetDiscriminatorMappingComplete(bool? complete)
=> SetOrRemoveAnnotation(CoreAnnotationNames.DiscriminatorMappingComplete, complete);
- ///
- /// Sets the discriminator value for this entity type.
- ///
- /// The value to set.
- void SetDiscriminatorValue(object? value)
- => SetAnnotation(CoreAnnotationNames.DiscriminatorValue, value);
-
- ///
- /// Removes the discriminator value for this entity type.
- ///
- void RemoveDiscriminatorValue()
- => RemoveAnnotation(CoreAnnotationNames.DiscriminatorValue);
-
///
/// Gets all types in the model from which this entity type derives, starting with the root.
///
@@ -113,21 +87,21 @@ void RemoveDiscriminatorValue()
///
/// The derived types.
new IEnumerable GetDerivedTypes()
- => ((IReadOnlyEntityType)this).GetDerivedTypes().Cast();
+ => ((IReadOnlyTypeBase)this).GetDerivedTypes().Cast();
///
/// Returns all derived types of this entity type, including the type itself.
///
/// Derived types.
new IEnumerable GetDerivedTypesInclusive()
- => ((IReadOnlyEntityType)this).GetDerivedTypesInclusive().Cast();
+ => ((IReadOnlyTypeBase)this).GetDerivedTypesInclusive().Cast();
///
/// Gets all types in the model that directly derive from this entity type.
///
/// The derived types.
new IEnumerable GetDirectlyDerivedTypes()
- => ((IReadOnlyEntityType)this).GetDirectlyDerivedTypes().Cast();
+ => ((IReadOnlyTypeBase)this).GetDirectlyDerivedTypes().Cast();
///
/// Gets the root base type for a given entity type.
diff --git a/src/EFCore/Metadata/IMutableTypeBase.cs b/src/EFCore/Metadata/IMutableTypeBase.cs
index 3c5080be740..69efecea0e5 100644
--- a/src/EFCore/Metadata/IMutableTypeBase.cs
+++ b/src/EFCore/Metadata/IMutableTypeBase.cs
@@ -32,6 +32,42 @@ public interface IMutableTypeBase : IReadOnlyTypeBase, IMutableAnnotatable
new IMutableEntityType ContainingEntityType
=> (IMutableEntityType)this;
+ ///
+ /// Gets or sets the base type of this type. Returns if this is not a derived type in an inheritance
+ /// hierarchy.
+ ///
+ new IMutableTypeBase? BaseType { get; set; }
+
+ ///
+ /// Gets the root base type for a given type.
+ ///
+ ///
+ /// The root base type. If the given type is not a derived type, then the same type is returned.
+ ///
+ new IMutableTypeBase GetRootType()
+ => (IMutableTypeBase)((IReadOnlyTypeBase)this).GetRootType();
+
+ ///
+ /// Gets all types in the model that derive from this type.
+ ///
+ /// The derived types.
+ new IEnumerable GetDerivedTypes()
+ => ((IReadOnlyTypeBase)this).GetDerivedTypes().Cast();
+
+ ///
+ /// Returns all derived types of this type, including the type itself.
+ ///
+ /// Derived types.
+ new IEnumerable GetDerivedTypesInclusive()
+ => ((IReadOnlyTypeBase)this).GetDerivedTypesInclusive().Cast();
+
+ ///
+ /// Gets all types in the model that directly derive from this type.
+ ///
+ /// The derived types.
+ new IEnumerable GetDirectlyDerivedTypes()
+ => ((IReadOnlyTypeBase)this).GetDirectlyDerivedTypes().Cast();
+
///
/// Marks the given member name as ignored, preventing conventions from adding a matching property
/// or navigation to the type.
@@ -60,6 +96,32 @@ public interface IMutableTypeBase : IReadOnlyTypeBase, IMutableAnnotatable
/// The list of ignored member names.
IEnumerable GetIgnoredMembers();
+ ///
+ /// Returns the property that will be used for storing a discriminator value.
+ ///
+ /// The property that will be used for storing a discriminator value.
+ new IMutableProperty? FindDiscriminatorProperty()
+ => (IMutableProperty?)((IReadOnlyTypeBase)this).FindDiscriminatorProperty();
+
+ ///
+ /// Sets the that will be used for storing a discriminator value.
+ ///
+ /// The property to set.
+ void SetDiscriminatorProperty(IReadOnlyProperty? property);
+
+ ///
+ /// Sets the discriminator value for this type.
+ ///
+ /// The value to set.
+ void SetDiscriminatorValue(object? value)
+ => SetAnnotation(CoreAnnotationNames.DiscriminatorValue, value);
+
+ ///
+ /// Removes the discriminator value for this type.
+ ///
+ void RemoveDiscriminatorValue()
+ => RemoveAnnotation(CoreAnnotationNames.DiscriminatorValue);
+
///
/// Adds a property to this type.
///
diff --git a/src/EFCore/Metadata/IReadOnlyComplexType.cs b/src/EFCore/Metadata/IReadOnlyComplexType.cs
index 6d6633a641c..e23f8c684fd 100644
--- a/src/EFCore/Metadata/IReadOnlyComplexType.cs
+++ b/src/EFCore/Metadata/IReadOnlyComplexType.cs
@@ -19,6 +19,12 @@ public interface IReadOnlyComplexType : IReadOnlyTypeBase
///
IReadOnlyComplexProperty ComplexProperty { get; }
+ ///
+ /// Gets the base type of this complex type. Returns if this is not a
+ /// derived type in an inheritance hierarchy.
+ ///
+ new IReadOnlyComplexType? BaseType { get; }
+
///
/// Gets a value indicating whether given type is one of the containing types for this complex type.
///
@@ -44,6 +50,34 @@ bool IsContainedBy(Type type)
return false;
}
+ ///
+ /// Gets all types in the model that derive from this complex type.
+ ///
+ /// The derived types.
+ new IEnumerable GetDerivedTypes();
+
+ ///
+ /// Returns all derived types of this complex type, including the type itself.
+ ///
+ /// Derived types.
+ new IEnumerable GetDerivedTypesInclusive()
+ => new[] { this }.Concat(GetDerivedTypes());
+
+ ///
+ /// Gets all types in the model that directly derive from this complex type.
+ ///
+ /// The derived types.
+ new IEnumerable GetDirectlyDerivedTypes();
+
+ ///
+ /// Gets the root base type for a given entity type.
+ ///
+ ///
+ /// The root base type. If the given entity type is not a derived type, then the same entity type is returned.
+ ///
+ new IReadOnlyComplexType GetRootType()
+ => BaseType?.GetRootType() ?? this;
+
///
///
/// Creates a human-readable representation of the given metadata.
diff --git a/src/EFCore/Metadata/IReadOnlyEntityType.cs b/src/EFCore/Metadata/IReadOnlyEntityType.cs
index b5ee5ba7083..0b04f92494c 100644
--- a/src/EFCore/Metadata/IReadOnlyEntityType.cs
+++ b/src/EFCore/Metadata/IReadOnlyEntityType.cs
@@ -18,7 +18,7 @@ public interface IReadOnlyEntityType : IReadOnlyTypeBase
/// Gets the base type of this entity type. Returns if this is not a
/// derived type in an inheritance hierarchy.
///
- IReadOnlyEntityType? BaseType { get; }
+ new IReadOnlyEntityType? BaseType { get; }
///
/// Gets the data stored in the model for the given entity type.
@@ -35,22 +35,6 @@ public interface IReadOnlyEntityType : IReadOnlyTypeBase
/// The LINQ expression filter.
LambdaExpression? GetQueryFilter();
- ///
- /// Returns the property that will be used for storing a discriminator value.
- ///
- /// The property that will be used for storing a discriminator value.
- IReadOnlyProperty? FindDiscriminatorProperty()
- {
- var propertyName = GetDiscriminatorPropertyName();
- return propertyName == null ? null : FindProperty(propertyName);
- }
-
- ///
- /// Returns the name of the property that will be used for storing a discriminator value.
- ///
- /// The name of the property that will be used for storing a discriminator value.
- string? GetDiscriminatorPropertyName();
-
///
/// Returns the value indicating whether the discriminator mapping is complete for this entity type.
///
@@ -58,28 +42,6 @@ bool GetIsDiscriminatorMappingComplete()
=> (bool?)this[CoreAnnotationNames.DiscriminatorMappingComplete]
?? true;
- ///
- /// Returns the discriminator value for this entity type.
- ///
- /// The discriminator value for this entity type.
- object? GetDiscriminatorValue()
- {
- var annotation = FindAnnotation(CoreAnnotationNames.DiscriminatorValue);
- return annotation != null
- ? annotation.Value
- : !ClrType.IsInstantiable()
- || (BaseType == null && GetDirectlyDerivedTypes().Count() == 0)
- ? null
- : (object?)GetDefaultDiscriminatorValue();
- }
-
- ///
- /// Returns the default discriminator value that would be used for this entity type.
- ///
- /// The default discriminator value for this entity type.
- string GetDefaultDiscriminatorValue()
- => !HasSharedClrType ? ClrType.ShortDisplayName() : ShortName();
-
///
/// Gets all types in the model from which this entity type derives, starting with the root.
///
@@ -123,20 +85,22 @@ IEnumerable GetAllBaseTypesInclusiveAscending()
/// Gets all types in the model that derive from this entity type.
///
/// The derived types.
- IEnumerable GetDerivedTypes();
+ new IEnumerable GetDerivedTypes()
+ => ((IReadOnlyTypeBase)this).GetDerivedTypes().Cast();
///
/// Returns all derived types of this entity type, including the type itself.
///
/// Derived types.
- IEnumerable GetDerivedTypesInclusive()
- => new[] { this }.Concat(GetDerivedTypes());
+ new IEnumerable GetDerivedTypesInclusive()
+ => ((IReadOnlyTypeBase)this).GetDerivedTypesInclusive().Cast();
///
/// Gets all types in the model that directly derive from this entity type.
///
/// The derived types.
- IEnumerable GetDirectlyDerivedTypes();
+ new IEnumerable GetDirectlyDerivedTypes()
+ => ((IReadOnlyTypeBase)this).GetDirectlyDerivedTypes().Cast();
///
/// Returns all the derived types of this entity type, including the type itself,
@@ -152,7 +116,7 @@ IEnumerable GetConcreteDerivedTypesInclusive()
///
/// The root base type. If the given entity type is not a derived type, then the same entity type is returned.
///
- IReadOnlyEntityType GetRootType()
+ new IReadOnlyEntityType GetRootType()
=> BaseType?.GetRootType() ?? this;
///
diff --git a/src/EFCore/Metadata/IReadOnlyTypeBase.cs b/src/EFCore/Metadata/IReadOnlyTypeBase.cs
index 5da87dc25aa..b08a3e3eb71 100644
--- a/src/EFCore/Metadata/IReadOnlyTypeBase.cs
+++ b/src/EFCore/Metadata/IReadOnlyTypeBase.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace Microsoft.EntityFrameworkCore.Metadata;
@@ -24,6 +25,12 @@ public interface IReadOnlyTypeBase : IReadOnlyAnnotatable
IReadOnlyEntityType ContainingEntityType
=> (IReadOnlyEntityType)this;
+ ///
+ /// Gets the base type of this type. Returns if this is not a
+ /// derived type in an inheritance hierarchy.
+ ///
+ IReadOnlyTypeBase? BaseType { get; }
+
///
/// Gets the name of this type.
///
@@ -177,6 +184,72 @@ bool IsAssignableFrom(IReadOnlyTypeBase derivedType)
bool IsStrictlyDerivedFrom(IReadOnlyTypeBase baseType)
=> this != Check.NotNull(baseType, nameof(baseType)) && baseType.IsAssignableFrom(this);
+ ///
+ /// Gets all types in the model that derive from this type.
+ ///
+ /// The derived types.
+ IEnumerable GetDerivedTypes();
+
+ ///
+ /// Returns all derived types of this type, including the type itself.
+ ///
+ /// Derived types.
+ IEnumerable GetDerivedTypesInclusive()
+ => new[] { this }.Concat(GetDerivedTypes());
+
+ ///
+ /// Gets all types in the model that directly derive from this type.
+ ///
+ /// The derived types.
+ IEnumerable GetDirectlyDerivedTypes();
+
+ ///
+ /// Gets the root base type for a given entity type.
+ ///
+ ///
+ /// The root base type. If the given entity type is not a derived type, then the same entity type is returned.
+ ///
+ IReadOnlyTypeBase GetRootType()
+ => BaseType?.GetRootType() ?? this;
+
+ ///
+ /// Returns the property that will be used for storing a discriminator value.
+ ///
+ /// The property that will be used for storing a discriminator value.
+ IReadOnlyProperty? FindDiscriminatorProperty()
+ {
+ var propertyName = GetDiscriminatorPropertyName();
+ return propertyName == null ? null : FindProperty(propertyName);
+ }
+
+ ///
+ /// Returns the name of the property that will be used for storing a discriminator value.
+ ///
+ /// The name of the property that will be used for storing a discriminator value.
+ string? GetDiscriminatorPropertyName();
+
+ ///
+ /// Returns the discriminator value for this type.
+ ///
+ /// The discriminator value for this type.
+ object? GetDiscriminatorValue()
+ {
+ var annotation = FindAnnotation(CoreAnnotationNames.DiscriminatorValue);
+ return annotation != null
+ ? annotation.Value
+ : !ClrType.IsInstantiable()
+ || (BaseType == null && GetDirectlyDerivedTypes().Count() == 0)
+ ? null
+ : (object?)GetDefaultDiscriminatorValue();
+ }
+
+ ///
+ /// Returns the default discriminator value that would be used for this type.
+ ///
+ /// The default discriminator value for this type.
+ string GetDefaultDiscriminatorValue()
+ => !HasSharedClrType ? ClrType.ShortDisplayName() : ShortName();
+
///
/// Gets the property with the given name. Returns if no property with the given name is defined.
///
diff --git a/src/EFCore/Metadata/ITypeBase.cs b/src/EFCore/Metadata/ITypeBase.cs
index 45aa87e5bc3..e0d92258e51 100644
--- a/src/EFCore/Metadata/ITypeBase.cs
+++ b/src/EFCore/Metadata/ITypeBase.cs
@@ -22,11 +22,43 @@ public interface ITypeBase : IReadOnlyTypeBase, IAnnotatable
new IEntityType ContainingEntityType
=> (IEntityType)this;
+ ///
+ /// Gets the base type of this type. Returns if this is not a derived type in an inheritance
+ /// hierarchy.
+ ///
+ new ITypeBase? BaseType { get; }
+
///
/// Gets the for the preferred constructor.
///
InstantiationBinding? ConstructorBinding { get; }
+ ///
+ /// Gets all types in the model that derive from this type.
+ ///
+ /// The derived types.
+ new IEnumerable GetDerivedTypes()
+ => ((IReadOnlyTypeBase)this).GetDerivedTypes().Cast();
+
+ ///
+ /// Returns all derived types of this type, including the type itself.
+ ///
+ /// Derived types.
+ new IEnumerable GetDerivedTypesInclusive()
+ => ((IReadOnlyTypeBase)this).GetDerivedTypesInclusive().Cast();
+
+ ///
+ /// Gets all types in the model that directly derive from this type.
+ ///
+ /// The derived types.
+ new IEnumerable GetDirectlyDerivedTypes();
+
+ ///
+ /// Returns the that will be used for storing a discriminator value.
+ ///
+ new IProperty? FindDiscriminatorProperty()
+ => (IProperty?)((IReadOnlyTypeBase)this).FindDiscriminatorProperty();
+
///
/// Gets a property on the given type. Returns if no property is found.
///
diff --git a/src/EFCore/Metadata/Internal/ComplexType.cs b/src/EFCore/Metadata/Internal/ComplexType.cs
index b906a8a1c5b..35e15e5726f 100644
--- a/src/EFCore/Metadata/Internal/ComplexType.cs
+++ b/src/EFCore/Metadata/Internal/ComplexType.cs
@@ -137,6 +137,15 @@ public virtual void SetRemovedFromModel()
public new virtual ComplexType? BaseType
=> (ComplexType?)base.BaseType;
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override TypeBase? SetBaseType(TypeBase? newBaseType, ConfigurationSource configurationSource)
+ => SetBaseType((ComplexType?)newBaseType, configurationSource);
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -216,7 +225,7 @@ public virtual void SetRemovedFromModel()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- public virtual ConfigurationSource? GetBaseTypeConfigurationSource()
+ public override ConfigurationSource? GetBaseTypeConfigurationSource()
=> _baseTypeConfigurationSource;
[DebuggerStepThrough]
@@ -290,7 +299,7 @@ public virtual bool IsAssignableFrom(ComplexType derivedType)
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- public virtual ComplexType GetRootType()
+ public new virtual ComplexType GetRootType()
=> BaseType?.GetRootType() ?? this;
///
@@ -529,6 +538,53 @@ public override string ToString()
#region Explicit interface implementations
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ IReadOnlyComplexType? IReadOnlyComplexType.BaseType
+ {
+ [DebuggerStepThrough]
+ get => BaseType;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ IMutableComplexType? IMutableComplexType.BaseType
+ {
+ get => BaseType;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ IConventionComplexType? IConventionComplexType.BaseType
+ {
+ [DebuggerStepThrough]
+ get => BaseType;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ IComplexType? IComplexType.BaseType
+ {
+ [DebuggerStepThrough]
+ get => BaseType;
+ }
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -685,5 +741,35 @@ IEntityType ITypeBase.ContainingEntityType
get => ContainingEntityType;
}
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ IEnumerable IReadOnlyComplexType.GetDerivedTypes()
+ => GetDerivedTypes();
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ IEnumerable IReadOnlyComplexType.GetDirectlyDerivedTypes()
+ => GetDirectlyDerivedTypes();
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ IEnumerable IComplexType.GetDirectlyDerivedTypes()
+ => GetDirectlyDerivedTypes();
+
#endregion
}
diff --git a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
index cb6e7a32c61..1246a351809 100644
--- a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
+++ b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
@@ -195,15 +195,6 @@ public static class CoreAnnotationNames
///
public const string QueryFilter = "QueryFilter";
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [Obsolete] // Remove with defining query
- public const string DefiningQuery = "DefiningQuery";
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -411,9 +402,6 @@ public static class CoreAnnotationNames
AfterSaveBehavior,
BeforeSaveBehavior,
QueryFilter,
-#pragma warning disable CS0612 // Type or member is obsolete
- DefiningQuery,
-#pragma warning restore CS0612 // Type or member is obsolete
EagerLoaded,
LazyLoadingEnabled,
ProviderClrType,
diff --git a/src/EFCore/Metadata/Internal/EntityType.cs b/src/EFCore/Metadata/Internal/EntityType.cs
index 7cadc45f845..79cf4767af7 100644
--- a/src/EFCore/Metadata/Internal/EntityType.cs
+++ b/src/EFCore/Metadata/Internal/EntityType.cs
@@ -330,6 +330,15 @@ private string DisplayName()
private void UpdateIsKeylessConfigurationSource(ConfigurationSource configurationSource)
=> _isKeylessConfigurationSource = configurationSource.Max(_isKeylessConfigurationSource);
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override TypeBase? SetBaseType(TypeBase? newBaseType, ConfigurationSource configurationSource)
+ => SetBaseType((EntityType?)newBaseType, configurationSource);
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -423,9 +432,15 @@ private void UpdateIsKeylessConfigurationSource(ConfigurationSource configuratio
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- public virtual ConfigurationSource? GetBaseTypeConfigurationSource()
+ public override ConfigurationSource? GetBaseTypeConfigurationSource()
=> _baseTypeConfigurationSource;
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
[DebuggerStepThrough]
private void UpdateBaseTypeConfigurationSource(ConfigurationSource configurationSource)
=> _baseTypeConfigurationSource = configurationSource.Max(_baseTypeConfigurationSource);
@@ -475,8 +490,8 @@ private bool InheritsFrom(EntityType entityType)
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
[DebuggerStepThrough]
- public virtual EntityType GetRootType()
- => (EntityType)((IReadOnlyEntityType)this).GetRootType();
+ new public virtual EntityType GetRootType()
+ => (EntityType)((IReadOnlyTypeBase)this).GetRootType();
///
/// Runs the conventions when an annotation was set or removed.
@@ -2903,75 +2918,6 @@ public virtual PropertyAccessMode GetNavigationAccessMode()
public virtual ConfigurationSource? GetQueryFilterConfigurationSource()
=> FindAnnotation(CoreAnnotationNames.QueryFilter)?.GetConfigurationSource();
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [Obsolete]
- public virtual LambdaExpression? SetDefiningQuery(LambdaExpression? definingQuery, ConfigurationSource configurationSource)
- => (LambdaExpression?)SetOrRemoveAnnotation(CoreAnnotationNames.DefiningQuery, definingQuery, configurationSource)?.Value;
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual Property? SetDiscriminatorProperty(Property? property, ConfigurationSource configurationSource)
- {
- if ((string?)this[CoreAnnotationNames.DiscriminatorProperty] == property?.Name)
- {
- return property;
- }
-
- CheckDiscriminatorProperty(property);
-
- SetAnnotation(CoreAnnotationNames.DiscriminatorProperty, property?.Name, configurationSource);
-
- return Model.ConventionDispatcher.OnDiscriminatorPropertySet(Builder, property?.Name) == property?.Name
- ? property
- : (Property?)((IReadOnlyEntityType)this).FindDiscriminatorProperty();
- }
-
- private void CheckDiscriminatorProperty(Property? property)
- {
- if (property != null)
- {
- if (BaseType != null)
- {
- throw new InvalidOperationException(
- CoreStrings.DiscriminatorPropertyMustBeOnRoot(DisplayName()));
- }
-
- if (property.DeclaringType != this)
- {
- throw new InvalidOperationException(
- CoreStrings.DiscriminatorPropertyNotFound(property.Name, DisplayName()));
- }
- }
- }
-
- ///
- /// Returns the name of the property that will be used for storing a discriminator value.
- ///
- /// The name of the property that will be used for storing a discriminator value.
- public virtual string? GetDiscriminatorPropertyName()
- => BaseType is null
- ? (string?)this[CoreAnnotationNames.DiscriminatorProperty]
- : ((IReadOnlyEntityType)this).GetRootType().GetDiscriminatorPropertyName();
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [DebuggerStepThrough]
- public virtual ConfigurationSource? GetDiscriminatorPropertyConfigurationSource()
- => FindAnnotation(CoreAnnotationNames.DiscriminatorProperty)?.GetConfigurationSource();
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -3225,30 +3171,6 @@ IModel ITypeBase.Model
get => BaseType;
}
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [DebuggerStepThrough]
- void IMutableEntityType.SetDiscriminatorProperty(IReadOnlyProperty? property)
- => SetDiscriminatorProperty((Property?)property, ConfigurationSource.Explicit);
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [DebuggerStepThrough]
- IConventionProperty? IConventionEntityType.SetDiscriminatorProperty(
- IReadOnlyProperty? property,
- bool fromDataAnnotation)
- => SetDiscriminatorProperty(
- (Property?)property,
- fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
diff --git a/src/EFCore/Metadata/Internal/InternalComplexTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalComplexTypeBuilder.cs
index c32a85e7df1..6c150269c17 100644
--- a/src/EFCore/Metadata/Internal/InternalComplexTypeBuilder.cs
+++ b/src/EFCore/Metadata/Internal/InternalComplexTypeBuilder.cs
@@ -503,6 +503,60 @@ public virtual bool CanSetServiceOnlyConstructorBinding(
=> configurationSource.Overrides(Metadata.GetServiceOnlyConstructorBindingConfigurationSource())
|| Metadata.ServiceOnlyConstructorBinding == constructorBinding;
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual ComplexTypeDiscriminatorBuilder? HasDiscriminator(ConfigurationSource configurationSource)
+ => DiscriminatorBuilder(
+ GetOrCreateDiscriminatorProperty(type: null, name: null, memberInfo: null, configurationSource));
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual ComplexTypeDiscriminatorBuilder? HasDiscriminator(
+ string? name,
+ Type? type,
+ ConfigurationSource configurationSource)
+ {
+ Check.DebugAssert(name != null || type != null, $"Either {nameof(name)} or {nameof(type)} should be non-null");
+
+ return CanSetDiscriminator(name, type, configurationSource)
+ ? DiscriminatorBuilder(
+ GetOrCreateDiscriminatorProperty(type, name, memberInfo: null, configurationSource))
+ : null;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual ComplexTypeDiscriminatorBuilder? HasDiscriminator(MemberInfo memberInfo, ConfigurationSource configurationSource)
+ => CanSetDiscriminator(
+ Check.NotNull(memberInfo, nameof(memberInfo)).GetSimpleMemberName(), memberInfo.GetMemberType(), configurationSource)
+ ? DiscriminatorBuilder(
+ GetOrCreateDiscriminatorProperty(type: null, name: null, memberInfo, configurationSource))
+ : null;
+
+ private ComplexTypeDiscriminatorBuilder? DiscriminatorBuilder(InternalPropertyBuilder? discriminatorPropertyBuilder)
+ => discriminatorPropertyBuilder == null ? null : new ComplexTypeDiscriminatorBuilder(Metadata);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public new virtual InternalComplexTypeBuilder? HasNoDiscriminator(ConfigurationSource configurationSource)
+ => (InternalComplexTypeBuilder?)base.HasNoDiscriminator(configurationSource);
+
IConventionComplexType IConventionComplexTypeBuilder.Metadata
{
[DebuggerStepThrough]
@@ -617,4 +671,72 @@ IConventionComplexTypeBuilder IConventionComplexTypeBuilder.RemoveUnusedImplicit
bool fromDataAnnotation)
=> (IConventionComplexTypeBuilder?)UsePropertyAccessMode(
propertyAccessMode, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ IConventionComplexTypeDiscriminatorBuilder? IConventionComplexTypeBuilder.HasDiscriminator(bool fromDataAnnotation)
+ => HasDiscriminator(
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ IConventionComplexTypeDiscriminatorBuilder? IConventionComplexTypeBuilder.HasDiscriminator(Type type, bool fromDataAnnotation)
+ => HasDiscriminator(
+ name: null, Check.NotNull(type, nameof(type)),
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ IConventionComplexTypeDiscriminatorBuilder? IConventionComplexTypeBuilder.HasDiscriminator(string name, bool fromDataAnnotation)
+ => HasDiscriminator(
+ Check.NotEmpty(name, nameof(name)), type: null,
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ IConventionComplexTypeDiscriminatorBuilder? IConventionComplexTypeBuilder.HasDiscriminator(string name, Type type, bool fromDataAnnotation)
+ => HasDiscriminator(
+ Check.NotEmpty(name, nameof(name)), Check.NotNull(type, nameof(type)),
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ IConventionComplexTypeDiscriminatorBuilder? IConventionComplexTypeBuilder.HasDiscriminator(MemberInfo memberInfo, bool fromDataAnnotation)
+ => HasDiscriminator(
+ memberInfo, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ public IConventionComplexTypeBuilder? HasNoDiscriminator(bool fromDataAnnotation = false)
+ => HasNoDiscriminator(fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
}
diff --git a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
index 58c607bc23e..39f503cab8e 100644
--- a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
+++ b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
@@ -1322,38 +1322,6 @@ public virtual bool CanSetQueryFilter(LambdaExpression? filter, ConfigurationSou
=> configurationSource.Overrides(Metadata.GetQueryFilterConfigurationSource())
|| Metadata.GetQueryFilter() == filter;
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [Obsolete]
- public virtual InternalEntityTypeBuilder? HasDefiningQuery(
- LambdaExpression? query,
- ConfigurationSource configurationSource)
- {
- if (CanSetDefiningQuery(query, configurationSource))
- {
- Metadata.SetDefiningQuery(query, configurationSource);
-
- return this;
- }
-
- return null;
- }
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [Obsolete]
- public virtual bool CanSetDefiningQuery(LambdaExpression? query, ConfigurationSource configurationSource)
- => configurationSource.Overrides(Metadata.GetDefiningQueryConfigurationSource())
- || Metadata.GetDefiningQuery() == query;
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -4352,8 +4320,7 @@ public virtual bool CanSetServiceOnlyConstructorBinding(
///
public virtual DiscriminatorBuilder? HasDiscriminator(ConfigurationSource configurationSource)
=> DiscriminatorBuilder(
- GetOrCreateDiscriminatorProperty(type: null, name: null, ConfigurationSource.Convention),
- configurationSource);
+ GetOrCreateDiscriminatorProperty(type: null, name: null, memberInfo: null, configurationSource));
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -4370,8 +4337,7 @@ public virtual bool CanSetServiceOnlyConstructorBinding(
return CanSetDiscriminator(name, type, configurationSource)
? DiscriminatorBuilder(
- GetOrCreateDiscriminatorProperty(type, name, configurationSource),
- configurationSource)
+ GetOrCreateDiscriminatorProperty(type, name, memberInfo: null, configurationSource))
: null;
}
@@ -4385,87 +4351,11 @@ public virtual bool CanSetServiceOnlyConstructorBinding(
=> CanSetDiscriminator(
Check.NotNull(memberInfo, nameof(memberInfo)).GetSimpleMemberName(), memberInfo.GetMemberType(), configurationSource)
? DiscriminatorBuilder(
- Metadata.GetRootType().Builder.Property(
- memberInfo, configurationSource),
- configurationSource)
+ GetOrCreateDiscriminatorProperty(type: null, name: null, memberInfo, configurationSource))
: null;
- private const string DefaultDiscriminatorName = "Discriminator";
-
- private static readonly Type DefaultDiscriminatorType = typeof(string);
-
- private InternalPropertyBuilder? GetOrCreateDiscriminatorProperty(Type? type, string? name, ConfigurationSource configurationSource)
- {
- var discriminatorProperty = ((IReadOnlyEntityType)Metadata).FindDiscriminatorProperty();
- if ((name != null && discriminatorProperty?.Name != name)
- || (type != null && discriminatorProperty?.ClrType != type))
- {
- discriminatorProperty = null;
- }
-
- return Metadata.GetRootType().Builder.Property(
- type ?? discriminatorProperty?.ClrType ?? DefaultDiscriminatorType,
- name ?? discriminatorProperty?.Name ?? DefaultDiscriminatorName,
- typeConfigurationSource: type != null ? configurationSource : null,
- configurationSource)?.AfterSave(PropertySaveBehavior.Throw, ConfigurationSource.Convention);
- }
-
- private DiscriminatorBuilder? DiscriminatorBuilder(
- InternalPropertyBuilder? discriminatorPropertyBuilder,
- ConfigurationSource configurationSource)
- {
- if (discriminatorPropertyBuilder == null)
- {
- return null;
- }
-
- var rootTypeBuilder = Metadata.GetRootType().Builder;
- var discriminatorProperty = discriminatorPropertyBuilder.Metadata;
- // Make sure the property is on the root type
- discriminatorPropertyBuilder = rootTypeBuilder.Property(
- discriminatorProperty.ClrType, discriminatorProperty.Name, null, ConfigurationSource.Convention)!;
-
- RemoveUnusedDiscriminatorProperty(discriminatorProperty, configurationSource);
-
- rootTypeBuilder.Metadata.SetDiscriminatorProperty(discriminatorProperty, configurationSource);
-
- RemoveIncompatibleDiscriminatorValues(Metadata, discriminatorProperty, configurationSource);
-
- discriminatorPropertyBuilder.IsRequired(true, ConfigurationSource.Convention);
- discriminatorPropertyBuilder.HasValueGeneratorFactory(
- typeof(DiscriminatorValueGeneratorFactory), ConfigurationSource.Convention);
-
- return new DiscriminatorBuilder(Metadata);
- }
-
- private void RemoveIncompatibleDiscriminatorValues(
- EntityType entityType,
- Property? newDiscriminatorProperty,
- ConfigurationSource configurationSource)
- {
- if ((newDiscriminatorProperty != null || entityType.BaseType != null)
- && (newDiscriminatorProperty == null
- || newDiscriminatorProperty.ClrType.IsInstanceOfType(((IReadOnlyEntityType)entityType).GetDiscriminatorValue())))
- {
- return;
- }
-
- if (configurationSource.Overrides(((IConventionEntityType)entityType).GetDiscriminatorValueConfigurationSource()))
- {
- ((IMutableEntityType)entityType).RemoveDiscriminatorValue();
- }
-
- if (entityType.BaseType == null)
- {
- foreach (var derivedType in entityType.GetDerivedTypes())
- {
- if (configurationSource.Overrides(((IConventionEntityType)derivedType).GetDiscriminatorValueConfigurationSource()))
- {
- ((IMutableEntityType)derivedType).RemoveDiscriminatorValue();
- }
- }
- }
- }
+ private DiscriminatorBuilder? DiscriminatorBuilder(InternalPropertyBuilder? discriminatorPropertyBuilder)
+ => discriminatorPropertyBuilder == null ? null : new DiscriminatorBuilder(Metadata);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -4473,27 +4363,14 @@ private void RemoveIncompatibleDiscriminatorValues(
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public virtual InternalEntityTypeBuilder? HasNoDiscriminator(ConfigurationSource configurationSource)
+ public override InternalEntityTypeBuilder? HasNoDiscriminator(ConfigurationSource configurationSource)
{
- if (Metadata[CoreAnnotationNames.DiscriminatorProperty] == null)
- {
- return this;
- }
-
- if (!configurationSource.Overrides(Metadata.GetDiscriminatorPropertyConfigurationSource()))
+ var builder = base.HasNoDiscriminator(configurationSource);
+ if (builder == null)
{
return null;
}
- if (((IReadOnlyEntityType)Metadata).FindDiscriminatorProperty()?.DeclaringType == Metadata)
- {
- RemoveUnusedDiscriminatorProperty(null, configurationSource);
- }
-
- Metadata.SetDiscriminatorProperty(null, configurationSource);
-
- RemoveIncompatibleDiscriminatorValues(Metadata, null, configurationSource);
-
if (configurationSource == ConfigurationSource.Explicit)
{
((IMutableEntityType)Metadata).SetDiscriminatorMappingComplete(null);
@@ -4508,78 +4385,6 @@ private void RemoveIncompatibleDiscriminatorValues(
return this;
}
- private void RemoveUnusedDiscriminatorProperty(Property? newDiscriminatorProperty, ConfigurationSource configurationSource)
- {
- var oldDiscriminatorProperty = ((IReadOnlyEntityType)Metadata).FindDiscriminatorProperty() as Property;
- if (oldDiscriminatorProperty?.IsInModel == true
- && oldDiscriminatorProperty != newDiscriminatorProperty)
- {
- oldDiscriminatorProperty.DeclaringType.Builder.RemoveUnusedImplicitProperties(
- new[] { oldDiscriminatorProperty });
-
- if (oldDiscriminatorProperty.IsInModel)
- {
- oldDiscriminatorProperty.Builder.IsRequired(null, configurationSource);
- oldDiscriminatorProperty.Builder.HasValueGenerator((Type?)null, configurationSource);
- }
- }
- }
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual bool CanSetDiscriminator(string? name, Type? type, ConfigurationSource configurationSource)
- => name == null && type == null
- ? CanRemoveDiscriminator(configurationSource)
- : CanSetDiscriminator(((IReadOnlyEntityType)Metadata).FindDiscriminatorProperty(), name, type, configurationSource);
-
- private bool CanSetDiscriminator(
- IReadOnlyProperty? discriminatorProperty,
- string? name,
- Type? discriminatorType,
- ConfigurationSource configurationSource)
- => ((name == null && discriminatorType == null)
- || ((name == null || discriminatorProperty?.Name == name)
- && (discriminatorType == null || discriminatorProperty?.ClrType == discriminatorType))
- || configurationSource.Overrides(Metadata.GetRootType().GetDiscriminatorPropertyConfigurationSource()))
- && (discriminatorProperty != null
- || Metadata.GetRootType().Builder.CanAddDiscriminatorProperty(
- discriminatorType ?? DefaultDiscriminatorType,
- name ?? DefaultDiscriminatorName,
- typeConfigurationSource: discriminatorType != null
- ? configurationSource
- : null));
-
- private bool CanRemoveDiscriminator(ConfigurationSource configurationSource)
- => CanSetAnnotation(CoreAnnotationNames.DiscriminatorProperty, null, configurationSource);
-
- private bool CanAddDiscriminatorProperty(
- Type propertyType,
- string name,
- ConfigurationSource? typeConfigurationSource)
- {
- var conflictingProperty = Metadata.FindPropertiesInHierarchy(name).FirstOrDefault();
- if (conflictingProperty != null
- && (conflictingProperty.IsShadowProperty() || conflictingProperty.IsIndexerProperty())
- && conflictingProperty.ClrType != propertyType
- && typeConfigurationSource != null
- && !typeConfigurationSource.Overrides(conflictingProperty.GetTypeConfigurationSource()))
- {
- return false;
- }
-
- var memberInfo = Metadata.IsPropertyBag
- ? null
- : Metadata.ClrType.GetMembersInHierarchy(name).FirstOrDefault();
-
- return memberInfo == null
- || propertyType == memberInfo.GetMemberType()
- || typeConfigurationSource == null;
- }
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -5481,28 +5286,6 @@ bool IConventionEntityTypeBuilder.CanHaveTrigger(string modelName, bool fromData
bool IConventionEntityTypeBuilder.CanSetQueryFilter(LambdaExpression? filter, bool fromDataAnnotation)
=> CanSetQueryFilter(filter, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [DebuggerStepThrough]
- [Obsolete]
- IConventionEntityTypeBuilder? IConventionEntityTypeBuilder.HasDefiningQuery(LambdaExpression? query, bool fromDataAnnotation)
- => HasDefiningQuery(query, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [DebuggerStepThrough]
- [Obsolete]
- bool IConventionEntityTypeBuilder.CanSetDefiningQuery(LambdaExpression? query, bool fromDataAnnotation)
- => CanSetDefiningQuery(query, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -5597,64 +5380,6 @@ bool IConventionEntityTypeBuilder.CanSetDefiningQuery(LambdaExpression? query, b
IConventionEntityTypeBuilder? IConventionEntityTypeBuilder.HasNoDiscriminator(bool fromDataAnnotation)
=> HasNoDiscriminator(fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [DebuggerStepThrough]
- bool IConventionEntityTypeBuilder.CanSetDiscriminator(string name, bool fromDataAnnotation)
- => CanSetDiscriminator(
- name, type: null,
- fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [DebuggerStepThrough]
- bool IConventionEntityTypeBuilder.CanSetDiscriminator(Type type, bool fromDataAnnotation)
- => CanSetDiscriminator(
- name: null, type,
- fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [DebuggerStepThrough]
- bool IConventionEntityTypeBuilder.CanSetDiscriminator(string name, Type type, bool fromDataAnnotation)
- => CanSetDiscriminator(
- name, type,
- fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [DebuggerStepThrough]
- bool IConventionEntityTypeBuilder.CanSetDiscriminator(MemberInfo memberInfo, bool fromDataAnnotation)
- => CanSetDiscriminator(
- Check.NotNull(memberInfo, nameof(memberInfo)).GetSimpleMemberName(), memberInfo.GetMemberType(),
- fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- [DebuggerStepThrough]
- bool IConventionEntityTypeBuilder.CanRemoveDiscriminator(bool fromDataAnnotation)
- => CanRemoveDiscriminator(fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
diff --git a/src/EFCore/Metadata/Internal/InternalTypeBaseBuilder.cs b/src/EFCore/Metadata/Internal/InternalTypeBaseBuilder.cs
index 0e889e243f4..213ba106bdb 100644
--- a/src/EFCore/Metadata/Internal/InternalTypeBaseBuilder.cs
+++ b/src/EFCore/Metadata/Internal/InternalTypeBaseBuilder.cs
@@ -13,7 +13,8 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
-public abstract class InternalTypeBaseBuilder : AnnotatableBuilder,
+public abstract class InternalTypeBaseBuilder :
+ AnnotatableBuilder,
IConventionTypeBaseBuilder
{
///
@@ -169,9 +170,9 @@ public static bool IsCompatible(MemberInfo? newMemberInfo, PropertyBase existing
ConfigurationSource? configurationSource,
bool skipTypeCheck = false)
{
- var entityType = Metadata;
+ var structuralType = Metadata;
List? propertiesToDetach = null;
- var existingProperty = entityType.FindProperty(propertyName);
+ var existingProperty = structuralType.FindProperty(propertyName);
if (existingProperty != null)
{
if (existingProperty.DeclaringType != Metadata)
@@ -181,7 +182,7 @@ public static bool IsCompatible(MemberInfo? newMemberInfo, PropertyBase existing
Metadata.RemoveIgnored(propertyName);
}
- entityType = (EntityType)existingProperty.DeclaringType;
+ structuralType = existingProperty.DeclaringType;
}
if (IsCompatible(memberInfo, existingProperty)
@@ -274,7 +275,7 @@ public static bool IsCompatible(MemberInfo? newMemberInfo, PropertyBase existing
RemoveMembersInHierarchy(propertyName, configurationSource.Value);
}
- builder = entityType.AddProperty(
+ builder = structuralType.AddProperty(
propertyName, propertyType, memberInfo, typeConfigurationSource, configurationSource.Value)!.Builder;
detachedProperties?.Attach(this);
@@ -1022,7 +1023,7 @@ protected virtual void RemovePropertyIfUnused(Property property, ConfigurationSo
Metadata.RemoveIgnored(propertyName);
}
- typeBase = (EntityType)existingComplexProperty.DeclaringType;
+ typeBase = existingComplexProperty.DeclaringType;
}
var existingComplexType = existingComplexProperty.ComplexType;
@@ -1336,6 +1337,224 @@ public virtual bool CanSetPropertyAccessMode(PropertyAccessMode? propertyAccessM
=> configurationSource.Overrides(((IConventionTypeBase)Metadata).GetPropertyAccessModeConfigurationSource())
|| ((IConventionTypeBase)Metadata).GetPropertyAccessMode() == propertyAccessMode;
+ private const string DefaultDiscriminatorName = "Discriminator";
+
+ private static readonly Type DefaultDiscriminatorType = typeof(string);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual InternalTypeBaseBuilder? HasNoDiscriminator(ConfigurationSource configurationSource)
+ {
+ if (Metadata[CoreAnnotationNames.DiscriminatorProperty] == null)
+ {
+ return this;
+ }
+
+ if (!configurationSource.Overrides(Metadata.GetDiscriminatorPropertyConfigurationSource()))
+ {
+ return null;
+ }
+
+ if (((IReadOnlyTypeBase)Metadata).FindDiscriminatorProperty()?.DeclaringType == Metadata)
+ {
+ RemoveUnusedDiscriminatorProperty(null, configurationSource);
+ }
+
+ Metadata.SetDiscriminatorProperty(null, configurationSource);
+
+ RemoveIncompatibleDiscriminatorValues(Metadata, null, configurationSource);
+
+ return this;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected virtual InternalPropertyBuilder? GetOrCreateDiscriminatorProperty(
+ Type? type, string? name, MemberInfo? memberInfo, ConfigurationSource configurationSource)
+ {
+ if (memberInfo != null)
+ {
+ type ??= memberInfo.GetMemberType();
+ name ??= memberInfo.Name;
+ }
+ else
+ {
+ var existingDiscriminatorProperty = ((IReadOnlyTypeBase)Metadata).FindDiscriminatorProperty();
+ if ((name == null || (existingDiscriminatorProperty?.Name) == name)
+ && (type == null || (existingDiscriminatorProperty?.ClrType) == type))
+ {
+ type ??= existingDiscriminatorProperty?.ClrType;
+ name ??= existingDiscriminatorProperty?.Name;
+ }
+
+ type ??= DefaultDiscriminatorType;
+ name ??= DefaultDiscriminatorName;
+ }
+
+ var rootType = Metadata.GetRootType();
+ var discriminatorPropertyBuilder = rootType.Builder.Property(
+ type,
+ name,
+ memberInfo,
+ typeConfigurationSource: type != null ? ConfigurationSource.Convention : null,
+ ConfigurationSource.Convention);
+
+ if (discriminatorPropertyBuilder == null)
+ {
+ if (configurationSource != ConfigurationSource.Convention)
+ {
+ rootType.Builder.RemoveProperty(rootType.FindProperty(name)!, configurationSource);
+ discriminatorPropertyBuilder = rootType.Builder.Property(
+ type,
+ name,
+ memberInfo,
+ typeConfigurationSource: type != null ? ConfigurationSource.Convention : null,
+ ConfigurationSource.Convention);
+ }
+
+ if (discriminatorPropertyBuilder == null)
+ {
+ return null;
+ }
+ }
+
+ var discriminatorProperty = discriminatorPropertyBuilder.Metadata;
+ RemoveUnusedDiscriminatorProperty(discriminatorProperty, configurationSource);
+
+ rootType.SetDiscriminatorProperty(discriminatorProperty, configurationSource);
+
+ RemoveIncompatibleDiscriminatorValues(Metadata, discriminatorProperty, configurationSource);
+
+ discriminatorPropertyBuilder.AfterSave(PropertySaveBehavior.Throw, ConfigurationSource.Convention);
+ discriminatorPropertyBuilder.IsRequired(true, ConfigurationSource.Convention);
+ discriminatorPropertyBuilder.HasValueGeneratorFactory(
+ typeof(DiscriminatorValueGeneratorFactory), ConfigurationSource.Convention);
+
+ return discriminatorPropertyBuilder;
+ }
+
+ private void RemoveUnusedDiscriminatorProperty(Property? newDiscriminatorProperty, ConfigurationSource configurationSource)
+ {
+ var oldDiscriminatorProperty = ((IReadOnlyTypeBase)Metadata).FindDiscriminatorProperty() as Property;
+ if (oldDiscriminatorProperty?.IsInModel == true
+ && oldDiscriminatorProperty != newDiscriminatorProperty)
+ {
+ oldDiscriminatorProperty.DeclaringType.Builder.RemoveUnusedImplicitProperties([oldDiscriminatorProperty]);
+
+ if (oldDiscriminatorProperty.IsInModel)
+ {
+ // TODO: Remove this once layering is implemented, #15898
+ oldDiscriminatorProperty.Builder.AfterSave(null, ConfigurationSource.Convention);
+ oldDiscriminatorProperty.Builder.IsRequired(null, ConfigurationSource.Convention);
+ oldDiscriminatorProperty.Builder.HasValueGenerator((Type?)null, ConfigurationSource.Convention);
+ }
+ }
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual bool CanSetDiscriminator(string? name, Type? type, ConfigurationSource configurationSource)
+ => name == null && type == null
+ ? CanRemoveDiscriminator(configurationSource)
+ : CanSetDiscriminator(((IReadOnlyTypeBase)Metadata).FindDiscriminatorProperty(), name, type, configurationSource);
+
+ private bool CanSetDiscriminator(
+ IReadOnlyProperty? discriminatorProperty,
+ string? name,
+ Type? discriminatorType,
+ ConfigurationSource configurationSource)
+ => ((name == null && discriminatorType == null)
+ || ((name == null || discriminatorProperty?.Name == name)
+ && (discriminatorType == null || discriminatorProperty?.ClrType == discriminatorType))
+ || configurationSource.Overrides(Metadata.GetRootType().GetDiscriminatorPropertyConfigurationSource()))
+ && (discriminatorProperty != null
+ || Metadata.GetRootType().Builder.CanAddDiscriminatorProperty(
+ discriminatorType ?? DefaultDiscriminatorType,
+ name ?? DefaultDiscriminatorName,
+ typeConfigurationSource: discriminatorType != null
+ ? configurationSource
+ : null));
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual bool CanRemoveDiscriminator(ConfigurationSource configurationSource)
+ => CanSetAnnotation(CoreAnnotationNames.DiscriminatorProperty, null, configurationSource);
+
+ private bool CanAddDiscriminatorProperty(
+ Type propertyType,
+ string name,
+ ConfigurationSource? typeConfigurationSource)
+ {
+ var conflictingProperty = Metadata.FindPropertiesInHierarchy(name).FirstOrDefault();
+ if (conflictingProperty != null
+ && (conflictingProperty.IsShadowProperty() || conflictingProperty.IsIndexerProperty())
+ && conflictingProperty.ClrType != propertyType
+ && typeConfigurationSource != null
+ && !typeConfigurationSource.Overrides(conflictingProperty.GetTypeConfigurationSource()))
+ {
+ return false;
+ }
+
+ var memberInfo = Metadata.IsPropertyBag
+ ? null
+ : Metadata.ClrType.GetMembersInHierarchy(name).FirstOrDefault();
+
+ return memberInfo == null
+ || propertyType == memberInfo.GetMemberType()
+ || typeConfigurationSource == null;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ protected virtual void RemoveIncompatibleDiscriminatorValues(
+ TypeBase structuralType,
+ Property? newDiscriminatorProperty,
+ ConfigurationSource configurationSource)
+ {
+ if ((newDiscriminatorProperty != null || structuralType.BaseType != null)
+ && (newDiscriminatorProperty == null
+ || newDiscriminatorProperty.ClrType.IsInstanceOfType(((IReadOnlyTypeBase)structuralType).GetDiscriminatorValue())))
+ {
+ return;
+ }
+
+ if (configurationSource.Overrides(((IConventionTypeBase)structuralType).GetDiscriminatorValueConfigurationSource()))
+ {
+ ((IMutableTypeBase)structuralType).RemoveDiscriminatorValue();
+ }
+
+ if (structuralType.BaseType == null)
+ {
+ foreach (var derivedType in structuralType.GetDerivedTypes())
+ {
+ if (configurationSource.Overrides(((IConventionTypeBase)derivedType).GetDiscriminatorValueConfigurationSource()))
+ {
+ ((IMutableTypeBase)derivedType).RemoveDiscriminatorValue();
+ }
+ }
+ }
+ }
+
IConventionTypeBase IConventionTypeBaseBuilder.Metadata
{
[DebuggerStepThrough]
@@ -1375,6 +1594,16 @@ IConventionTypeBase IConventionTypeBaseBuilder.Metadata
=> (IConventionTypeBaseBuilder?)base.HasNoAnnotation(
name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ bool IConventionTypeBaseBuilder.CanRemoveDiscriminator(bool fromDataAnnotation)
+ => CanRemoveDiscriminator(fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -1768,4 +1997,52 @@ bool IConventionTypeBaseBuilder.CanSetChangeTrackingStrategy(ChangeTrackingStrat
bool IConventionTypeBaseBuilder.CanSetPropertyAccessMode(PropertyAccessMode? propertyAccessMode, bool fromDataAnnotation)
=> CanSetPropertyAccessMode(
propertyAccessMode, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ bool IConventionTypeBaseBuilder.CanSetDiscriminator(string name, bool fromDataAnnotation)
+ => CanSetDiscriminator(
+ name, type: null,
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ bool IConventionTypeBaseBuilder.CanSetDiscriminator(Type type, bool fromDataAnnotation)
+ => CanSetDiscriminator(
+ name: null, type,
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ bool IConventionTypeBaseBuilder.CanSetDiscriminator(string name, Type type, bool fromDataAnnotation)
+ => CanSetDiscriminator(
+ name, type,
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ bool IConventionTypeBaseBuilder.CanSetDiscriminator(MemberInfo memberInfo, bool fromDataAnnotation)
+ => CanSetDiscriminator(
+ Check.NotNull(memberInfo, nameof(memberInfo)).GetSimpleMemberName(), memberInfo.GetMemberType(),
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
}
diff --git a/src/EFCore/Metadata/Internal/TypeBase.cs b/src/EFCore/Metadata/Internal/TypeBase.cs
index 720db7259b9..e654a0e2f7e 100644
--- a/src/EFCore/Metadata/Internal/TypeBase.cs
+++ b/src/EFCore/Metadata/Internal/TypeBase.cs
@@ -155,7 +155,7 @@ public virtual InternalTypeBaseBuilder Builder
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- protected virtual TypeBase? BaseType
+ public virtual TypeBase? BaseType
{
get => _baseType;
set => _baseType = value;
@@ -167,9 +167,25 @@ protected virtual TypeBase? BaseType
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- protected virtual SortedSet DirectlyDerivedTypes
+ public virtual SortedSet DirectlyDerivedTypes
=> _directlyDerivedTypes;
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public abstract TypeBase? SetBaseType(TypeBase? newBaseType, ConfigurationSource configurationSource);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public abstract ConfigurationSource? GetBaseTypeConfigurationSource();
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -190,7 +206,7 @@ protected virtual IEnumerable GetDerivedTypes()
{
if (DirectlyDerivedTypes.Count == 0)
{
- return Enumerable.Empty();
+ return [];
}
var derivedTypes = new List();
@@ -217,7 +233,7 @@ protected virtual IEnumerable GetDerivedTypes()
[DebuggerStepThrough]
public virtual IEnumerable GetDerivedTypesInclusive()
=> DirectlyDerivedTypes.Count == 0
- ? new[] { this }
+ ? [this]
: new[] { this }.Concat(GetDerivedTypes());
///
@@ -254,6 +270,16 @@ public virtual bool IsAssignableFrom(TypeBase derivedType)
return false;
}
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ public virtual TypeBase GetRootType()
+ => (TypeBase)((IReadOnlyTypeBase)this).GetRootType();
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -344,6 +370,65 @@ public virtual void UpdateConfigurationSource(ConfigurationSource configurationS
return null;
}
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public virtual Property? SetDiscriminatorProperty(Property? property, ConfigurationSource configurationSource)
+ {
+ if ((string?)this[CoreAnnotationNames.DiscriminatorProperty] == property?.Name)
+ {
+ return property;
+ }
+
+ CheckDiscriminatorProperty(property);
+
+ SetAnnotation(CoreAnnotationNames.DiscriminatorProperty, property?.Name, configurationSource);
+
+ return Model.ConventionDispatcher.OnDiscriminatorPropertySet(Builder, property?.Name) == property?.Name
+ ? property
+ : (Property?)((IReadOnlyEntityType)this).FindDiscriminatorProperty();
+ }
+
+ private void CheckDiscriminatorProperty(Property? property)
+ {
+ if (property != null)
+ {
+ if (BaseType != null)
+ {
+ throw new InvalidOperationException(
+ CoreStrings.DiscriminatorPropertyMustBeOnRoot(DisplayName()));
+ }
+
+ if (property.DeclaringType != this)
+ {
+ throw new InvalidOperationException(
+ CoreStrings.DiscriminatorPropertyNotFound(property.Name, DisplayName()));
+ }
+ }
+ }
+
+ ///
+ /// Returns the name of the property that will be used for storing a discriminator value.
+ ///
+ /// The name of the property that will be used for storing a discriminator value.
+ public virtual string? GetDiscriminatorPropertyName()
+ => BaseType is null
+ ? (string?)this[CoreAnnotationNames.DiscriminatorProperty]
+ : ((IReadOnlyEntityType)this).GetRootType().GetDiscriminatorPropertyName();
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ public virtual ConfigurationSource? GetDiscriminatorPropertyConfigurationSource()
+ => FindAnnotation(CoreAnnotationNames.DiscriminatorProperty)?.GetConfigurationSource();
+
#region Properties
///
@@ -1447,6 +1532,54 @@ Type IReadOnlyTypeBase.ClrType
get => ClrType;
}
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ IReadOnlyTypeBase? IReadOnlyTypeBase.BaseType
+ {
+ [DebuggerStepThrough]
+ get => BaseType;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ IMutableTypeBase? IMutableTypeBase.BaseType
+ {
+ get => BaseType;
+ set => SetBaseType((TypeBase?)value, ConfigurationSource.Explicit);
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ IConventionTypeBase? IConventionTypeBase.BaseType
+ {
+ [DebuggerStepThrough]
+ get => BaseType;
+ }
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ ITypeBase? ITypeBase.BaseType
+ {
+ [DebuggerStepThrough]
+ get => BaseType;
+ }
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -1479,6 +1612,71 @@ IConventionTypeBaseBuilder IConventionTypeBase.Builder
string? IConventionTypeBase.AddIgnored(string name, bool fromDataAnnotation)
=> AddIgnored(name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ IConventionTypeBase? IConventionTypeBase.SetBaseType(IConventionTypeBase? structuralType, bool fromDataAnnotation)
+ => SetBaseType(
+ (TypeBase?)structuralType, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ IEnumerable IReadOnlyTypeBase.GetDerivedTypes()
+ => GetDerivedTypes();
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ IEnumerable IReadOnlyTypeBase.GetDirectlyDerivedTypes()
+ => DirectlyDerivedTypes;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ IEnumerable ITypeBase.GetDirectlyDerivedTypes()
+ => DirectlyDerivedTypes;
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ void IMutableTypeBase.SetDiscriminatorProperty(IReadOnlyProperty? property)
+ => SetDiscriminatorProperty((Property?)property, ConfigurationSource.Explicit);
+
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ [DebuggerStepThrough]
+ IConventionProperty? IConventionTypeBase.SetDiscriminatorProperty(
+ IReadOnlyProperty? property,
+ bool fromDataAnnotation)
+ => SetDiscriminatorProperty(
+ (Property?)property,
+ fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
diff --git a/src/EFCore/Metadata/RuntimeComplexProperty.cs b/src/EFCore/Metadata/RuntimeComplexProperty.cs
index e8b95bbeafb..d00da612c07 100644
--- a/src/EFCore/Metadata/RuntimeComplexProperty.cs
+++ b/src/EFCore/Metadata/RuntimeComplexProperty.cs
@@ -37,6 +37,8 @@ public RuntimeComplexProperty(
ChangeTrackingStrategy changeTrackingStrategy,
PropertyInfo? indexerPropertyInfo,
bool propertyBag,
+ string? discriminatorProperty,
+ object? discriminatorValue,
int propertyCount,
int complexPropertyCount)
: base(name, propertyInfo, fieldInfo, propertyAccessMode)
@@ -47,6 +49,7 @@ public RuntimeComplexProperty(
_isCollection = collection;
ComplexType = new RuntimeComplexType(
targetTypeName, targetType, this, changeTrackingStrategy, indexerPropertyInfo, propertyBag,
+ discriminatorProperty, discriminatorValue,
propertyCount: propertyCount,
complexPropertyCount: complexPropertyCount);
}
diff --git a/src/EFCore/Metadata/RuntimeComplexType.cs b/src/EFCore/Metadata/RuntimeComplexType.cs
index 0fd2c553b67..0c769ff8ede 100644
--- a/src/EFCore/Metadata/RuntimeComplexType.cs
+++ b/src/EFCore/Metadata/RuntimeComplexType.cs
@@ -34,10 +34,13 @@ public RuntimeComplexType(
ChangeTrackingStrategy changeTrackingStrategy,
PropertyInfo? indexerPropertyInfo,
bool propertyBag,
+ string? discriminatorProperty,
+ object? discriminatorValue,
int propertyCount,
int complexPropertyCount)
: base(
- name, type, complexProperty.DeclaringType.Model, null, changeTrackingStrategy, indexerPropertyInfo, propertyBag,
+ name, type, complexProperty.DeclaringType.Model, baseType: null, changeTrackingStrategy, indexerPropertyInfo, propertyBag,
+ discriminatorProperty, discriminatorValue,
derivedTypesCount: 0,
propertyCount: propertyCount,
complexPropertyCount: complexPropertyCount)
@@ -51,6 +54,9 @@ public RuntimeComplexType(
};
}
+ private new RuntimeComplexType? BaseType
+ => (RuntimeComplexType?)base.BaseType;
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -241,4 +247,39 @@ IEntityType ITypeBase.ContainingEntityType
[DebuggerStepThrough]
get => ContainingEntityType;
}
+
+ ///
+ IReadOnlyComplexType? IReadOnlyComplexType.BaseType
+ {
+ [DebuggerStepThrough]
+ get => BaseType;
+ }
+
+ ///
+ IComplexType? IComplexType.BaseType
+ {
+ [DebuggerStepThrough]
+ get => BaseType;
+ }
+
+ ///
+ [DebuggerStepThrough]
+ IEnumerable IReadOnlyComplexType.GetDerivedTypes()
+ => GetDerivedTypes();
+
+ ///
+ IEnumerable IReadOnlyComplexType.GetDerivedTypesInclusive()
+ => !HasDirectlyDerivedTypes
+ ? [this]
+ : new[] { this }.Concat(GetDerivedTypes());
+
+ ///
+ [DebuggerStepThrough]
+ IEnumerable IReadOnlyComplexType.GetDirectlyDerivedTypes()
+ => DirectlyDerivedTypes.Cast();
+
+ ///
+ [DebuggerStepThrough]
+ IEnumerable IComplexType.GetDirectlyDerivedTypes()
+ => DirectlyDerivedTypes.Cast();
}
diff --git a/src/EFCore/Metadata/RuntimeEntityType.cs b/src/EFCore/Metadata/RuntimeEntityType.cs
index e9516734cea..d6d9c18e7e4 100644
--- a/src/EFCore/Metadata/RuntimeEntityType.cs
+++ b/src/EFCore/Metadata/RuntimeEntityType.cs
@@ -25,7 +25,6 @@ public class RuntimeEntityType : RuntimeTypeBase, IRuntimeEntityType
private Utilities.OrderedDictionary? _namedIndexes;
private readonly Utilities.OrderedDictionary, RuntimeKey> _keys;
private Utilities.OrderedDictionary? _triggers;
- private readonly object? _discriminatorValue;
private readonly bool _hasSharedClrType;
private RuntimeKey? _primaryKey;
private InstantiationBinding? _constructorBinding;
@@ -58,10 +57,10 @@ public RuntimeEntityType(
bool sharedClrType,
RuntimeModel model,
RuntimeEntityType? baseType,
- string? discriminatorProperty,
ChangeTrackingStrategy changeTrackingStrategy,
PropertyInfo? indexerPropertyInfo,
bool propertyBag,
+ string? discriminatorProperty,
object? discriminatorValue,
int derivedTypesCount,
int propertyCount,
@@ -76,14 +75,12 @@ public RuntimeEntityType(
int triggerCount)
: base(
name, type, model, baseType, changeTrackingStrategy, indexerPropertyInfo, propertyBag,
+ discriminatorProperty, discriminatorValue,
derivedTypesCount: derivedTypesCount,
propertyCount: propertyCount,
complexPropertyCount: complexPropertyCount)
{
_hasSharedClrType = sharedClrType;
-
- SetAnnotation(CoreAnnotationNames.DiscriminatorProperty, discriminatorProperty);
- _discriminatorValue = discriminatorValue;
_foreignKeys = new List(foreignKeyCount);
_navigations = new Utilities.OrderedDictionary(navigationCount, StringComparer.Ordinal);
if (skipNavigationCount > 0)
@@ -327,7 +324,7 @@ public virtual IEnumerable FindDeclaredForeignKeys(IReadOnlyL
private RuntimeForeignKey? FindDeclaredForeignKey(
IReadOnlyList properties,
IReadOnlyKey principalKey,
- IReadOnlyEntityType principalEntityType)
+ IReadOnlyTypeBase principalEntityType)
{
if (_foreignKeys.Count == 0)
{
@@ -949,23 +946,6 @@ public virtual DebugView DebugView
LambdaExpression? IReadOnlyEntityType.GetQueryFilter()
=> (LambdaExpression?)this[CoreAnnotationNames.QueryFilter];
- ///
- [DebuggerStepThrough]
- string? IReadOnlyEntityType.GetDiscriminatorPropertyName()
- {
- if (BaseType != null)
- {
- return ((IReadOnlyEntityType)this).GetRootType().GetDiscriminatorPropertyName();
- }
-
- return (string?)this[CoreAnnotationNames.DiscriminatorProperty];
- }
-
- ///
- [DebuggerStepThrough]
- object? IReadOnlyEntityType.GetDiscriminatorValue()
- => _discriminatorValue;
-
///
bool IReadOnlyTypeBase.HasSharedClrType
{
@@ -1009,7 +989,7 @@ IEnumerable IReadOnlyEntityType.GetDerivedTypes()
///
IEnumerable IReadOnlyEntityType.GetDerivedTypesInclusive()
=> !HasDirectlyDerivedTypes
- ? new[] { this }
+ ? [this]
: new[] { this }.Concat(GetDerivedTypes());
///
diff --git a/src/EFCore/Metadata/RuntimeModel.cs b/src/EFCore/Metadata/RuntimeModel.cs
index 3b52e587521..bab0de72d9f 100644
--- a/src/EFCore/Metadata/RuntimeModel.cs
+++ b/src/EFCore/Metadata/RuntimeModel.cs
@@ -98,13 +98,13 @@ public virtual void SetSkipDetectChanges(bool skipDetectChanges)
/// The CLR class that is used to represent instances of this type.
/// Whether this entity type can share its ClrType with other entities.
/// The base type of this entity type.
- /// The name of the property that will be used for storing a discriminator value.
/// The change tracking strategy for this entity type.
/// The for the indexer on the associated CLR type if one exists.
///
/// A value indicating whether this entity type has an indexer which is able to contain arbitrary properties
/// and a method that can be used to determine whether a given indexer property contains a value.
///
+ /// The name of the property that will be used for storing a discriminator value.
/// The discriminator value for this entity type.
/// The expected number of directly derived entity types.
/// The expected number of declared properties for this entity type.
@@ -123,10 +123,10 @@ public virtual RuntimeEntityType AddEntityType(
[DynamicallyAccessedMembers(IEntityType.DynamicallyAccessedMemberTypes)] Type type,
RuntimeEntityType? baseType = null,
bool sharedClrType = false,
- string? discriminatorProperty = null,
ChangeTrackingStrategy changeTrackingStrategy = ChangeTrackingStrategy.Snapshot,
PropertyInfo? indexerPropertyInfo = null,
bool propertyBag = false,
+ string? discriminatorProperty = null,
object? discriminatorValue = null,
int derivedTypesCount = 0,
int propertyCount = 0,
@@ -146,10 +146,10 @@ public virtual RuntimeEntityType AddEntityType(
sharedClrType,
this,
baseType,
- discriminatorProperty,
changeTrackingStrategy,
indexerPropertyInfo,
propertyBag,
+ discriminatorProperty,
discriminatorValue,
derivedTypesCount: derivedTypesCount,
propertyCount: propertyCount,
diff --git a/src/EFCore/Metadata/RuntimeTypeBase.cs b/src/EFCore/Metadata/RuntimeTypeBase.cs
index c3c6676699a..64fbb264fb6 100644
--- a/src/EFCore/Metadata/RuntimeTypeBase.cs
+++ b/src/EFCore/Metadata/RuntimeTypeBase.cs
@@ -19,6 +19,7 @@ public abstract class RuntimeTypeBase : RuntimeAnnotatableBase, IRuntimeTypeBase
private RuntimeModel _model;
private readonly RuntimeTypeBase? _baseType;
private SortedSet? _directlyDerivedTypes;
+ private readonly object? _discriminatorValue;
private readonly Utilities.OrderedDictionary _properties;
private Utilities.OrderedDictionary? _complexProperties;
private readonly PropertyInfo? _indexerPropertyInfo;
@@ -45,6 +46,8 @@ protected RuntimeTypeBase(
ChangeTrackingStrategy changeTrackingStrategy,
PropertyInfo? indexerPropertyInfo,
bool propertyBag,
+ string? discriminatorProperty,
+ object? discriminatorValue,
int derivedTypesCount,
int propertyCount,
int complexPropertyCount)
@@ -60,7 +63,11 @@ protected RuntimeTypeBase(
_changeTrackingStrategy = changeTrackingStrategy;
_indexerPropertyInfo = indexerPropertyInfo;
+ _discriminatorValue = discriminatorValue;
_isPropertyBag = propertyBag;
+ if(discriminatorProperty != null) {
+ SetAnnotation(CoreAnnotationNames.DiscriminatorProperty, discriminatorProperty);
+ }
_properties = new Utilities.OrderedDictionary(propertyCount, new PropertyNameComparer(this));
if (complexPropertyCount > 0)
{
@@ -129,7 +136,7 @@ protected virtual IEnumerable GetDerivedTypes()
{
if (!HasDirectlyDerivedTypes)
{
- return Enumerable.Empty();
+ return [];
}
var derivedTypes = new List();
@@ -363,6 +370,8 @@ protected virtual IEnumerable GetProperties()
/// A value indicating whether this entity type has an indexer which is able to contain arbitrary properties
/// and a method that can be used to determine whether a given indexer property contains a value.
///
+ /// The name of the property that will be used for storing a discriminator value.
+ /// The discriminator value for this complex type.
/// The expected number of declared properties for this complex type.
/// The expected number of declared complex properties for this complex type.
/// The newly created property.
@@ -379,6 +388,8 @@ public virtual RuntimeComplexProperty AddComplexProperty(
ChangeTrackingStrategy changeTrackingStrategy = ChangeTrackingStrategy.Snapshot,
PropertyInfo? indexerPropertyInfo = null,
bool propertyBag = false,
+ string? discriminatorProperty = null,
+ object? discriminatorValue = null,
int propertyCount = 0,
int complexPropertyCount = 0)
{
@@ -396,6 +407,8 @@ public virtual RuntimeComplexProperty AddComplexProperty(
changeTrackingStrategy,
indexerPropertyInfo,
propertyBag,
+ discriminatorProperty,
+ discriminatorValue,
propertyCount: propertyCount,
complexPropertyCount: complexPropertyCount);
@@ -428,8 +441,8 @@ public virtual IEnumerable GetDeclaredComplexProperties(
private IEnumerable GetDerivedComplexProperties()
=> !HasDirectlyDerivedTypes
- ? Enumerable.Empty()
- : GetDerivedTypes().Cast().SelectMany(et => et.GetDeclaredComplexProperties());
+ ? []
+ : GetDerivedTypes().SelectMany(et => et.GetDeclaredComplexProperties());
///
/// Gets the complex properties defined on this type.
@@ -459,7 +472,7 @@ private IEnumerable FindDerivedComplexProperties(string
Check.NotNull(propertyName, nameof(propertyName));
return !HasDirectlyDerivedTypes
- ? Enumerable.Empty()
+ ? []
: (IEnumerable)GetDerivedTypes()
.Select(et => et.FindDeclaredComplexProperty(propertyName)).Where(p => p != null);
}
@@ -607,8 +620,43 @@ public virtual void FinalizeType()
protected static IEnumerable ToEnumerable(T? element)
where T : class
=> element == null
- ? Enumerable.Empty()
- : new[] { element };
+ ? []
+ : [element];
+
+ ///
+ IReadOnlyTypeBase? IReadOnlyTypeBase.BaseType
+ {
+ [DebuggerStepThrough]
+ get => BaseType;
+ }
+
+ ///
+ ITypeBase? ITypeBase.BaseType
+ {
+ [DebuggerStepThrough]
+ get => BaseType;
+ }
+
+ ///
+ [DebuggerStepThrough]
+ IEnumerable IReadOnlyTypeBase.GetDerivedTypes()
+ => GetDerivedTypes();
+
+ ///
+ IEnumerable IReadOnlyTypeBase.GetDerivedTypesInclusive()
+ => !HasDirectlyDerivedTypes
+ ? [this]
+ : new[] { this }.Concat(GetDerivedTypes());
+
+ ///
+ [DebuggerStepThrough]
+ IEnumerable IReadOnlyTypeBase.GetDirectlyDerivedTypes()
+ => DirectlyDerivedTypes.Cast();
+
+ ///
+ [DebuggerStepThrough]
+ IEnumerable ITypeBase.GetDirectlyDerivedTypes()
+ => DirectlyDerivedTypes.Cast();
///
bool IReadOnlyTypeBase.HasSharedClrType
@@ -638,6 +686,23 @@ IModel ITypeBase.Model
get => Model;
}
+ ///
+ [DebuggerStepThrough]
+ string? IReadOnlyTypeBase.GetDiscriminatorPropertyName()
+ {
+ if (BaseType != null)
+ {
+ return ((IReadOnlyTypeBase)this).GetRootType().GetDiscriminatorPropertyName();
+ }
+
+ return (string?)this[CoreAnnotationNames.DiscriminatorProperty];
+ }
+
+ ///
+ [DebuggerStepThrough]
+ object? IReadOnlyTypeBase.GetDiscriminatorValue()
+ => _discriminatorValue;
+
///
[DebuggerStepThrough]
IReadOnlyProperty? IReadOnlyTypeBase.FindDeclaredProperty(string name)
diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
index 0c4d3874bfe..5752032680e 100644
--- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
+++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
@@ -200,9 +200,8 @@ protected override Expression VisitExtension(Expression extensionExpression)
{
case EntityQueryRootExpression entityQueryRootExpression:
var entityType = entityQueryRootExpression.EntityType;
-#pragma warning disable CS0618 // Type or member is obsolete
- var definingQuery = entityType.GetDefiningQuery();
-#pragma warning restore CS0618 // Type or member is obsolete
+ // TODO: Move to InMemory #21624
+ var definingQuery = (LambdaExpression?)entityType["InMemory:DefiningQuery"];
NavigationExpansionExpression navigationExpansionExpression;
if (definingQuery != null
// Apply defining query only when it is not custom query root
@@ -1120,15 +1119,14 @@ private NavigationExpansionExpression ProcessInclude(
{
if (source.PendingSelector is NavigationTreeExpression { Value: EntityReference entityReference })
{
-#pragma warning disable CS0618 // Type or member is obsolete
- if (entityReference.EntityType.GetDefiningQuery() != null)
+ // TODO: Move to InMemory #21624
+ if (entityReference.EntityType["InMemory:DefiningQuery"] != null)
{
throw new InvalidOperationException(
#pragma warning disable CS0612 // Type or member is obsolete
CoreStrings.IncludeOnEntityWithDefiningQueryNotSupported(expression, entityReference.EntityType.DisplayName()));
#pragma warning restore CS0612 // Type or member is obsolete
}
-#pragma warning restore CS0618 // Type or member is obsolete
if (expression is ConstantExpression { Value: string navigationChain })
{
diff --git a/src/EFCore/Query/StructuralTypeShaperExpression.cs b/src/EFCore/Query/StructuralTypeShaperExpression.cs
index 5a41c40663e..10c73ba14c0 100644
--- a/src/EFCore/Query/StructuralTypeShaperExpression.cs
+++ b/src/EFCore/Query/StructuralTypeShaperExpression.cs
@@ -21,10 +21,10 @@ namespace Microsoft.EntityFrameworkCore.Query;
public class StructuralTypeShaperExpression : Expression, IPrintableExpression
{
private static readonly MethodInfo CreateUnableToDiscriminateExceptionMethod
- = typeof(StructuralTypeShaperExpression).GetTypeInfo().GetDeclaredMethod(nameof(CreateUnableToDiscriminateException))!;
+ = typeof(StructuralTypeShaperExpression).GetMethod(nameof(CreateUnableToDiscriminateException))!;
private static readonly MethodInfo GetDiscriminatorValueMethod
- = typeof(IReadOnlyEntityType).GetTypeInfo().GetDeclaredMethod(nameof(IReadOnlyEntityType.GetDiscriminatorValue))!;
+ = typeof(IReadOnlyTypeBase).GetMethod(nameof(IReadOnlyTypeBase.GetDiscriminatorValue))!;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -176,7 +176,7 @@ protected virtual LambdaExpression GenerateMaterializationCondition(ITypeBase ty
expressions.Add(conditions);
}
- body = Block(new[] { discriminatorValueVariable }, expressions);
+ body = Block([discriminatorValueVariable], expressions);
}
else
{
diff --git a/test/Directory.Packages.props b/test/Directory.Packages.props
index de8a6dc1cf3..59f7d4a7b5c 100644
--- a/test/Directory.Packages.props
+++ b/test/Directory.Packages.props
@@ -1,11 +1,13 @@
+
+
-
-
-
+
+
+
+
-
@@ -14,19 +16,13 @@
-
+
-
-
-
-
-
-
diff --git a/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Tests.csproj b/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Tests.csproj
index 084a382f3ab..c763d148fe9 100644
--- a/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Tests.csproj
+++ b/test/EFCore.Analyzers.Tests/EFCore.Analyzers.Tests.csproj
@@ -6,6 +6,8 @@
Microsoft.EntityFrameworkCoretruetrue
+
+ $(NoWarn);NU1608
@@ -45,10 +47,10 @@
-
-
-
-
+
+
+
+
diff --git a/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs
index 1daeec31d64..fbca3930f57 100644
--- a/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/EndToEndCosmosTest.cs
@@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore;
#nullable disable
-public class EndToEndCosmosTest : NonSharedModelTestBase
+public class EndToEndCosmosTest(NonSharedFixture fixture) : NonSharedModelTestBase(fixture), IClassFixture
{
[ConditionalFact]
public async Task Can_add_update_delete_end_to_end()
@@ -27,7 +27,6 @@ public async Task Can_add_update_delete_end_to_end()
using (var context = contextFactory.CreateContext())
{
ListLoggerFactory.Clear();
- context.Database.EnsureCreated();
context.Add(customer);
@@ -37,7 +36,7 @@ public async Task Can_add_update_delete_end_to_end()
Assert.Equal(LogLevel.Information, logEntry.Level);
Assert.Contains("CreateItem", logEntry.Message);
- Assert.Equal(3, ListLoggerFactory.Log.Count(l => l.Id == CosmosEventId.SyncNotSupported));
+ Assert.Equal(1, ListLoggerFactory.Log.Count(l => l.Id == CosmosEventId.SyncNotSupported));
}
using (var context = contextFactory.CreateContext())
@@ -111,8 +110,6 @@ public async Task Can_add_update_delete_end_to_end_async()
using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
await context.AddAsync(customer);
await context.SaveChangesAsync();
@@ -189,8 +186,6 @@ public async Task Can_add_update_delete_detached_entity_end_to_end_async()
string storeId = null;
using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
var entry = await context.AddAsync(customer);
await context.SaveChangesAsync();
@@ -257,102 +252,6 @@ public async Task Can_add_update_delete_detached_entity_end_to_end_async()
}
}
- [ConditionalFact]
- public async Task Can_add_update_untracked_properties()
- {
- var contextFactory = await InitializeAsync(
- b => b.Entity(),
- shouldLogCategory: _ => true,
- onConfiguring: o => o.ConfigureWarnings(w => w.Log(CosmosEventId.SyncNotSupported, CosmosEventId.NoPartitionKeyDefined)));
-
- var customer = new Customer { Id = 42, Name = "Theon" };
-
- using (var context = contextFactory.CreateContext())
- {
- context.Database.EnsureCreated();
-
- var entry = context.Add(customer);
-
- context.SaveChanges();
-
- var document = entry.Property("__jObject").CurrentValue;
- Assert.NotNull(document);
- Assert.Equal("Theon", document["Name"]);
-
- context.Remove(customer);
-
- context.SaveChanges();
-
- Assert.Equal(4, ListLoggerFactory.Log.Count(l => l.Id == CosmosEventId.SyncNotSupported));
- }
-
- using (var context = contextFactory.CreateContext())
- {
- Assert.Empty(context.Set().ToList());
-
- var entry = context.Add(customer);
-
- entry.Property("__jObject").CurrentValue = new JObject { ["key1"] = "value1" };
-
- context.SaveChanges();
-
- var document = entry.Property("__jObject").CurrentValue;
- Assert.NotNull(document);
- Assert.Equal("Theon", document["Name"]);
- Assert.Equal("value1", document["key1"]);
-
- document["key2"] = "value2";
- entry.State = EntityState.Modified;
- context.SaveChanges();
-
- Assert.Equal(7, ListLoggerFactory.Log.Count(l => l.Id == CosmosEventId.SyncNotSupported));
- }
-
- using (var context = contextFactory.CreateContext())
- {
- var customerFromStore = context.Set().Single();
-
- Assert.Equal(42, customerFromStore.Id);
- Assert.Equal("Theon", customerFromStore.Name);
-
- var entry = context.Entry(customerFromStore);
- var document = entry.Property("__jObject").CurrentValue;
- Assert.Equal("value1", document["key1"]);
- Assert.Equal("value2", document["key2"]);
-
- document["key1"] = "value1.1";
- customerFromStore.Name = "Theon Greyjoy";
-
- context.SaveChanges();
-
- Assert.Equal(9, ListLoggerFactory.Log.Count(l => l.Id == CosmosEventId.SyncNotSupported));
- }
-
- using (var context = contextFactory.CreateContext())
- {
- var customerFromStore = context.Set().Single();
-
- Assert.Equal("Theon Greyjoy", customerFromStore.Name);
-
- var entry = context.Entry(customerFromStore);
- var document = entry.Property("__jObject").CurrentValue;
- Assert.Equal("value1.1", document["key1"]);
- Assert.Equal("value2", document["key2"]);
-
- context.Remove(customerFromStore);
-
- context.SaveChanges();
-
- Assert.Equal(11, ListLoggerFactory.Log.Count(l => l.Id == CosmosEventId.SyncNotSupported));
- }
-
- using (var context = contextFactory.CreateContext())
- {
- Assert.Empty(context.Set().ToList());
- Assert.Equal(12, ListLoggerFactory.Log.Count(l => l.Id == CosmosEventId.SyncNotSupported));
- }
- }
-
[ConditionalFact]
public async Task Can_add_update_untracked_properties_async()
{
@@ -365,8 +264,6 @@ public async Task Can_add_update_untracked_properties_async()
using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
var entry = await context.AddAsync(customer);
await context.SaveChangesAsync();
@@ -472,8 +369,6 @@ public async Task Can_add_update_delete_end_to_end_with_Guid_async()
using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
await context.AddAsync(customer);
await context.SaveChangesAsync();
@@ -540,8 +435,6 @@ public async Task Can_add_update_delete_end_to_end_with_DateTime_async()
using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
var entry = await context.AddAsync(customer);
Assert.Equal("0001-01-01T00:00:00.0000000|Theon^2F^5C^23^5C^5C^3F", entry.CurrentValues["__id"]);
@@ -636,8 +529,6 @@ public async Task Can_add_update_delete_with_dateTime_string_end_to_end_async()
using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
await context.AddAsync(customer);
await context.SaveChangesAsync();
@@ -983,8 +874,6 @@ private async Task Can_add_update_delete_with_collection(
using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
await context.AddAsync(customer);
await context.SaveChangesAsync();
@@ -1060,8 +949,6 @@ public async Task Can_read_with_find_with_resource_id_async()
await using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
Assert.Null(
context.Model.FindEntityType(typeof(CustomerWithResourceId))!
.FindProperty(CosmosJsonIdConvention.DefaultIdPropertyName));
@@ -1108,70 +995,6 @@ await context.AddAsync(
}
}
- [ConditionalFact]
- public async Task Can_read_with_find_with_resource_id()
- {
- var contextFactory = await InitializeAsync(
- shouldLogCategory: _ => true,
- onConfiguring: o => o.ConfigureWarnings(w => w.Log(CosmosEventId.SyncNotSupported)));
-
- const int pk1 = 1;
- const int pk2 = 2;
-
- var customer = new CustomerWithResourceId
- {
- id = "42",
- Name = "Theon",
- PartitionKey1 = pk1,
- PartitionKey2 = 3.15m
- };
-
- using (var context = contextFactory.CreateContext())
- {
- context.Database.EnsureCreated();
-
- context.Add(customer);
- context.Add(
- new CustomerWithResourceId
- {
- id = "42",
- Name = "Theon Twin",
- PartitionKey1 = pk2,
- PartitionKey2 = 3.15m
- });
-
- context.SaveChanges();
- }
-
- using (var context = contextFactory.CreateContext())
- {
- var customerFromStore = context.Set()
- .Find(pk1, 3.15m, "42");
-
- Assert.Equal("42", customerFromStore.id);
- Assert.Equal("Theon", customerFromStore.Name);
- Assert.Equal(pk1, customerFromStore.PartitionKey1);
- Assert.Equal(3.15m, customerFromStore.PartitionKey2);
- AssertSql(context, """ReadItem([1.0,3.15], 42)""");
-
- customerFromStore.Name = "Theon Greyjoy";
-
- context.SaveChanges();
- }
-
- using (var context = contextFactory.CreateContext())
- {
- var customerFromStore = context.Set()
- .WithPartitionKey(pk1, 3.15m)
- .Single();
-
- Assert.Equal("42", customerFromStore.id);
- Assert.Equal("Theon Greyjoy", customerFromStore.Name);
- Assert.Equal(pk1, customerFromStore.PartitionKey1);
- Assert.Equal(3.15m, customerFromStore.PartitionKey2);
- }
- }
-
[ConditionalFact]
public async Task Find_with_empty_resource_id_throws()
{
@@ -1211,8 +1034,6 @@ public async Task Can_read_with_find_with_partition_key_and_value_generator_asyn
await using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
await context.AddAsync(customer);
await context.AddAsync(
new Customer
@@ -1257,75 +1078,6 @@ await context.AddAsync(
}
}
- [ConditionalFact]
- public async Task Can_read_with_find_with_partition_key_and_value_generator()
- {
- var contextFactory = await InitializeAsync(
- shouldLogCategory: _ => true,
- onConfiguring: o => o.ConfigureWarnings(w => w.Log(CosmosEventId.SyncNotSupported)),
- addServices: s => s.AddSingleton());
-
- const int pk1 = 1;
- const int pk2 = 2;
-
- var customer = new Customer
- {
- Id = 42,
- Name = "Theon",
- PartitionKey1 = pk1,
- PartitionKey2 = "One",
- PartitionKey3 = true
- };
-
- using (var context = contextFactory.CreateContext())
- {
- context.Database.EnsureCreated();
-
- context.Add(customer);
- context.Add(
- new Customer
- {
- Id = 42,
- Name = "Theon Twin",
- PartitionKey1 = pk2,
- PartitionKey2 = "Two",
- PartitionKey3 = false
- });
-
- context.SaveChanges();
- }
-
- using (var context = contextFactory.CreateContext())
- {
- var customerFromStore = context.Set()
- .Find(pk1, 42, "One", true);
-
- Assert.Equal(42, customerFromStore.Id);
- Assert.Equal("Theon", customerFromStore.Name);
- Assert.Equal(pk1, customerFromStore.PartitionKey1);
- Assert.Equal("One", customerFromStore.PartitionKey2);
- Assert.True(customerFromStore.PartitionKey3);
- AssertSql(context, """ReadItem([1.0,"One",true], Customer-42)""");
-
- customerFromStore.Name = "Theon Greyjoy";
-
- context.SaveChanges();
- }
-
- using (var context = contextFactory.CreateContext())
- {
- var customerFromStore = context.Set()
- .WithPartitionKey(pk1, "One", true)
- .Single();
-
- Assert.Equal(42, customerFromStore.Id);
- Assert.Equal("Theon Greyjoy", customerFromStore.Name);
- Assert.Equal(pk1, customerFromStore.PartitionKey1);
- Assert.Equal("One", customerFromStore.PartitionKey2);
- Assert.True(customerFromStore.PartitionKey3);
- }
- }
-
[ConditionalFact]
public async Task Can_read_with_find_with_partition_key_without_value_generator()
{
@@ -1346,8 +1098,6 @@ public async Task Can_read_with_find_with_partition_key_without_value_generator(
using (var context = contextFactory.CreateContext())
{
- context.Database.EnsureCreated();
-
var customerEntry = context.Entry(customer);
customerEntry.Property(CosmosJsonIdConvention.DefaultIdPropertyName).CurrentValue = "42";
customerEntry.State = EntityState.Added;
@@ -1436,8 +1186,6 @@ public async Task Can_read_with_find_without_partition_key()
await using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
await context.AddAsync(customer);
await context.SaveChangesAsync();
@@ -1464,8 +1212,6 @@ public async Task Can_read_with_find_with_PK_partition_key()
await using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
await context.AddAsync(customer);
await context.SaveChangesAsync();
@@ -1492,8 +1238,6 @@ public async Task Can_read_with_find_with_PK_resource_id()
await using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
await context.AddAsync(customer);
await context.SaveChangesAsync();
@@ -1659,8 +1403,6 @@ public async Task Can_use_detached_entities_without_discriminators()
using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
await context.AddAsync(customer);
await context.SaveChangesAsync();
@@ -1710,8 +1452,6 @@ public async Task Can_update_unmapped_properties()
using (var context = contextFactory.CreateContext())
{
- context.Database.EnsureCreated();
-
var entry = context.Add(customer);
entry.Property("EMail").CurrentValue = "theon.g@winterfell.com";
@@ -1771,8 +1511,6 @@ public async Task Can_use_non_persisted_properties()
using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
await context.AddAsync(customer);
await context.SaveChangesAsync();
@@ -1861,8 +1599,6 @@ public async Task Using_a_conflicting_incompatible_id_throws()
await Assert.ThrowsAnyAsync(
async () =>
{
- await context.Database.EnsureCreatedAsync();
-
await context.AddAsync(new ConflictingIncompatibleId { id = 42 });
await context.SaveChangesAsync();
@@ -1893,8 +1629,6 @@ public async Task Can_add_update_delete_end_to_end_with_conflicting_id()
using (var context = contextFactory.CreateContext())
{
- await context.Database.EnsureCreatedAsync();
-
await context.AddAsync(entity);
await context.SaveChangesAsync();
@@ -1950,7 +1684,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
=> modelBuilder.Entity();
}
- [ConditionalTheory(Skip = "Issue #33600 - flaky test")]
+ [ConditionalTheory]
[InlineData(true)]
[InlineData(false)]
public async Task Can_have_non_string_property_named_Discriminator(bool useDiscriminator)
@@ -1973,7 +1707,6 @@ public async Task Can_have_non_string_property_named_Discriminator(bool useDiscr
onConfiguring: o => o.ConfigureWarnings(w => w.Log(CosmosEventId.SyncNotSupported, CosmosEventId.NoPartitionKeyDefined)));
using var context = contextFactory.CreateContext();
- context.Database.EnsureCreated();
var entry = await context.AddAsync(new NonStringDiscriminator { Id = 1 });
await context.SaveChangesAsync();
@@ -1985,117 +1718,57 @@ public async Task Can_have_non_string_property_named_Discriminator(bool useDiscr
var baseEntity = await context.Set().OrderBy(e => e.Id).FirstOrDefaultAsync();
Assert.NotNull(baseEntity);
- if (useDiscriminator)
- {
- AssertSql(
- context,
- """
-SELECT c
-FROM root c
-WHERE (c["Discriminator"] = 0)
-ORDER BY c["Id"]
-OFFSET 0 LIMIT 1
-""");
- }
- else
- {
- AssertSql(
- context,
- """
-SELECT c
+ AssertSql(
+ context,
+ """
+SELECT VALUE c
FROM root c
ORDER BY c["Id"]
OFFSET 0 LIMIT 1
""");
- }
ListLoggerFactory.Clear();
Assert.Equal(
baseEntity, await context.Set()
.Where(e => e.Discriminator == Discriminator.Base).OrderBy(e => e.Id).FirstOrDefaultAsync());
- if (useDiscriminator)
- {
- AssertSql(
- context,
- """
-SELECT c
-FROM root c
-WHERE ((c["Discriminator"] = 0) AND (c["Discriminator"] = 0))
-ORDER BY c["Id"]
-OFFSET 0 LIMIT 1
-""");
- }
- else
- {
AssertSql(
context,
"""
-SELECT c
+SELECT VALUE c
FROM root c
WHERE (c["Discriminator"] = 0)
ORDER BY c["Id"]
OFFSET 0 LIMIT 1
""");
- }
ListLoggerFactory.Clear();
Assert.Equal(
baseEntity, await context.Set()
.Where(e => e.GetType() == typeof(NonStringDiscriminator)).OrderBy(e => e.Id).FirstOrDefaultAsync());
- if (useDiscriminator)
- {
- AssertSql(
- context,
- """
-SELECT c
-FROM root c
-WHERE (c["Discriminator"] = 0)
-ORDER BY c["Id"]
-OFFSET 0 LIMIT 1
-""");
- }
- else
- {
AssertSql(
context,
"""
-SELECT c
+SELECT VALUE c
FROM root c
ORDER BY c["Id"]
OFFSET 0 LIMIT 1
""");
- }
ListLoggerFactory.Clear();
Assert.Equal(
baseEntity, await context.Set()
.Where(e => e is NonStringDiscriminator).OrderBy(e => e.Id).FirstOrDefaultAsync());
- if (useDiscriminator)
- {
- AssertSql(
- context,
- """
-SELECT c
-FROM root c
-WHERE (c["Discriminator"] = 0)
-ORDER BY c["Id"]
-OFFSET 0 LIMIT 1
-""");
- }
- else
- {
- AssertSql(
- context,
- """
-SELECT c
+ AssertSql(
+ context,
+ """
+SELECT VALUE c
FROM root c
ORDER BY c["Id"]
OFFSET 0 LIMIT 1
""");
- }
}
private class NonStringDiscriminator
diff --git a/test/EFCore.Cosmos.FunctionalTests/JsonTypesCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/JsonTypesCosmosTest.cs
index 49754ed19a5..40661f7433d 100644
--- a/test/EFCore.Cosmos.FunctionalTests/JsonTypesCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/JsonTypesCosmosTest.cs
@@ -3,7 +3,7 @@
namespace Microsoft.EntityFrameworkCore;
-public class JsonTypesCosmosTest : JsonTypesTestBase
+public class JsonTypesCosmosTest(NonSharedFixture fixture) : JsonTypesTestBase(fixture)
{
public override Task Can_read_write_collection_of_Guid_converted_to_bytes_JSON_values(string expected)
// Cosmos provider cannot map collections of elements with converters. See Issue #34026.
diff --git a/test/EFCore.Cosmos.FunctionalTests/MaterializationInterceptionCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/MaterializationInterceptionCosmosTest.cs
index 41e04f0587a..aaa80d3118c 100644
--- a/test/EFCore.Cosmos.FunctionalTests/MaterializationInterceptionCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/MaterializationInterceptionCosmosTest.cs
@@ -5,8 +5,8 @@ namespace Microsoft.EntityFrameworkCore;
#nullable disable
-public class MaterializationInterceptionCosmosTest :
- MaterializationInterceptionTestBase
+public class MaterializationInterceptionCosmosTest(NonSharedFixture fixture) :
+ MaterializationInterceptionTestBase(fixture)
{
public override Task Intercept_query_materialization_with_owned_types_projecting_collection(bool async, bool usePooling)
=> Task.CompletedTask;
diff --git a/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs b/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs
index cb55586cfb1..4b4f38d82ff 100644
--- a/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/ModelBuilding/CosmosModelBuilderGenericTest.cs
@@ -776,7 +776,7 @@ public override void Can_set_complex_property_annotation()
Assert.Equal("bar2", complexProperty["foo2"]);
Assert.Equal(typeof(Customer).Name, complexProperty.Name);
Assert.Equal(
- @"Customer (Customer) Required
+ @"Customer (Customer)
ComplexType: ComplexProperties.Customer#Customer
Properties: "
+ @"
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/AdHocCosmosTestHelpers.cs b/test/EFCore.Cosmos.FunctionalTests/Query/AdHocCosmosTestHelpers.cs
new file mode 100644
index 00000000000..19a5b39eccc
--- /dev/null
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/AdHocCosmosTestHelpers.cs
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Net;
+using Microsoft.Azure.Cosmos;
+using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Microsoft.EntityFrameworkCore.Query;
+
+public class AdHocCosmosTestHelpers
+{
+ public static async Task CreateCustomEntityHelperAsync(
+ Container container,
+ string json,
+ CancellationToken cancellationToken)
+ {
+ var document = JObject.Parse(json);
+
+ var stream = new MemoryStream();
+ await using var __ = stream.ConfigureAwait(false);
+ var writer = new StreamWriter(stream, new UTF8Encoding(), bufferSize: 1024, leaveOpen: false);
+ await using var ___ = writer.ConfigureAwait(false);
+ using var jsonWriter = new JsonTextWriter(writer);
+
+ CosmosClientWrapper.Serializer.Serialize(jsonWriter, document);
+ await jsonWriter.FlushAsync(cancellationToken).ConfigureAwait(false);
+
+ var response = await container.CreateItemStreamAsync(
+ stream,
+ PartitionKey.None,
+ requestOptions: null,
+ cancellationToken)
+ .ConfigureAwait(false);
+
+
+ if (response.StatusCode != HttpStatusCode.Created)
+ {
+ throw new InvalidOperationException($"Failed to create entity (status code: {response.StatusCode}) for json: {json}");
+ }
+ }
+}
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/AdHocJsonQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/AdHocJsonQueryCosmosTest.cs
new file mode 100644
index 00000000000..6ad9b089102
--- /dev/null
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/AdHocJsonQueryCosmosTest.cs
@@ -0,0 +1,1649 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Microsoft.EntityFrameworkCore.Query;
+
+public class AdHocJsonQueryCosmosTest(NonSharedFixture fixture) : AdHocJsonQueryTestBase(fixture)
+{
+ #region 21006
+
+ public override async Task Project_root_with_missing_scalars(bool async)
+ {
+ if (async)
+ {
+ await base.Project_root_with_missing_scalars(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+WHERE (c["Id"] < 4)
+""");
+ }
+ }
+
+ [ConditionalTheory(Skip = "issue #35702")]
+ public override async Task Project_top_level_json_entity_with_missing_scalars(bool async)
+ {
+ if (async)
+ {
+ await base.Project_top_level_json_entity_with_missing_scalars(async);
+
+ AssertSql();
+ }
+ }
+
+ public override async Task Project_nested_json_entity_with_missing_scalars(bool async)
+ {
+ if (async)
+ {
+ await AssertTranslationFailed(
+ () => base.Project_nested_json_entity_with_missing_scalars(async));
+
+ AssertSql();
+ }
+ }
+
+ [ConditionalTheory(Skip = "issue #34067")]
+ public override async Task Project_top_level_entity_with_null_value_required_scalars(bool async)
+ {
+ if (async)
+ {
+ await base.Project_top_level_entity_with_null_value_required_scalars(async);
+
+ AssertSql(
+ """
+SELECT c["Id"], c
+FROM root c
+WHERE (c["Id"] = 4)
+""");
+ }
+ }
+
+ public override async Task Project_root_entity_with_missing_required_navigation(bool async)
+ {
+ if (async)
+ {
+ await base.Project_root_entity_with_missing_required_navigation(async);
+
+ AssertSql(
+ """
+ReadItem(?, ?)
+""");
+ }
+ }
+
+ public override async Task Project_missing_required_navigation(bool async)
+ {
+ if (async)
+ {
+ await base.Project_missing_required_navigation(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+WHERE (c["Id"] = 5)
+""");
+ }
+ }
+
+ public override async Task Project_root_entity_with_null_required_navigation(bool async)
+ {
+ if (async)
+ {
+ await base.Project_root_entity_with_null_required_navigation(async);
+
+ AssertSql(
+ """
+ReadItem(?, ?)
+""");
+ }
+ }
+
+ public override async Task Project_null_required_navigation(bool async)
+ {
+ if (async)
+ {
+ await base.Project_null_required_navigation(async);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+WHERE (c["Id"] = 6)
+""");
+ }
+ }
+
+ public override async Task Project_missing_required_scalar(bool async)
+ {
+ if (async)
+ {
+ await base.Project_missing_required_scalar(async);
+
+ AssertSql(
+ """
+SELECT c["Id"], c["RequiredReference"]["Number"]
+FROM root c
+WHERE (c["Id"] = 2)
+""");
+ }
+ }
+
+ public override async Task Project_null_required_scalar(bool async)
+ {
+ if (async)
+ {
+ await base.Project_null_required_scalar(async);
+
+ AssertSql(
+ """
+SELECT c["Id"], c["RequiredReference"]["Number"]
+FROM root c
+WHERE (c["Id"] = 4)
+""");
+ }
+ }
+
+ protected override void OnModelCreating21006(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating21006(modelBuilder);
+
+ modelBuilder.Entity().ToContainer("Entities");
+ }
+
+ protected override async Task Seed21006(Context21006 context)
+ {
+ await base.Seed21006(context);
+
+ var wrapper = (CosmosClientWrapper)context.GetService();
+ var singletonWrapper = context.GetService();
+ var entitiesContainer = singletonWrapper.Client.GetContainer(context.Database.GetCosmosDatabaseId(), containerId: "Entities");
+
+ var missingTopLevel =
+$$"""
+{
+ "Id": 2,
+ "$type": "Entity",
+ "Name": "e2",
+ "id": "2",
+ "Collection":
+ [
+ {
+ "Text": "e2 c1",
+ "NestedCollection": [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 c1 c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 c1 c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 c1 nor"
+ },
+ "NestedRequiredReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 c1 nrr"
+ }
+ },
+ {
+ "Text": "e2 c2",
+ "NestedCollection": [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 c2 c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 c2 c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 c2 nor"
+ },
+ "NestedRequiredReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 c2 nrr"
+ }
+ }
+ ],
+ "OptionalReference": {
+ "Text": "e2 or",
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 or c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 or c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 or nor"
+ },
+ "NestedRequiredReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 or nrr"
+ }
+ },
+ "RequiredReference": {
+ "Text": "e2 rr",
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 rr c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 rr c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 rr nor"
+ },
+ "NestedRequiredReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e2 rr nrr"
+ }
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ missingTopLevel,
+ CancellationToken.None);
+
+ var missingNested =
+$$"""
+{
+ "Id": 3,
+ "$type": "Entity",
+ "Name": "e3",
+ "id": "3",
+ "Collection":
+ [
+ {
+ "Number": 7.0,
+ "Text": "e3 c1",
+ "NestedCollection":
+ [
+ {
+ "Text": "e3 c1 c1"
+ },
+ {
+ "Text": "e3 c1 c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "Text": "e3 c1 nor"
+ },
+ "NestedRequiredReference": {
+ "Text": "e3 c1 nrr"
+ }
+ },
+ {
+ "Number": 7.0,
+ "Text": "e3 c2",
+ "NestedCollection":
+ [
+ {
+ "Text": "e3 c2 c1"
+ },
+ {
+ "Text": "e3 c2 c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "Text": "e3 c2 nor"
+ },
+ "NestedRequiredReference": {
+ "Text": "e3 c2 nrr"
+ }
+ }
+ ],
+ "OptionalReference": {
+ "Number": 7.0,
+ "Text": "e3 or",
+ "NestedCollection":
+ [
+ {
+ "Text": "e3 or c1"
+ },
+ {
+ "Text": "e3 or c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "Text": "e3 or nor"
+ },
+ "NestedRequiredReference": {
+ "Text": "e3 or nrr"
+ }
+ },
+ "RequiredReference": {
+ "Number": 7.0,
+ "Text": "e3 rr",
+ "NestedCollection":
+ [
+ {
+ "Text": "e3 rr c1"
+ },
+ {
+ "Text": "e3 rr c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "Text": "e3 rr nor"
+ },
+ "NestedRequiredReference": {
+ "Text": "e3 rr nrr"
+ }
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ missingNested,
+ CancellationToken.None);
+
+ var nullTopLevel =
+$$"""
+{
+ "Id": 4,
+ "$type": "Entity",
+ "Name": "e4",
+ "id": "4",
+ "Collection":
+ [
+ {
+ "Number": null,
+ "Text": "e4 c1",
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 c1 c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 c1 c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 c1 nor"
+ },
+ "NestedRequiredReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 c1 nrr"
+ }
+ },
+ {
+ "Number": null,
+ "Text": "e4 c2",
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 c2 c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 c2 c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 c2 nor"
+ },
+ "NestedRequiredReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 c2 nrr"
+ }
+ }
+ ],
+ "OptionalReference": {
+ "Number": null,
+ "Text": "e4 or",
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 or c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 or c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 or nor"
+ },
+ "NestedRequiredReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 or nrr"
+ }
+ },
+ "RequiredReference": {
+ "Number": null,
+ "Text": "e4 rr",
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 rr c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 rr c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 rr nor"
+ },
+ "NestedRequiredReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e4 rr nrr"
+ }
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ nullTopLevel,
+ CancellationToken.None);
+
+ var missingRequiredNav =
+$$"""
+{
+ "Id": 5,
+ "$type": "Entity",
+ "Name": "e5",
+ "id": "5",
+ "Collection":
+ [
+ {
+ "Number": 7.0,
+ "Text": "e5 c1",
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e5 c1 c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e5 c1 c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e5 c1 nor"
+ },
+ },
+ {
+ "Number": 7.0,
+ "Text": "e5 c2",
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e5 c2 c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e5 c2 c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e5 c2 nor"
+ },
+ }
+ ],
+ "OptionalReference": {
+ "Number": 7.0,
+ "Text": "e5 or",
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e5 or c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e5 or c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e5 or nor"
+ },
+ },
+ "RequiredReference": {
+ "Number": 7.0,
+ "Text": "e5 rr",
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e5 rr c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e5 rr c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e5 rr nor"
+ },
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ missingRequiredNav,
+ CancellationToken.None);
+
+ var nullRequiredNav =
+$$"""
+{
+ "Id": 6,
+ "$type": "Entity",
+ "Name": "e6",
+ "id": "6",
+ "Collection":
+ [
+ {
+ "Number": 7.0,
+ "Text": "e6 c1",
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e6 c1 c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e6 c1 c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e6 c1 nor"
+ },
+ "NestedRequiredReference": null
+ },
+ {
+ "Number": 7.0,
+ "Text": "e6 c2",
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e6 c2 c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e6 c2 c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e6 c2 nor"
+ },
+ "NestedRequiredReference": null
+ }
+ ],
+ "OptionalReference": {
+ "Number": 7.0,
+ "Text": "e6 or",
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e6 or c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e6 or c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e6 or nor"
+ },
+ "NestedRequiredReference": null
+ },
+ "RequiredReference": {
+ "Number": 7.0,
+ "Text": "e6 rr",
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e6 rr c1"
+ },
+ {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e6 rr c2"
+ }
+ ],
+ "NestedOptionalReference": {
+ "DoB": "2000-01-01T00:00:00",
+ "Text": "e6 rr nor"
+ },
+ "NestedRequiredReference": null
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ nullRequiredNav,
+ CancellationToken.None);
+ }
+
+ #endregion
+
+ #region 29219
+
+ // Cosmos returns unexpected number of results (i.e. not returning row with non-existent NullableScalar
+ // this is by design behavior in Cosmos, so we just skip the test to avoid validation error
+ public override Task Can_project_nullable_json_property_when_the_element_in_json_is_not_present()
+ => Task.CompletedTask;
+
+ protected override void OnModelCreating29219(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating29219(modelBuilder);
+
+ modelBuilder.Entity().ToContainer("Entities");
+ }
+
+ protected override async Task Seed29219(DbContext context)
+ {
+ await base.Seed29219(context);
+
+ var wrapper = (CosmosClientWrapper)context.GetService();
+ var singletonWrapper = context.GetService();
+ var entitiesContainer = singletonWrapper.Client.GetContainer(context.Database.GetCosmosDatabaseId(), containerId: "Entities");
+
+ var missingNullableScalars =
+$$"""
+{
+ "Id": 3,
+ "$type": "MyEntity",
+ "id": "3",
+ "Collection":
+ [
+ {
+ "NonNullableScalar" : 10001
+ }
+ ],
+ "Reference": {
+ "NonNullableScalar" : 30
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ missingNullableScalars,
+ CancellationToken.None);
+ }
+
+ #endregion
+
+ #region 30028
+
+ [ConditionalTheory(Skip = "issue #35702")]
+ public override Task Missing_navigation_works_with_deduplication(bool async)
+ => base.Missing_navigation_works_with_deduplication(async);
+
+ // missing array comes out as empty on Cosmos
+ public override Task Accessing_missing_navigation_works()
+ => Task.CompletedTask;
+
+ protected override void OnModelCreating30028(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating30028(modelBuilder);
+
+ modelBuilder.Entity().ToContainer("Entities");
+ }
+
+ protected override async Task Seed30028(DbContext context)
+ {
+ var wrapper = (CosmosClientWrapper)context.GetService();
+ var singletonWrapper = context.GetService();
+ var entitiesContainer = singletonWrapper.Client.GetContainer(context.Database.GetCosmosDatabaseId(), containerId: "Entities");
+
+ var complete =
+$$$$"""
+{
+ "Id": 1,
+ "$type": "MyEntity",
+ "id": "1",
+ "Json": {
+ "RootName":"e1",
+ "Collection":
+ [
+ {
+ "BranchName":"e1 c1",
+ "Nested":{
+ "LeafName":"e1 c1 l"
+ }
+ },
+ {
+ "BranchName":"e1 c2",
+ "Nested":{
+ "LeafName":"e1 c2 l"
+ }
+ }
+ ],
+ "OptionalReference":{
+ "BranchName":"e1 or",
+ "Nested":{
+ "LeafName":"e1 or l"
+ }
+ },
+ "RequiredReference":{
+ "BranchName":"e1 rr",
+ "Nested":{
+ "LeafName":"e1 rr l"
+ }
+ }
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ complete,
+ CancellationToken.None);
+
+ var missingCollection =
+$$$$"""
+{
+ "Id": 2,
+ "$type": "MyEntity",
+ "id": "2",
+ "Json": {
+ "RootName":"e2",
+ "OptionalReference":{
+ "BranchName":"e2 or",
+ "Nested":{
+ "LeafName":"e2 or l"
+ }
+ },
+ "RequiredReference":{
+ "BranchName":"e2 rr",
+ "Nested":{
+ "LeafName":"e2 rr l"
+ }
+ }
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ missingCollection,
+ CancellationToken.None);
+
+ var missingOptionalReference =
+$$$$"""
+{
+ "Id": 3,
+ "$type": "MyEntity",
+ "id": "3",
+ "Json": {
+ "RootName":"e3",
+ "Collection":
+ [
+ {
+ "BranchName":"e3 c1",
+ "Nested":{
+ "LeafName":"e3 c1 l"
+ }
+ },
+ {
+ "BranchName":"e3 c2",
+ "Nested":{
+ "LeafName":"e3 c2 l"
+ }
+ }
+ ],
+ "RequiredReference":{
+ "BranchName":"e3 rr",
+ "Nested":{
+ "LeafName":"e3 rr l"
+ }
+ }
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ missingOptionalReference,
+ CancellationToken.None);
+
+ var missingRequiredReference =
+$$$$"""
+{
+ "Id": 4,
+ "$type": "MyEntity",
+ "id": "4",
+ "Json": {
+ "RootName":"e4",
+ "Collection":
+ [
+ {
+ "BranchName":"e4 c1",
+ "Nested":{
+ "LeafName":"e4 c1 l"
+ }
+ },
+ {
+ "BranchName":"e4 c2",
+ "Nested":{
+ "LeafName":"e4 c2 l"
+ }
+ }
+ ],
+ "OptionalReference":{
+ "BranchName":"e4 or",
+ "Nested":{
+ "LeafName":"e4 or l"
+ }
+ }
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ missingRequiredReference,
+ CancellationToken.None);
+ }
+
+ #endregion
+
+ #region 33046
+
+ protected override void OnModelCreating33046(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating33046(modelBuilder);
+
+ modelBuilder.Entity().ToContainer("Reviews");
+ }
+
+ protected override async Task Seed33046(DbContext context)
+ {
+ var wrapper = (CosmosClientWrapper)context.GetService();
+ var singletonWrapper = context.GetService();
+ var entitiesContainer = singletonWrapper.Client.GetContainer(context.Database.GetCosmosDatabaseId(), containerId: "Reviews");
+
+ var json =
+$$$$"""
+{
+ "Id": 1,
+ "$type": "Review",
+ "id": "1",
+ "Rounds":
+ [
+ {
+ "RoundNumber":11,
+ "SubRounds":
+ [
+ {
+ "SubRoundNumber":111
+ },
+ {
+ "SubRoundNumber":112
+ }
+ ]
+ }
+ ]
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ json,
+ CancellationToken.None);
+ }
+
+ #endregion
+
+ #region 34960
+
+
+ public override async Task Try_project_collection_but_JSON_is_entity()
+ {
+ var message = (await Assert.ThrowsAsync(
+ () => base.Try_project_collection_but_JSON_is_entity())).Message;
+
+ Assert.Equal(
+ $"Deserialized JSON type '{typeof(JObject).FullName}' is not compatible with expected type '{typeof(JArray).FullName}'. Path 'Collection'.",
+ message);
+ }
+
+ public override async Task Try_project_reference_but_JSON_is_collection()
+ {
+ var message = (await Assert.ThrowsAsync(
+ () => base.Try_project_reference_but_JSON_is_collection())).Message;
+
+ Assert.Equal(
+ $"Deserialized JSON type '{typeof(JArray).FullName}' is not compatible with expected type '{typeof(JObject).FullName}'. Path 'Reference'.",
+ message);
+ }
+
+ protected override void OnModelCreating34960(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating34960(modelBuilder);
+ modelBuilder.Entity().ToContainer("Entities");
+ modelBuilder.Entity().ToContainer("Junk");
+ }
+
+ protected async override Task Seed34960(Context34960 context)
+ {
+ await base.Seed34960(context);
+
+ var wrapper = (CosmosClientWrapper)context.GetService();
+ var singletonWrapper = context.GetService();
+ var entitiesContainer = singletonWrapper.Client.GetContainer(context.Database.GetCosmosDatabaseId(), containerId: "Entities");
+
+ var json =
+$$$"""
+{
+ "Id": 4,
+ "$type": "Entity",
+ "id": "4",
+ "Collection": null,
+ "Reference": null
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ json,
+ CancellationToken.None);
+
+ var junkContainer = singletonWrapper.Client.GetContainer(context.Database.GetCosmosDatabaseId(), containerId: "Junk");
+
+ var objectWhereCollectionShouldBe =
+$$$"""
+{
+ "Id": 1,
+ "$type": "JunkEntity",
+ "id": "1",
+ "Collection": { "DoB":"2000-01-01T00:00:00","Text":"junk" },
+ "Reference": null
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ junkContainer,
+ objectWhereCollectionShouldBe,
+ CancellationToken.None);
+
+ var collectionWhereEntityShouldBe =
+$$$"""
+{
+ "Id": 2,
+ "$type": "JunkEntity",
+ "id": "2",
+ "Collection": null,
+ "Reference": [{ "DoB":"2000-01-01T00:00:00","Text":"junk" }]
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ junkContainer,
+ collectionWhereEntityShouldBe,
+ CancellationToken.None);
+ }
+
+ #endregion
+
+ #region ArrayOfPrimitives
+
+ protected override void OnModelCreatingArrayOfPrimitives(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreatingArrayOfPrimitives(modelBuilder);
+ }
+
+ #endregion
+
+ #region JunkInJson
+
+ protected override void OnModelCreatingJunkInJson(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreatingJunkInJson(modelBuilder);
+
+ modelBuilder.Entity().ToContainer("Entities");
+ }
+
+ protected override async Task SeedJunkInJson(DbContext context)
+ {
+ var wrapper = (CosmosClientWrapper)context.GetService();
+ var singletonWrapper = context.GetService();
+ var entitiesContainer = singletonWrapper.Client.GetContainer(context.Database.GetCosmosDatabaseId(), containerId: "Entities");
+
+ var json =
+$$$"""
+{
+ "Id": 1,
+ "$type": "MyEntity",
+ "id": "1",
+ "Collection":
+ [
+ {
+ "JunkReference": {
+ "Something":"SomeValue"
+ },
+ "Name":"c11",
+ "JunkProperty1":50,
+ "Number":11.5,
+ "JunkCollection1":[],
+ "JunkCollection2":
+ [
+ {
+ "Foo":"junk value"
+ }
+ ],
+ "NestedCollection":
+ [
+ {
+ "DoB":"2002-04-01T00:00:00",
+ "DummyProp":"Dummy value"
+ },
+ {
+ "DoB":"2002-04-02T00:00:00",
+ "DummyReference":{
+ "Foo":5
+ }
+ }
+ ],
+ "NestedReference":{
+ "DoB":"2002-03-01T00:00:00"
+ }
+ },
+ {
+ "Name":"c12",
+ "Number":12.5,
+ "NestedCollection":
+ [
+ {
+ "DoB":"2002-06-01T00:00:00"
+ },
+ {
+ "DoB":"2002-06-02T00:00:00"
+ }
+ ],
+ "NestedDummy":59,
+ "NestedReference":{
+ "DoB":"2002-05-01T00:00:00"
+ }
+ }
+ ],
+ "CollectionWithCtor":
+ [
+ {
+ "MyBool":true,
+ "Name":"c11 ctor",
+ "JunkReference":{
+ "Something":"SomeValue",
+ "JunkCollection":
+ [
+ {
+ "Foo":"junk value"
+ }
+ ]
+ },
+ "NestedCollection":
+ [
+ {
+ "DoB":"2002-08-01T00:00:00"
+ },
+ {
+ "DoB":"2002-08-02T00:00:00"
+ }
+ ],
+ "NestedReference":{
+ "DoB":"2002-07-01T00:00:00"
+ }
+ },
+ {
+ "MyBool":false,
+ "Name":"c12 ctor",
+ "NestedCollection":
+ [
+ {
+ "DoB":"2002-10-01T00:00:00"
+ },
+ {
+ "DoB":"2002-10-02T00:00:00"
+ }
+ ],
+ "JunkCollection":
+ [
+ {
+ "Foo":"junk value"
+ }
+ ],
+ "NestedReference":{
+ "DoB":"2002-09-01T00:00:00"
+ }
+ }
+ ],
+ "Reference": {
+ "Name":"r1",
+ "JunkCollection":
+ [
+ {
+ "Foo":"junk value"
+ }
+ ],
+ "JunkReference":{
+ "Something":"SomeValue"
+ },
+ "Number":1.5,
+ "NestedCollection":
+ [
+ {
+ "DoB":"2000-02-01T00:00:00",
+ "JunkReference":{
+ "Something":"SomeValue"
+ }
+ },
+ {
+ "DoB":"2000-02-02T00:00:00"
+ }
+ ],
+ "NestedReference":{
+ "DoB":"2000-01-01T00:00:00"
+ }
+ },
+ "ReferenceWithCtor":{
+ "MyBool":true,
+ "JunkCollection":
+ [
+ {
+ "Foo":"junk value"
+ }
+ ],
+ "Name":"r1 ctor",
+ "JunkReference":{
+ "Something":"SomeValue"
+ },
+ "NestedCollection":
+ [
+ {
+ "DoB":"2001-02-01T00:00:00"
+ },
+ {
+ "DoB":"2001-02-02T00:00:00"
+ }
+ ],
+ "NestedReference":{
+ "JunkCollection":
+ [
+ {
+ "Foo":"junk value"
+ }
+ ],
+ "DoB":"2001-01-01T00:00:00"
+ }
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ json,
+ CancellationToken.None);
+ }
+
+ #endregion
+
+ #region TrickyBuffering
+
+ protected override void OnModelCreatingTrickyBuffering(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreatingTrickyBuffering(modelBuilder);
+
+ modelBuilder.Entity().ToContainer("Entities");
+ }
+
+ protected override async Task SeedTrickyBuffering(DbContext context)
+ {
+ var wrapper = (CosmosClientWrapper)context.GetService();
+ var singletonWrapper = context.GetService();
+ var entitiesContainer = singletonWrapper.Client.GetContainer(context.Database.GetCosmosDatabaseId(), containerId: "Entities");
+
+ var json =
+$$$"""
+{
+ "Id": 1,
+ "$type": "MyEntity",
+ "id": "1",
+ "Reference": {
+ "Name": "r1",
+ "Number": 7,
+ "JunkReference": {
+ "Something": "SomeValue"
+ },
+ "JunkCollection":
+ [
+ {
+ "Foo": "junk value"
+ }
+ ],
+ "NestedReference": {
+ "DoB": "2000-01-01T00:00:00"
+ },
+ "NestedCollection":
+ [
+ {
+ "DoB": "2000-02-01T00:00:00",
+ "JunkReference": {
+ "Something": "SomeValue"
+ }
+ },
+ {
+ "DoB": "2000-02-02T00:00:00"
+ }
+ ]
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ json,
+ CancellationToken.None);
+ }
+
+ #endregion
+
+ #region ShadowProperties
+
+ protected override void OnModelCreatingShadowProperties(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreatingShadowProperties(modelBuilder);
+
+ modelBuilder.Entity(b =>
+ {
+ b.ToContainer("Entities");
+
+ //b.OwnsOne(x => x.Reference, b =>
+ //{
+ // // b.ToJson().HasColumnType(JsonColumnType);
+ // b.Property("ShadowString");
+ //});
+
+ b.OwnsOne(x => x.ReferenceWithCtor, b =>
+ {
+ // b.ToJson().HasColumnType(JsonColumnType);
+ b.Property("Shadow_Int").ToJsonProperty("ShadowInt");
+ });
+
+ //b.OwnsMany(
+ // x => x.Collection, b =>
+ // {
+ // // b.ToJson().HasColumnType(JsonColumnType);
+ // b.Property("ShadowDouble");
+ // });
+
+ //b.OwnsMany(
+ // x => x.CollectionWithCtor, b =>
+ // {
+ // //b.ToJson().HasColumnType(JsonColumnType);
+ // b.Property("ShadowNullableByte");
+ // });
+ });
+ }
+
+ protected override async Task SeedShadowProperties(DbContext context)
+ {
+ var wrapper = (CosmosClientWrapper)context.GetService();
+ var singletonWrapper = context.GetService();
+ var entitiesContainer = singletonWrapper.Client.GetContainer(context.Database.GetCosmosDatabaseId(), containerId: "Entities");
+
+ var json =
+$$$"""
+{
+ "Id": 1,
+ "$type": "MyEntity",
+ "id": "1",
+ "Name": "e1",
+ "Collection":
+ [
+ {
+ "Name":"e1_c1","ShadowDouble":5.5
+ },
+ {
+ "ShadowDouble":20.5,"Name":"e1_c2"
+ }
+ ],
+ "CollectionWithCtor":
+ [
+ {
+ "Name":"e1_c1 ctor","ShadowNullableByte":6
+ },
+ {
+ "ShadowNullableByte":null,"Name":"e1_c2 ctor"
+ }
+ ],
+ "Reference": { "Name":"e1_r", "ShadowString":"Foo" },
+ "ReferenceWithCtor": { "ShadowInt":143,"Name":"e1_r ctor" }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ json,
+ CancellationToken.None);
+ }
+
+ #endregion
+
+ #region LazyLoadingProxies
+
+ protected override void OnModelCreatingLazyLoadingProxies(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreatingLazyLoadingProxies(modelBuilder);
+ }
+
+ #endregion
+
+ #region NotICollection
+
+ protected override void OnModelCreatingNotICollection(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreatingNotICollection(modelBuilder);
+
+ modelBuilder.Entity().ToContainer("Entities");
+ }
+
+ protected override async Task SeedNotICollection(DbContext context)
+ {
+ var wrapper = (CosmosClientWrapper)context.GetService();
+ var singletonWrapper = context.GetService();
+ var entitiesContainer = singletonWrapper.Client.GetContainer(context.Database.GetCosmosDatabaseId(), containerId: "Entities");
+
+ var json1 =
+$$$"""
+{
+ "Id": 1,
+ "$type": "MyEntity",
+ "id": "1",
+ "Json":
+ {
+ "Collection":
+ [
+ {
+ "Bar":11,"Foo":"c11"
+ },
+ {
+ "Bar":12,"Foo":"c12"
+ },
+ {
+ "Bar":13,"Foo":"c13"
+ }
+ ]
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ json1,
+ CancellationToken.None);
+
+ var json2 =
+$$$"""
+{
+ "Id": 2,
+ "$type": "MyEntity",
+ "id": "2",
+ "Json": {
+ "Collection":
+ [
+ {
+ "Bar":21,"Foo":"c21"
+ },
+ {
+ "Bar":22,"Foo":"c22"
+ }
+ ]
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ json2,
+ CancellationToken.None);
+ }
+
+ #endregion
+
+ #region BadJsonProperties
+
+ // missing collection comes back as empty on Cosmos
+ public override Task Bad_json_properties_empty_navigations(bool noTracking)
+ => Task.CompletedTask;
+
+ protected override void OnModelCreatingBadJsonProperties(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreatingBadJsonProperties(modelBuilder);
+
+ modelBuilder.Entity().ToContainer("Entities");
+ }
+
+ protected override async Task SeedBadJsonProperties(ContextBadJsonProperties context)
+ {
+ var wrapper = (CosmosClientWrapper)context.GetService();
+ var singletonWrapper = context.GetService();
+ var entitiesContainer = singletonWrapper.Client.GetContainer(context.Database.GetCosmosDatabaseId(), containerId: "Entities");
+
+ var baseline =
+$$$"""
+{
+ "Id": 1,
+ "$type": "Entity",
+ "id": "1",
+ "Scenario": "baseline",
+ "OptionalReference": {"NestedOptional": { "Text":"or no" }, "NestedRequired": { "Text":"or nr" }, "NestedCollection": [ { "Text":"or nc 1" }, { "Text":"or nc 2" } ] },
+ "RequiredReference": {"NestedOptional": { "Text":"rr no" }, "NestedRequired": { "Text":"rr nr" }, "NestedCollection": [ { "Text":"rr nc 1" }, { "Text":"rr nc 2" } ] },
+ "Collection":
+ [
+ {"NestedOptional": { "Text":"c 1 no" }, "NestedRequired": { "Text":"c 1 nr" }, "NestedCollection": [ { "Text":"c 1 nc 1" }, { "Text":"c 1 nc 2" } ] },
+ {"NestedOptional": { "Text":"c 2 no" }, "NestedRequired": { "Text":"c 2 nr" }, "NestedCollection": [ { "Text":"c 2 nc 1" }, { "Text":"c 2 nc 2" } ] }
+ ]
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ baseline,
+ CancellationToken.None);
+
+ var duplicatedNavigations =
+$$$"""
+{
+ "Id": 2,
+ "$type": "Entity",
+ "id": "2",
+ "Scenario": "duplicated navigations",
+ "OptionalReference": {"NestedOptional": { "Text":"or no" }, "NestedOptional": { "Text":"or no dupnav" }, "NestedRequired": { "Text":"or nr" }, "NestedCollection": [ { "Text":"or nc 1" }, { "Text":"or nc 2" } ], "NestedCollection": [ { "Text":"or nc 1 dupnav" }, { "Text":"or nc 2 dupnav" } ], "NestedRequired": { "Text":"or nr dupnav" } },
+ "RequiredReference": {"NestedOptional": { "Text":"rr no" }, "NestedOptional": { "Text":"rr no dupnav" }, "NestedRequired": { "Text":"rr nr" }, "NestedCollection": [ { "Text":"rr nc 1" }, { "Text":"rr nc 2" } ], "NestedCollection": [ { "Text":"rr nc 1 dupnav" }, { "Text":"rr nc 2 dupnav" } ], "NestedRequired": { "Text":"rr nr dupnav" } },
+ "Collection":
+ [
+ {"NestedOptional": { "Text":"c 1 no" }, "NestedOptional": { "Text":"c 1 no dupnav" }, "NestedRequired": { "Text":"c 1 nr" }, "NestedCollection": [ { "Text":"c 1 nc 1" }, { "Text":"c 1 nc 2" } ], "NestedCollection": [ { "Text":"c 1 nc 1 dupnav" }, { "Text":"c 1 nc 2 dupnav" } ], "NestedRequired": { "Text":"c 1 nr dupnav" } },
+ {"NestedOptional": { "Text":"c 2 no" }, "NestedOptional": { "Text":"c 2 no dupnav" }, "NestedRequired": { "Text":"c 2 nr" }, "NestedCollection": [ { "Text":"c 2 nc 1" }, { "Text":"c 2 nc 2" } ], "NestedCollection": [ { "Text":"c 2 nc 1 dupnav" }, { "Text":"c 2 nc 2 dupnav" } ], "NestedRequired": { "Text":"c 2 nr dupnav" } }
+ ]
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ duplicatedNavigations,
+ CancellationToken.None);
+
+ var duplicatedScalars =
+$$$"""
+{
+ "Id": 3,
+ "$type": "Entity",
+ "id": "3",
+ "Scenario": "duplicated scalars",
+ "OptionalReference": {"NestedOptional": { "Text":"or no", "Text":"or no dupprop" }, "NestedRequired": { "Text":"or nr", "Text":"or nr dupprop" }, "NestedCollection": [ { "Text":"or nc 1", "Text":"or nc 1 dupprop" }, { "Text":"or nc 2", "Text":"or nc 2 dupprop" } ] },
+ "RequiredReference": {"NestedOptional": { "Text":"rr no", "Text":"rr no dupprop" }, "NestedRequired": { "Text":"rr nr", "Text":"rr nr dupprop" }, "NestedCollection": [ { "Text":"rr nc 1", "Text":"rr nc 1 dupprop" }, { "Text":"rr nc 2", "Text":"rr nc 2 dupprop" } ] },
+ "Collection":
+ [
+ {"NestedOptional": { "Text":"c 1 no", "Text":"c 1 no dupprop" }, "NestedRequired": { "Text":"c 1 nr", "Text":"c 1 nr dupprop" }, "NestedCollection": [ { "Text":"c 1 nc 1", "Text":"c 1 nc 1 dupprop" }, { "Text":"c 1 nc 2", "Text":"c 1 nc 2 dupprop" } ] },
+ {"NestedOptional": { "Text":"c 2 no", "Text":"c 2 no dupprop" }, "NestedRequired": { "Text":"c 2 nr", "Text":"c 2 nr dupprop" }, "NestedCollection": [ { "Text":"c 2 nc 1", "Text":"c 2 nc 1 dupprop" }, { "Text":"c 2 nc 2", "Text":"c 2 nc 2 dupprop" } ] }
+ ]
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ duplicatedScalars,
+ CancellationToken.None);
+
+ var emptyNavs =
+$$$"""
+{
+ "Id": 4,
+ "$type": "Entity",
+ "id": "4",
+ "Scenario": "empty navigation property names",
+ "OptionalReference": {"": { "Text":"or no" }, "": { "Text":"or nr" }, "": [ { "Text":"or nc 1" }, { "Text":"or nc 2" } ] },
+ "RequiredReference": {"": { "Text":"rr no" }, "": { "Text":"rr nr" }, "": [ { "Text":"rr nc 1" }, { "Text":"rr nc 2" } ] },
+ "Collection":
+ [
+ {"": { "Text":"c 1 no" }, "": { "Text":"c 1 nr" }, "": [ { "Text":"c 1 nc 1" }, { "Text":"c 1 nc 2" } ] },
+ {"": { "Text":"c 2 no" }, "": { "Text":"c 2 nr" }, "": [ { "Text":"c 2 nc 1" }, { "Text":"c 2 nc 2" } ] }
+ ]
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ emptyNavs,
+ CancellationToken.None);
+
+ var emptyScalars =
+$$$"""
+{
+ "Id": 5,
+ "$type": "Entity",
+ "id": "5",
+ "Scenario": "empty scalar property names",
+ "OptionalReference": {"NestedOptional": { "":"or no" }, "NestedRequired": { "":"or nr" }, "NestedCollection": [ { "":"or nc 1" }, { "":"or nc 2" } ] },
+ "RequiredReference": {"NestedOptional": { "":"rr no" }, "NestedRequired": { "":"rr nr" }, "NestedCollection": [ { "":"rr nc 1" }, { "":"rr nc 2" } ] },
+ "Collection":
+ [
+ {"NestedOptional": { "":"c 1 no" }, "NestedRequired": { "":"c 1 nr" }, "NestedCollection": [ { "":"c 1 nc 1" }, { "":"c 1 nc 2" } ] },
+ {"NestedOptional": { "":"c 2 no" }, "NestedRequired": { "":"c 2 nr" }, "NestedCollection": [ { "":"c 2 nc 1" }, { "":"c 2 nc 2" } ] }
+ ]
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ emptyScalars,
+ CancellationToken.None);
+
+ var nullNavs =
+$$$"""
+{
+ "Id": 10,
+ "$type": "Entity",
+ "id": "10",
+ "Scenario": "null navigation property names",
+ "OptionalReference": {null: { "Text":"or no" }, null: { "Text":"or nr" }, null: [ { "Text":"or nc 1" }, { "Text":"or nc 2" } ] },
+ "RequiredReference": {null: { "Text":"rr no" }, null: { "Text":"rr nr" }, null: [ { "Text":"rr nc 1" }, { "Text":"rr nc 2" } ] },
+ "Collection":
+ [
+ {null: { "Text":"c 1 no" }, null: { "Text":"c 1 nr" }, null: [ { "Text":"c 1 nc 1" }, { "Text":"c 1 nc 2" } ] },
+ {null: { "Text":"c 2 no" }, null: { "Text":"c 2 nr" }, null: [ { "Text":"c 2 nc 1" }, { "Text":"c 2 nc 2" } ] }
+ ]
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ nullNavs,
+ CancellationToken.None);
+
+ var nullScalars =
+$$$"""
+{
+ "Id": 11,
+ "$type": "Entity",
+ "id": "11",
+ "Scenario": "null scalar property names",
+ "OptionalReference": {"NestedOptional": { null:"or no", "Text":"or no nonnull" }, "NestedRequired": { null:"or nr", "Text":"or nr nonnull" }, "NestedCollection": [ { null:"or nc 1", "Text":"or nc 1 nonnull" }, { null:"or nc 2", "Text":"or nc 2 nonnull" } ] },
+ "RequiredReference": {"NestedOptional": { null:"rr no", "Text":"rr no nonnull" }, "NestedRequired": { null:"rr nr", "Text":"rr nr nonnull" }, "NestedCollection": [ { null:"rr nc 1", "Text":"rr nc 1 nonnull" }, { null:"rr nc 2", "Text":"rr nc 2 nonnull" } ] },
+ "Collection":
+ [
+ {"NestedOptional": { null:"c 1 no", "Text":"c 1 no nonnull" }, "NestedRequired": { null:"c 1 nr", "Text":"c 1 nr nonnull" }, "NestedCollection": [ { null:"c 1 nc 1", "Text":"c 1 nc 1 nonnull" }, { null:"c 1 nc 2", "Text":"c 1 nc 2 nonnull" } ] },
+ {"NestedOptional": { null:"c 2 no", "Text":"c 2 no nonnull" }, "NestedRequired": { null:"c 2 nr", "Text":"c 2 nr nonnull" }, "NestedCollection": [ { null:"c 2 nc 1", "Text":"c 2 nc 1 nonnull" }, { null:"c 2 nc 2", "Text":"c 2 nc 2 nonnull" } ] }
+ ]
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ nullScalars,
+ CancellationToken.None);
+ }
+
+ #endregion
+
+ protected TestSqlLoggerFactory TestSqlLoggerFactory
+ => (TestSqlLoggerFactory)ListLoggerFactory;
+
+ private void AssertSql(params string[] expected)
+ => TestSqlLoggerFactory.AssertBaseline(expected);
+
+ protected static async Task AssertTranslationFailed(Func query)
+ => Assert.Contains(
+ CoreStrings.TranslationFailed("")[48..],
+ (await Assert.ThrowsAsync(query))
+ .Message);
+
+ protected override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
+ => builder.ConfigureWarnings(b => b.Ignore(CosmosEventId.NoPartitionKeyDefined));
+
+ protected override ITestStoreFactory TestStoreFactory
+ => CosmosTestStoreFactory.Instance;
+}
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/AdHocMiscellaneousQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/AdHocMiscellaneousQueryCosmosTest.cs
index e2dd2b90904..fdcff9e15e5 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/AdHocMiscellaneousQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/AdHocMiscellaneousQueryCosmosTest.cs
@@ -2,13 +2,152 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations.Schema;
+using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
namespace Microsoft.EntityFrameworkCore.Query;
#nullable disable
-public class AdHocMiscellaneousQueryCosmosTest : NonSharedModelTestBase
+public class AdHocMiscellaneousQueryCosmosTest(NonSharedFixture fixture) : NonSharedModelTestBase(fixture), IClassFixture
{
+ #region 21006
+
+ [ConditionalFact]
+ public virtual async Task Project_all_types_entity_with_missing_scalars()
+ {
+ var contextFactory = await InitializeAsync(
+ onModelCreating: OnModelCreating21006,
+ seed: Seed21006);
+
+ await using var context = contextFactory.CreateContext();
+
+ var query = context.Set();
+
+ var result = await query.ToListAsync();
+ }
+
+ public void OnModelCreating21006(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity(b =>
+ {
+ b.Property(x => x.Id).ValueGeneratedNever();
+ b.ToContainer("Entities");
+ b.Property(x => x.TestDecimal).HasPrecision(18, 3);
+ b.OwnsOne(x => x.Reference, bb =>
+ {
+ bb.Property(x => x.TestDecimal).HasPrecision(18, 3);
+ bb.Property(x => x.TestEnumWithIntConverter).HasConversion();
+ });
+ });
+ }
+
+ protected async Task Seed21006(JsonContext21006 context)
+ {
+ var wrapper = (CosmosClientWrapper)context.GetService();
+ var singletonWrapper = context.GetService();
+ var entitiesContainer = singletonWrapper.Client.GetContainer(context.Database.GetCosmosDatabaseId(), containerId: "Entities");
+
+ var missingTopLevel =
+$$"""
+{
+ "Id": 1,
+ "$type": "Entity",
+ "id": "1",
+ "Reference": {
+ "Text": "e2 or"
+ }
+}
+""";
+
+ await AdHocCosmosTestHelpers.CreateCustomEntityHelperAsync(
+ entitiesContainer,
+ missingTopLevel,
+ CancellationToken.None);
+ }
+
+ protected class JsonContext21006(DbContextOptions options) : DbContext(options)
+ {
+ public DbSet Entities { get; set; }
+
+ public class Entity
+ {
+ public int Id { get; set; }
+
+ public short TestInt16 { get; set; }
+ public int TestInt32 { get; set; }
+ public long TestInt64 { get; set; }
+ public double TestDouble { get; set; }
+ public decimal TestDecimal { get; set; }
+ public DateTime TestDateTime { get; set; }
+ public DateTimeOffset TestDateTimeOffset { get; set; }
+ public TimeSpan TestTimeSpan { get; set; }
+ public DateOnly TestDateOnly { get; set; }
+ public TimeOnly TestTimeOnly { get; set; }
+ public float TestSingle { get; set; }
+ public bool TestBoolean { get; set; }
+ public byte TestByte { get; set; }
+
+ public byte[] TestByteArray { get; set; }
+ public Guid TestGuid { get; set; }
+ public ushort TestUnsignedInt16 { get; set; }
+ public uint TestUnsignedInt32 { get; set; }
+ public ulong TestUnsignedInt64 { get; set; }
+ public char TestCharacter { get; set; }
+ public sbyte TestSignedByte { get; set; }
+ public int? TestNullableInt32 { get; set; }
+ public JsonEnum TestEnum { get; set; }
+ public byte[] TestByteCollection { get; set; }
+ public IList TestUnsignedInt16Collection { get; set; }
+ public uint[] TestUnsignedInt32Collection { get; set; }
+ public sbyte[] TestSignedByteCollection { get; set; }
+ public JsonEntity Reference { get; set; }
+ }
+
+ public class JsonEntity
+ {
+ public string Text { get; set; }
+
+ public short TestInt16 { get; set; }
+ public int TestInt32 { get; set; }
+ public long TestInt64 { get; set; }
+ public double TestDouble { get; set; }
+ public decimal TestDecimal { get; set; }
+ public DateTime TestDateTime { get; set; }
+ public DateTimeOffset TestDateTimeOffset { get; set; }
+ public TimeSpan TestTimeSpan { get; set; }
+ public DateOnly TestDateOnly { get; set; }
+ public TimeOnly TestTimeOnly { get; set; }
+ public float TestSingle { get; set; }
+ public bool TestBoolean { get; set; }
+ public byte TestByte { get; set; }
+ public byte[] TestByteArray { get; set; }
+ public Guid TestGuid { get; set; }
+ public ushort TestUnsignedInt16 { get; set; }
+ public uint TestUnsignedInt32 { get; set; }
+ public ulong TestUnsignedInt64 { get; set; }
+ public char TestCharacter { get; set; }
+ public sbyte TestSignedByte { get; set; }
+ public int? TestNullableInt32 { get; set; }
+ public JsonEnum TestEnum { get; set; }
+ public JsonEnum TestEnumWithIntConverter { get; set; }
+
+ public byte[] TestByteCollection { get; set; }
+ public IList TestUnsignedInt16Collection { get; set; }
+ public uint[] TestUnsignedInt32Collection { get; set; }
+
+ public sbyte[] TestSignedByteCollection { get; set; }
+ }
+
+ public enum JsonEnum
+ {
+ One = -1,
+ Two = 2,
+ Three = -3
+ }
+ }
+
+ #endregion
+
#region 34911
[ConditionalFact]
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryCosmosTest.cs
index d74da8d9e11..0816f8e70c9 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryCosmosTest.cs
@@ -5,7 +5,7 @@
namespace Microsoft.EntityFrameworkCore.Query;
-public class NonSharedPrimitiveCollectionsQueryCosmosTest : NonSharedPrimitiveCollectionsQueryTestBase
+public class NonSharedPrimitiveCollectionsQueryCosmosTest(NonSharedFixture fixture) : NonSharedPrimitiveCollectionsQueryTestBase(fixture)
{
#region Support for specific element types
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/InProjection/JsonRelationshipsInProjectionNoTrackingQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/InProjection/JsonRelationshipsInProjectionNoTrackingQueryCosmosTest.cs
deleted file mode 100644
index 469d6eb33c7..00000000000
--- a/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/InProjection/JsonRelationshipsInProjectionNoTrackingQueryCosmosTest.cs
+++ /dev/null
@@ -1,317 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace Microsoft.EntityFrameworkCore.Query.Relationships.InProjection;
-
-public class JsonRelationshipsInProjectionNoTrackingQueryCosmosTest : JsonRelationshipsInProjectionQueryTestBase
-{
- private readonly NoTrackingRewriter _noTrackingRewriter = new();
-
- public JsonRelationshipsInProjectionNoTrackingQueryCosmosTest(JsonRelationshipsQueryCosmosFixture fixture, ITestOutputHelper testOutputHelper)
- : base(fixture)
- {
- Fixture.TestSqlLoggerFactory.Clear();
- Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
- }
-
- protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression)
- {
- var rewritten = _noTrackingRewriter.Visit(serverQueryExpression);
-
- return rewritten;
- }
-
- public override Task Project_root(bool async)
- => Fixture.NoSyncTest(
- async, async a =>
- {
- await base.Project_root(a);
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-""");
- });
-
- public override Task Project_trunk_optional(bool async)
- => Fixture.NoSyncTest(
- async, async a =>
- {
- await base.Project_trunk_optional(a);
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-ORDER BY c["Id"]
-""");
- });
-
- public override Task Project_trunk_required(bool async)
- => Fixture.NoSyncTest(
- async, async a =>
- {
- await base.Project_trunk_required(a);
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-ORDER BY c["Id"]
-""");
- });
-
- public override Task Project_trunk_collection(bool async)
- => Fixture.NoSyncTest(
- async, async a =>
- {
- await base.Project_trunk_collection(a);
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-ORDER BY c["Id"]
-""");
- });
-
- public override Task Project_branch_required_required(bool async)
- => Fixture.NoSyncTest(
- async, async a =>
- {
- await base.Project_branch_required_required(a);
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-ORDER BY c["Id"]
-""");
- });
-
- public override Task Project_branch_required_optional(bool async)
- => Fixture.NoSyncTest(
- async, async a =>
- {
- await base.Project_branch_required_optional(a);
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-ORDER BY c["Id"]
-""");
- });
-
- public override async Task Project_branch_required_collection(bool async)
- {
- if (async)
- {
- //issue #31696
- await Assert.ThrowsAsync(
- () => base.Project_branch_required_collection(async));
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-ORDER BY c["Id"]
-""");
- }
- }
-
- public override Task Project_branch_optional_required(bool async)
- => Fixture.NoSyncTest(
- async, async a =>
- {
- await base.Project_branch_optional_required(a);
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-ORDER BY c["Id"]
-""");
- });
-
- public override Task Project_branch_optional_optional(bool async)
- => Fixture.NoSyncTest(
- async, async a =>
- {
- await base.Project_branch_optional_optional(a);
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-ORDER BY c["Id"]
-""");
- });
-
- public override async Task Project_branch_optional_collection(bool async)
- {
- if (async)
- {
- //issue #31696
- await Assert.ThrowsAsync(
- () => base.Project_branch_optional_collection(async));
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-ORDER BY c["Id"]
-""");
- }
- }
-
- public override async Task Project_branch_collection_element_using_indexer_constant(bool async)
- {
- if (async)
- {
- //issue #31696
- await Assert.ThrowsAsync(
- () => base.Project_branch_collection_element_using_indexer_constant(async));
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-ORDER BY c["Id"]
-""");
- }
- }
-
- public override Task Project_root_duplicated(bool async)
- => Fixture.NoSyncTest(
- async, async a =>
- {
- await base.Project_root_duplicated(a);
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-""");
- });
-
- public override async Task Project_trunk_and_branch_duplicated(bool async)
- {
- if (async)
- {
- //issue #31696
- await Assert.ThrowsAsync(
- () => base.Project_trunk_and_branch_duplicated(async));
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-ORDER BY c["Id"]
-""");
- }
- }
-
- public override async Task Project_trunk_and_trunk_duplicated(bool async)
- {
- if (async)
- {
- //issue #31696
- await Assert.ThrowsAsync(
- () => base.Project_trunk_and_trunk_duplicated(async));
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-ORDER BY c["Id"]
-""");
- }
- }
-
- public override async Task Project_multiple_branch_leaf(bool async)
- {
- if (async)
- {
- //issue #35702
- await Assert.ThrowsAsync(
- () => base.Project_multiple_branch_leaf(async));
-
- AssertSql();
- }
- }
-
- public override Task Project_leaf_trunk_root(bool async)
- => Fixture.NoSyncTest(
- async, async a =>
- {
- await base.Project_leaf_trunk_root(a);
-
- AssertSql(
- """
-SELECT VALUE c
-FROM root c
-""");
- });
-
-
- public override Task Project_subquery_root_set_required_trunk_FirstOrDefault_branch(bool async)
- => AssertTranslationFailed(
- () => base.Project_subquery_root_set_required_trunk_FirstOrDefault_branch(async));
-
- public override Task Project_subquery_root_set_optional_trunk_FirstOrDefault_branch(bool async)
- => AssertTranslationFailed(
- () => base.Project_subquery_root_set_optional_trunk_FirstOrDefault_branch(async));
-
- public override Task Project_subquery_root_set_trunk_FirstOrDefault_collection(bool async)
- => AssertTranslationFailed(
- () => base.Project_subquery_root_set_trunk_FirstOrDefault_collection(async));
-
- public override Task Project_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(bool async)
- => AssertTranslationFailed(
- () => base.Project_subquery_root_set_complex_projection_including_references_to_outer_FirstOrDefault(async));
-
- public override Task Project_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(bool async)
- => AssertTranslationFailed(
- () => base.Project_subquery_root_set_complex_projection_FirstOrDefault_project_reference_to_outer(async));
-
- public override async Task SelectMany_trunk_collection(bool async)
- {
- if (async)
- {
- //issue #34349
- await Assert.ThrowsAsync(
- () => base.SelectMany_trunk_collection(async));
-
- AssertSql();
- }
- }
-
- public override async Task SelectMany_required_trunk_reference_branch_collection(bool async)
- {
- if (async)
- {
- //issue #34349
- await Assert.ThrowsAsync(
- () => base.SelectMany_required_trunk_reference_branch_collection(async));
-
- AssertSql();
- }
- }
-
- public override async Task SelectMany_optional_trunk_reference_branch_collection(bool async)
- {
- if (async)
- {
- //issue #34349
- await Assert.ThrowsAsync(
- () => base.SelectMany_optional_trunk_reference_branch_collection(async));
-
- AssertSql();
- }
- }
-
- private void AssertSql(params string[] expected)
- => Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
-}
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/JsonRelationshipsQueryCosmosFixture.cs b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedJsonRelationshipsCosmosFixture.cs
similarity index 94%
rename from test/EFCore.Cosmos.FunctionalTests/Query/Relationships/JsonRelationshipsQueryCosmosFixture.cs
rename to test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedJsonRelationshipsCosmosFixture.cs
index 0444901a127..4bd8b155917 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/JsonRelationshipsQueryCosmosFixture.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/OwnedJsonRelationshipsCosmosFixture.cs
@@ -5,7 +5,7 @@
namespace Microsoft.EntityFrameworkCore.Query.Relationships;
-public class JsonRelationshipsQueryCosmosFixture : JsonRelationshipsQueryFixtureBase
+public class OwnedJsonRelationshipsCosmosFixture : OwnedJsonRelationshipsFixtureBase
{
public TestSqlLoggerFactory TestSqlLoggerFactory
=> (TestSqlLoggerFactory)ListLoggerFactory;
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/Projection/OwnedJsonNoTrackingProjectionCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/Projection/OwnedJsonNoTrackingProjectionCosmosTest.cs
new file mode 100644
index 00000000000..863a7153f00
--- /dev/null
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/Relationships/Projection/OwnedJsonNoTrackingProjectionCosmosTest.cs
@@ -0,0 +1,151 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Query.Relationships.Projection;
+
+public class OwnedJsonNoTrackingProjectionCosmosTest : OwnedJsonNoTrackingProjectionTestBase
+{
+ private readonly TrackingRewriter _trackingRewriter = new();
+
+ public OwnedJsonNoTrackingProjectionCosmosTest(OwnedJsonRelationshipsCosmosFixture fixture, ITestOutputHelper testOutputHelper)
+ : base(fixture)
+ {
+ Fixture.TestSqlLoggerFactory.Clear();
+ Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
+ }
+
+ protected override Expression RewriteServerQueryExpression(Expression serverQueryExpression)
+ {
+ var rewritten = _trackingRewriter.Visit(serverQueryExpression);
+
+ return rewritten;
+ }
+
+ public override Task Select_trunk_collection(bool async)
+ => Fixture.NoSyncTest(
+ async, async a =>
+ {
+ await base.Select_trunk_collection(a);
+
+ AssertSql(
+ """
+SELECT VALUE c
+FROM root c
+ORDER BY c["Id"]
+""");
+ });
+
+ public override async Task Select_branch_required_collection(bool async)
+ {
+ if (async)
+ {
+ //issue #31696
+ await Assert.ThrowsAsync