diff --git a/.editorconfig b/.editorconfig
index 246a4e1422f..fc773da07c9 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -91,9 +91,9 @@ csharp_style_var_elsewhere = true:suggestion
# Expression-bodied members
# Explicitly disabled due to difference in coding style between source and tests
-#csharp_style_expression_bodied_methods = false:silent
-#csharp_style_expression_bodied_constructors = false:silent
-csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_methods = true:suggestion
+csharp_style_expression_bodied_constructors = true:suggestion
+csharp_style_expression_bodied_operators = true:suggestion
csharp_style_expression_bodied_properties = true:suggestion
csharp_style_expression_bodied_indexers = true:suggestion
csharp_style_expression_bodied_accessors = true:suggestion
@@ -251,8 +251,5 @@ dotnet_naming_rule.everything_else_naming.symbols = everything_else
dotnet_naming_rule.everything_else_naming.style = camel_case_style
dotnet_naming_rule.everything_else_naming.severity = suggestion
-# Microsoft .NET properties
-csharp_style_expression_bodied_methods = true:suggestion
-
# ReSharper properties
resharper_local_function_body = expression_body
diff --git a/.gitattributes b/.gitattributes
index f5f8978580c..cc296e9aaab 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -2,3 +2,4 @@
*.cs diff=csharp
*.sh eol=lf
*.sln eol=crlf
+*.sql diff
diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md
index f588c1c5439..8e505319174 100644
--- a/.github/SUPPORT.md
+++ b/.github/SUPPORT.md
@@ -24,4 +24,4 @@ If you have a specific question about using the product, we encourage you to [as
Official Support
----------------
-Entity Framework Core is covered by Microsoft's [.NET Core Support Policy](https://dotnet.microsoft.com/platform/support/policy/dotnet-core). If you've tried all the optoins above and are still looking for help, you may wish to [contact a Microsoft Support professional](http://support.microsoft.com/supportforbusiness/productselection?sapId=bec2bc54-b200-6962-301f-f098532f27b2). Please note that personal help may incur a fee.
+Entity Framework Core is covered by Microsoft's [.NET Core Support Policy](https://dotnet.microsoft.com/platform/support/policy/dotnet-core). If you've tried all the options above and are still looking for help, you may wish to [contact a Microsoft Support professional](http://support.microsoft.com/supportforbusiness/productselection?sapId=bec2bc54-b200-6962-301f-f098532f27b2). Please note that personal help may incur a fee.
diff --git a/Directory.Build.props b/Directory.Build.props
index 80858aaddfc..27589f30050 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -55,9 +55,6 @@
$(NoWarn.Replace(';1591', ''))
-
-
-
diff --git a/Directory.Build.targets b/Directory.Build.targets
index 1ecc794c902..9044e67550f 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -11,13 +11,6 @@
-
-
-
-
+
+
+
+
+ true
+ true
+ true
+
+ $(NoWarn);NU1507
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/EFCore.sln b/EFCore.sln
index 3f1cd5ae7c2..22394d18d26 100644
--- a/EFCore.sln
+++ b/EFCore.sln
@@ -141,6 +141,8 @@ Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "EFCore.VisualBasic.Function
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EFCore.Tasks", "src\EFCore.Tasks\EFCore.Tasks.csproj", "{711EE8F3-F92D-4470-8B0B-25D8B13EF282}"
EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "EFCore.FSharp.FunctionalTests", "test\EFCore.FSharp.FunctionalTests\EFCore.FSharp.FunctionalTests.fsproj", "{89180105-1D98-4844-9C24-3A5DA2C53329}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -371,6 +373,10 @@ Global
{711EE8F3-F92D-4470-8B0B-25D8B13EF282}.Debug|Any CPU.Build.0 = Debug|Any CPU
{711EE8F3-F92D-4470-8B0B-25D8B13EF282}.Release|Any CPU.ActiveCfg = Release|Any CPU
{711EE8F3-F92D-4470-8B0B-25D8B13EF282}.Release|Any CPU.Build.0 = Release|Any CPU
+ {89180105-1D98-4844-9C24-3A5DA2C53329}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {89180105-1D98-4844-9C24-3A5DA2C53329}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {89180105-1D98-4844-9C24-3A5DA2C53329}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {89180105-1D98-4844-9C24-3A5DA2C53329}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -433,6 +439,7 @@ Global
{3D935B7D-80BD-49AD-BDC9-E1B0C9D9494F} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
{2AC6A8AC-5C0A-422A-B21A-CDC8D75F20A3} = {258D5057-81B9-40EC-A872-D21E27452749}
{711EE8F3-F92D-4470-8B0B-25D8B13EF282} = {CE6B50B2-34AE-44C9-940A-4E48C3E1B3BC}
+ {89180105-1D98-4844-9C24-3A5DA2C53329} = {258D5057-81B9-40EC-A872-D21E27452749}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {285A5EB4-BCF4-40EB-B9E1-DF6DBCB5E705}
diff --git a/NuGet.config b/NuGet.config
index d1a8a417e43..ec22121ce3c 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -12,10 +12,16 @@
+
+
+
+
+
+
diff --git a/azure-pipelines-public.yml b/azure-pipelines-public.yml
index cd521dd30b6..d56ef44140b 100644
--- a/azure-pipelines-public.yml
+++ b/azure-pipelines-public.yml
@@ -53,7 +53,7 @@ stages:
enablePublishTestResults: true
pool:
name: $(DncEngPublicBuildPool)
- demands: ImageOverride -equals 1es-windows-2019-open
+ demands: ImageOverride -equals 1es-windows-2022-open
timeoutInMinutes: 90
variables:
- _InternalBuildArgs: ''
@@ -143,7 +143,7 @@ stages:
timeoutInMinutes: 180
pool:
name: $(DncEngPublicBuildPool)
- demands: ImageOverride -equals 1es-windows-2019-open
+ demands: ImageOverride -equals 1es-windows-2022-open
variables:
# Rely on task Arcade injects, not auto-injected build step.
- skipComponentGovernanceDetection: true
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 64638066d6f..19604d65d27 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -56,6 +56,8 @@ extends:
baselineFile: $(Build.SourcesDirectory)\.config\guardian\.gdnbaselines
binskim:
scanOutputDirectoryOnly: true
+ policheck:
+ enabled: true
tsa:
enabled: true
customBuildTags:
@@ -250,4 +252,4 @@ extends:
enableSourceLinkValidation: false
publishAssetsImmediately: true
SDLValidationParameters:
- enable: false
\ No newline at end of file
+ enable: false
diff --git a/benchmark/Directory.Build.props b/benchmark/Directory.Build.props
index d92102a50e8..acb8c8be1b6 100644
--- a/benchmark/Directory.Build.props
+++ b/benchmark/Directory.Build.props
@@ -6,10 +6,10 @@
-
-
-
-
+
+
+
+
diff --git a/benchmark/Directory.Packages.props b/benchmark/Directory.Packages.props
new file mode 100644
index 00000000000..7d6fb856d91
--- /dev/null
+++ b/benchmark/Directory.Packages.props
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/benchmark/EFCore.Benchmarks/EFCore.Benchmarks.csproj b/benchmark/EFCore.Benchmarks/EFCore.Benchmarks.csproj
index f0bd47ad2fd..271d03e2cb8 100644
--- a/benchmark/EFCore.Benchmarks/EFCore.Benchmarks.csproj
+++ b/benchmark/EFCore.Benchmarks/EFCore.Benchmarks.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/benchmark/EFCore.Sqlite.Benchmarks/EFCore.Sqlite.Benchmarks.csproj b/benchmark/EFCore.Sqlite.Benchmarks/EFCore.Sqlite.Benchmarks.csproj
index 51ac02729fc..2eb2534dfb2 100644
--- a/benchmark/EFCore.Sqlite.Benchmarks/EFCore.Sqlite.Benchmarks.csproj
+++ b/benchmark/EFCore.Sqlite.Benchmarks/EFCore.Sqlite.Benchmarks.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 849d6cbbaed..33d4a6f3e00 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -1,71 +1,79 @@
-
+ https://github.com/dotnet/runtime
- 7cb32e193a55a95c74fc3bd56501b951b48b700f
+ 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11
-
+ https://github.com/dotnet/runtime
- 7cb32e193a55a95c74fc3bd56501b951b48b700f
+ 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11
-
+ https://github.com/dotnet/runtime
- 7cb32e193a55a95c74fc3bd56501b951b48b700f
+ 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11
-
+ https://github.com/dotnet/runtime
- 7cb32e193a55a95c74fc3bd56501b951b48b700f
+ 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11
-
+ https://github.com/dotnet/runtime
- 7cb32e193a55a95c74fc3bd56501b951b48b700f
+ 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11
-
+ https://github.com/dotnet/runtime
- 7cb32e193a55a95c74fc3bd56501b951b48b700f
+ 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11
-
+ https://github.com/dotnet/runtime
- 7cb32e193a55a95c74fc3bd56501b951b48b700f
+ 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11
-
+ https://github.com/dotnet/runtime
- 7cb32e193a55a95c74fc3bd56501b951b48b700f
+ 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11
-
+ https://github.com/dotnet/runtime
- 7cb32e193a55a95c74fc3bd56501b951b48b700f
+ 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11
-
+ https://github.com/dotnet/runtime
- 7cb32e193a55a95c74fc3bd56501b951b48b700f
+ 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11
-
+ https://github.com/dotnet/runtime
- 7cb32e193a55a95c74fc3bd56501b951b48b700f
+ 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11
-
+ https://github.com/dotnet/runtime
- 7cb32e193a55a95c74fc3bd56501b951b48b700f
+ 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11
+
+
+ https://github.com/dotnet/runtime
+ 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11
+
+
+ https://github.com/dotnet/runtime
+ 0372b5080a4aec8d73d6861ad4b993d9d0fe3a11
-
+ https://github.com/dotnet/arcade
- 60ae233c3d77f11c5fdb53e570b64d503b13ba59
+ f209a925b15bc66ecb9a8825bd9595937bbe3aa1
-
+ https://github.com/dotnet/arcade
- 60ae233c3d77f11c5fdb53e570b64d503b13ba59
+ f209a925b15bc66ecb9a8825bd9595937bbe3aa1
-
+ https://github.com/dotnet/arcade
- 60ae233c3d77f11c5fdb53e570b64d503b13ba59
+ f209a925b15bc66ecb9a8825bd9595937bbe3aa1
diff --git a/eng/Versions.props b/eng/Versions.props
index 8f7ab52831d..33ecdca09d5 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -1,7 +1,7 @@
- 9.0.0
- rc
+ 10.0.0
+ alpha1Falsetrue
@@ -16,31 +16,33 @@
False
- 9.0.0-rc.1.24410.5
- 9.0.0-rc.1.24410.5
- 9.0.0-rc.1.24410.5
- 9.0.0-rc.1.24410.5
- 9.0.0-rc.1.24410.5
- 9.0.0-rc.1.24410.5
- 9.0.0-rc.1.24410.5
- 9.0.0-rc.1.24410.5
- 9.0.0-rc.1.24410.5
- 9.0.0-rc.1.24410.5
- 9.0.0-rc.1.24410.5
- 9.0.0-rc.1.24410.5
+ 10.0.0-alpha.1.24504.10
+ 10.0.0-alpha.1.24504.10
+ 10.0.0-alpha.1.24504.10
+ 10.0.0-alpha.1.24504.10
+ 10.0.0-alpha.1.24504.10
+ 10.0.0-alpha.1.24504.10
+ 10.0.0-alpha.1.24504.10
+ 10.0.0-alpha.1.24504.10
+ 10.0.0-alpha.1.24504.10
+ 10.0.0-alpha.1.24504.10
+ 10.0.0-alpha.1.24504.10
+ 10.0.0-alpha.1.24504.10
+ 10.0.0-alpha.1.24504.10
+ 10.0.0-alpha.1.24504.10
- 9.0.0-beta.24408.2
+ 10.0.0-beta.24504.4
- 17.9.5
- 17.9.5
- 17.9.5
-
+ 17.8.3
+ 17.8.3
+
4.8.0
- 1.1.2-beta1.24121.1
- 1.11.3
+ 1.1.2
+ 1.12.11.3.21.8.1
+ 2.1.10
diff --git a/eng/common/SetupNugetSources.ps1 b/eng/common/SetupNugetSources.ps1
index 2b0a5c9e665..5db4ad71ee2 100644
--- a/eng/common/SetupNugetSources.ps1
+++ b/eng/common/SetupNugetSources.ps1
@@ -157,7 +157,7 @@ if ($dotnet31Source -ne $null) {
AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal-transport/nuget/v2" -Creds $creds -Username $userName -pwd $Password
}
-$dotnetVersions = @('5','6','7','8')
+$dotnetVersions = @('5','6','7','8','9')
foreach ($dotnetVersion in $dotnetVersions) {
$feedPrefix = "dotnet" + $dotnetVersion;
diff --git a/eng/common/SetupNugetSources.sh b/eng/common/SetupNugetSources.sh
index b493479a1da..4604b61b032 100644
--- a/eng/common/SetupNugetSources.sh
+++ b/eng/common/SetupNugetSources.sh
@@ -99,7 +99,7 @@ if [ "$?" == "0" ]; then
PackageSources+=('dotnet3.1-internal-transport')
fi
-DotNetVersions=('5' '6' '7' '8')
+DotNetVersions=('5' '6' '7' '8' '9')
for DotNetVersion in ${DotNetVersions[@]} ; do
FeedPrefix="dotnet${DotNetVersion}";
diff --git a/eng/common/core-templates/job/job.yml b/eng/common/core-templates/job/job.yml
index c732bee9f4a..c37d16634d1 100644
--- a/eng/common/core-templates/job/job.yml
+++ b/eng/common/core-templates/job/job.yml
@@ -19,6 +19,7 @@ parameters:
# publishing defaults
artifacts: ''
enableMicrobuild: false
+ enableMicrobuildForMacAndLinux: false
enablePublishBuildArtifacts: false
enablePublishBuildAssets: false
enablePublishTestResults: false
@@ -33,11 +34,6 @@ parameters:
artifactPublishSteps: []
runAsPublic: false
-# Sbom related params
- enableSbom: true
- PackageVersion: 9.0.0
- BuildDropPath: '$(Build.SourcesDirectory)/artifacts'
-
# 1es specific parameters
is1ESPipeline: ''
@@ -139,11 +135,26 @@ jobs:
signType: $(_SignType)
zipSources: false
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)'
env:
TeamName: $(_TeamName)
MicroBuildOutputFolderOverride: '$(Agent.TempDirectory)'
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
continueOnError: ${{ parameters.continueOnError }}
- condition: and(succeeded(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT'))
+ condition: and(
+ succeeded(),
+ or(
+ and(
+ eq(variables['Agent.Os'], 'Windows_NT'),
+ in(variables['_SignType'], 'real', 'test')
+ ),
+ and(
+ ${{ eq(parameters.enableMicrobuildForMacAndLinux, true) }},
+ ne(variables['Agent.Os'], 'Windows_NT'),
+ eq(variables['_SignType'], 'real')
+ )
+ ))
- ${{ if and(eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal')) }}:
- task: NuGetAuthenticate@1
@@ -176,7 +187,19 @@ jobs:
- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- task: MicroBuildCleanup@1
displayName: Execute Microbuild cleanup tasks
- condition: and(always(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT'))
+ condition: and(
+ always(),
+ or(
+ and(
+ eq(variables['Agent.Os'], 'Windows_NT'),
+ in(variables['_SignType'], 'real', 'test')
+ ),
+ and(
+ ${{ eq(parameters.enableMicrobuildForMacAndLinux, true) }},
+ ne(variables['Agent.Os'], 'Windows_NT'),
+ eq(variables['_SignType'], 'real')
+ )
+ ))
continueOnError: ${{ parameters.continueOnError }}
env:
TeamName: $(_TeamName)
diff --git a/eng/common/core-templates/job/source-index-stage1.yml b/eng/common/core-templates/job/source-index-stage1.yml
index 205fb5b3a39..30530359a5d 100644
--- a/eng/common/core-templates/job/source-index-stage1.yml
+++ b/eng/common/core-templates/job/source-index-stage1.yml
@@ -1,8 +1,5 @@
parameters:
runAsPublic: false
- sourceIndexUploadPackageVersion: 2.0.0-20240522.1
- sourceIndexProcessBinlogPackageVersion: 1.0.1-20240522.1
- sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json
sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci"
preSteps: []
binlogPath: artifacts/log/Debug/Build.binlog
@@ -16,12 +13,6 @@ jobs:
dependsOn: ${{ parameters.dependsOn }}
condition: ${{ parameters.condition }}
variables:
- - name: SourceIndexUploadPackageVersion
- value: ${{ parameters.sourceIndexUploadPackageVersion }}
- - name: SourceIndexProcessBinlogPackageVersion
- value: ${{ parameters.sourceIndexProcessBinlogPackageVersion }}
- - name: SourceIndexPackageSource
- value: ${{ parameters.sourceIndexPackageSource }}
- name: BinlogPath
value: ${{ parameters.binlogPath }}
- template: /eng/common/core-templates/variables/pool-providers.yml
@@ -34,12 +25,10 @@ jobs:
pool:
${{ if eq(variables['System.TeamProject'], 'public') }}:
name: $(DncEngPublicBuildPool)
- image: 1es-windows-2022-open
- os: windows
+ image: windows.vs2022.amd64.open
${{ if eq(variables['System.TeamProject'], 'internal') }}:
name: $(DncEngInternalBuildPool)
- image: 1es-windows-2022
- os: windows
+ image: windows.vs2022.amd64
steps:
- ${{ if eq(parameters.is1ESPipeline, '') }}:
@@ -47,35 +36,9 @@ jobs:
- ${{ each preStep in parameters.preSteps }}:
- ${{ preStep }}
-
- - task: UseDotNet@2
- displayName: Use .NET 8 SDK
- inputs:
- packageType: sdk
- version: 8.0.x
- installationPath: $(Agent.TempDirectory)/dotnet
- workingDirectory: $(Agent.TempDirectory)
-
- - script: |
- $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(sourceIndexProcessBinlogPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools
- $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(sourceIndexUploadPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools
- displayName: Download Tools
- # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk.
- workingDirectory: $(Agent.TempDirectory)
-
- script: ${{ parameters.sourceIndexBuildCommand }}
displayName: Build Repository
- - script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i $(BinlogPath) -r $(Build.SourcesDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output
- displayName: Process Binlog into indexable sln
-
- - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- - task: AzureCLI@2
- displayName: Log in to Azure and upload stage1 artifacts to source index
- inputs:
- azureSubscription: 'SourceDotNet Stage1 Publish'
- addSpnToEnvironment: true
- scriptType: 'ps'
- scriptLocation: 'inlineScript'
- inlineScript: |
- $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) -s netsourceindexstage1 -b stage1
+ - template: /eng/common/core-templates/steps/source-index-stage1-publish.yml
+ parameters:
+ binLogPath: ${{ parameters.binLogPath }}
\ No newline at end of file
diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml
index 80788c52319..de24d0087c5 100644
--- a/eng/common/core-templates/steps/publish-logs.yml
+++ b/eng/common/core-templates/steps/publish-logs.yml
@@ -34,7 +34,9 @@ steps:
'$(akams-client-id)'
'$(microsoft-symbol-server-pat)'
'$(symweb-symbol-server-pat)'
+ '$(dnceng-symbol-server-pat)'
'$(dn-bot-all-orgs-build-rw-code-rw)'
+ '$(System.AccessToken)'
${{parameters.CustomSensitiveDataList}}
continueOnError: true
condition: always()
@@ -45,6 +47,7 @@ steps:
SourceFolder: '$(Build.SourcesDirectory)/PostBuildLogs'
Contents: '**'
TargetFolder: '$(Build.ArtifactStagingDirectory)/PostBuildLogs'
+ condition: always()
- template: /eng/common/core-templates/steps/publish-build-artifacts.yml
parameters:
diff --git a/eng/common/core-templates/steps/source-index-stage1-publish.yml b/eng/common/core-templates/steps/source-index-stage1-publish.yml
new file mode 100644
index 00000000000..473a22c4719
--- /dev/null
+++ b/eng/common/core-templates/steps/source-index-stage1-publish.yml
@@ -0,0 +1,35 @@
+parameters:
+ sourceIndexUploadPackageVersion: 2.0.0-20240522.1
+ sourceIndexProcessBinlogPackageVersion: 1.0.1-20240522.1
+ sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json
+ binlogPath: artifacts/log/Debug/Build.binlog
+
+steps:
+- task: UseDotNet@2
+ displayName: "Source Index: Use .NET 8 SDK"
+ inputs:
+ packageType: sdk
+ version: 8.0.x
+ installationPath: $(Agent.TempDirectory)/dotnet
+ workingDirectory: $(Agent.TempDirectory)
+
+- script: |
+ $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version ${{parameters.sourceIndexProcessBinlogPackageVersion}} --add-source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools
+ $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version ${{parameters.sourceIndexUploadPackageVersion}} --add-source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools
+ displayName: "Source Index: Download netsourceindex Tools"
+ # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk.
+ workingDirectory: $(Agent.TempDirectory)
+
+- script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i ${{parameters.BinlogPath}} -r $(Build.SourcesDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output
+ displayName: "Source Index: Process Binlog into indexable sln"
+
+- ${{ if and(ne(parameters.runAsPublic, 'true'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
+ - task: AzureCLI@2
+ displayName: "Source Index: Upload Source Index stage1 artifacts to Azure"
+ inputs:
+ azureSubscription: 'SourceDotNet Stage1 Publish'
+ addSpnToEnvironment: true
+ scriptType: 'ps'
+ scriptLocation: 'inlineScript'
+ inlineScript: |
+ $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) -s netsourceindexstage1 -b stage1
diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh
index 4b5e8d7166b..20ae8c28687 100755
--- a/eng/common/cross/build-rootfs.sh
+++ b/eng/common/cross/build-rootfs.sh
@@ -66,6 +66,7 @@ __UbuntuPackages+=" libcurl4-openssl-dev"
__UbuntuPackages+=" libkrb5-dev"
__UbuntuPackages+=" libssl-dev"
__UbuntuPackages+=" zlib1g-dev"
+__UbuntuPackages+=" libbrotli-dev"
__AlpinePackages+=" curl-dev"
__AlpinePackages+=" krb5-dev"
@@ -91,18 +92,18 @@ __HaikuPackages="gcc_syslibs"
__HaikuPackages+=" gcc_syslibs_devel"
__HaikuPackages+=" gmp"
__HaikuPackages+=" gmp_devel"
-__HaikuPackages+=" icu66"
-__HaikuPackages+=" icu66_devel"
+__HaikuPackages+=" icu[0-9]+"
+__HaikuPackages+=" icu[0-9]*_devel"
__HaikuPackages+=" krb5"
__HaikuPackages+=" krb5_devel"
__HaikuPackages+=" libiconv"
__HaikuPackages+=" libiconv_devel"
-__HaikuPackages+=" llvm12_libunwind"
-__HaikuPackages+=" llvm12_libunwind_devel"
+__HaikuPackages+=" llvm[0-9]*_libunwind"
+__HaikuPackages+=" llvm[0-9]*_libunwind_devel"
__HaikuPackages+=" mpfr"
__HaikuPackages+=" mpfr_devel"
-__HaikuPackages+=" openssl"
-__HaikuPackages+=" openssl_devel"
+__HaikuPackages+=" openssl3"
+__HaikuPackages+=" openssl3_devel"
__HaikuPackages+=" zlib"
__HaikuPackages+=" zlib_devel"
@@ -496,7 +497,7 @@ if [[ "$__CodeName" == "alpine" ]]; then
arch="$(uname -m)"
ensureDownloadTool
-
+
if [[ "$__hasWget" == 1 ]]; then
wget -P "$__ApkToolsDir" "https://gitlab.alpinelinux.org/api/v4/projects/5/packages/generic/v$__ApkToolsVersion/$arch/apk.static"
else
@@ -681,7 +682,7 @@ elif [[ "$__CodeName" == "haiku" ]]; then
ensureDownloadTool
- echo "Downloading Haiku package tool"
+ echo "Downloading Haiku package tools"
git clone https://github.com/haiku/haiku-toolchains-ubuntu --depth 1 "$__RootfsDir/tmp/script"
if [[ "$__hasWget" == 1 ]]; then
wget -O "$__RootfsDir/tmp/download/hosttools.zip" "$("$__RootfsDir/tmp/script/fetch.sh" --hosttools)"
@@ -691,34 +692,42 @@ elif [[ "$__CodeName" == "haiku" ]]; then
unzip -o "$__RootfsDir/tmp/download/hosttools.zip" -d "$__RootfsDir/tmp/bin"
- DepotBaseUrl="https://depot.haiku-os.org/__api/v2/pkg/get-pkg"
- HpkgBaseUrl="https://eu.hpkg.haiku-os.org/haiku/master/$__HaikuArch/current"
+ HaikuBaseUrl="https://eu.hpkg.haiku-os.org/haiku/master/$__HaikuArch/current"
+ HaikuPortsBaseUrl="https://eu.hpkg.haiku-os.org/haikuports/master/$__HaikuArch/current"
+
+ echo "Downloading HaikuPorts package repository index..."
+ if [[ "$__hasWget" == 1 ]]; then
+ wget -P "$__RootfsDir/tmp/download" "$HaikuPortsBaseUrl/repo"
+ else
+ curl -SLO --create-dirs --output-dir "$__RootfsDir/tmp/download" "$HaikuPortsBaseUrl/repo"
+ fi
- # Download Haiku packages
echo "Downloading Haiku packages"
read -ra array <<<"$__HaikuPackages"
for package in "${array[@]}"; do
echo "Downloading $package..."
- # API documented here: https://github.com/haiku/haikudepotserver/blob/master/haikudepotserver-api2/src/main/resources/api2/pkg.yaml#L60
- # The schema here: https://github.com/haiku/haikudepotserver/blob/master/haikudepotserver-api2/src/main/resources/api2/pkg.yaml#L598
+ hpkgFilename="$(LD_LIBRARY_PATH="$__RootfsDir/tmp/bin" "$__RootfsDir/tmp/bin/package_repo" list -f "$__RootfsDir/tmp/download/repo" |
+ grep -E "${package}-" | sort -V | tail -n 1 | xargs)"
+ if [ -z "$hpkgFilename" ]; then
+ >&2 echo "ERROR: package $package missing."
+ exit 1
+ fi
+ echo "Resolved filename: $hpkgFilename..."
+ hpkgDownloadUrl="$HaikuPortsBaseUrl/packages/$hpkgFilename"
if [[ "$__hasWget" == 1 ]]; then
- hpkgDownloadUrl="$(wget -qO- --post-data '{"name":"'"$package"'","repositorySourceCode":"haikuports_'$__HaikuArch'","versionType":"LATEST","naturalLanguageCode":"en"}' \
- --header 'Content-Type:application/json' "$DepotBaseUrl" | jq -r '.result.versions[].hpkgDownloadURL')"
wget -P "$__RootfsDir/tmp/download" "$hpkgDownloadUrl"
else
- hpkgDownloadUrl="$(curl -sSL -XPOST --data '{"name":"'"$package"'","repositorySourceCode":"haikuports_'$__HaikuArch'","versionType":"LATEST","naturalLanguageCode":"en"}' \
- --header 'Content-Type:application/json' "$DepotBaseUrl" | jq -r '.result.versions[].hpkgDownloadURL')"
curl -SLO --create-dirs --output-dir "$__RootfsDir/tmp/download" "$hpkgDownloadUrl"
fi
done
for package in haiku haiku_devel; do
echo "Downloading $package..."
if [[ "$__hasWget" == 1 ]]; then
- hpkgVersion="$(wget -qO- "$HpkgBaseUrl" | sed -n 's/^.*version: "\([^"]*\)".*$/\1/p')"
- wget -P "$__RootfsDir/tmp/download" "$HpkgBaseUrl/packages/$package-$hpkgVersion-1-$__HaikuArch.hpkg"
+ hpkgVersion="$(wget -qO- "$HaikuBaseUrl" | sed -n 's/^.*version: "\([^"]*\)".*$/\1/p')"
+ wget -P "$__RootfsDir/tmp/download" "$HaikuBaseUrl/packages/$package-$hpkgVersion-1-$__HaikuArch.hpkg"
else
- hpkgVersion="$(curl -sSL "$HpkgBaseUrl" | sed -n 's/^.*version: "\([^"]*\)".*$/\1/p')"
- curl -SLO --create-dirs --output-dir "$__RootfsDir/tmp/download" "$HpkgBaseUrl/packages/$package-$hpkgVersion-1-$__HaikuArch.hpkg"
+ hpkgVersion="$(curl -sSL "$HaikuBaseUrl" | sed -n 's/^.*version: "\([^"]*\)".*$/\1/p')"
+ curl -SLO --create-dirs --output-dir "$__RootfsDir/tmp/download" "$HaikuBaseUrl/packages/$package-$hpkgVersion-1-$__HaikuArch.hpkg"
fi
done
diff --git a/eng/common/internal/Tools.csproj b/eng/common/internal/Tools.csproj
index e925952d566..32f79dfb340 100644
--- a/eng/common/internal/Tools.csproj
+++ b/eng/common/internal/Tools.csproj
@@ -4,6 +4,7 @@
net472false
+ false
diff --git a/eng/common/template-guidance.md b/eng/common/template-guidance.md
index 5ef6c30ba92..98bbc1ded0b 100644
--- a/eng/common/template-guidance.md
+++ b/eng/common/template-guidance.md
@@ -57,7 +57,7 @@ extends:
Note: Multiple outputs are ONLY applicable to 1ES PT publishing (only usable when referencing `templates-official`).
-# Development notes
+## Development notes
**Folder / file structure**
diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml
index 0c2928d5c79..605692d2fb7 100644
--- a/eng/common/templates-official/job/job.yml
+++ b/eng/common/templates-official/job/job.yml
@@ -1,8 +1,23 @@
+parameters:
+# Sbom related params
+ enableSbom: true
+ runAsPublic: false
+ PackageVersion: 9.0.0
+ BuildDropPath: '$(Build.SourcesDirectory)/artifacts'
+
jobs:
- template: /eng/common/core-templates/job/job.yml
parameters:
is1ESPipeline: true
+ componentGovernanceSteps:
+ - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}:
+ - template: /eng/common/templates/steps/generate-sbom.yml
+ parameters:
+ PackageVersion: ${{ parameters.packageVersion }}
+ BuildDropPath: ${{ parameters.buildDropPath }}
+ publishArtifacts: false
+
# publish artifacts
# for 1ES managed templates, use the templateContext.output to handle multiple outputs.
templateContext:
diff --git a/eng/common/templates-official/steps/source-index-stage1-publish.yml b/eng/common/templates-official/steps/source-index-stage1-publish.yml
new file mode 100644
index 00000000000..9b8b80942b5
--- /dev/null
+++ b/eng/common/templates-official/steps/source-index-stage1-publish.yml
@@ -0,0 +1,7 @@
+steps:
+- template: /eng/common/core-templates/steps/source-index-stage1-publish.yml
+ parameters:
+ is1ESPipeline: true
+
+ ${{ each parameter in parameters }}:
+ ${{ parameter.key }}: ${{ parameter.value }}
diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml
index 8da477dd69f..d1aeb92fcea 100644
--- a/eng/common/templates/job/job.yml
+++ b/eng/common/templates/job/job.yml
@@ -4,6 +4,7 @@ parameters:
componentGovernanceIgnoreDirectories: ''
# Sbom related params
enableSbom: true
+ runAsPublic: false
PackageVersion: 9.0.0
BuildDropPath: '$(Build.SourcesDirectory)/artifacts'
@@ -19,71 +20,63 @@ jobs:
steps:
- ${{ each step in parameters.steps }}:
- ${{ step }}
-
+
componentGovernanceSteps:
- - template: /eng/common/templates/steps/component-governance.yml
- parameters:
- ${{ if eq(parameters.disableComponentGovernance, '') }}:
- ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.runAsPublic, 'false'), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/dotnet/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/microsoft/'), eq(variables['Build.SourceBranch'], 'refs/heads/main'))) }}:
- disableComponentGovernance: false
- ${{ else }}:
- disableComponentGovernance: true
+ - template: /eng/common/templates/steps/component-governance.yml
+ parameters:
+ ${{ if eq(parameters.disableComponentGovernance, '') }}:
+ ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.runAsPublic, 'false'), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/dotnet/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/microsoft/'), eq(variables['Build.SourceBranch'], 'refs/heads/main'))) }}:
+ disableComponentGovernance: false
${{ else }}:
- disableComponentGovernance: ${{ parameters.disableComponentGovernance }}
- componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }}
-
- - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}:
- - template: /eng/common/templates/steps/generate-sbom.yml
- parameters:
- PackageVersion: ${{ parameters.packageVersion }}
- BuildDropPath: ${{ parameters.buildDropPath }}
- publishArtifacts: false
-
+ disableComponentGovernance: true
+ ${{ else }}:
+ disableComponentGovernance: ${{ parameters.disableComponentGovernance }}
+ componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }}
artifactPublishSteps:
- - ${{ if ne(parameters.artifacts.publish, '') }}:
- - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}:
- - template: /eng/common/core-templates/steps/publish-build-artifacts.yml
- parameters:
- is1ESPipeline: false
- args:
- displayName: Publish pipeline artifacts
- pathToPublish: '$(Build.ArtifactStagingDirectory)/artifacts'
- publishLocation: Container
- artifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }}
- continueOnError: true
- condition: always()
- - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}:
- - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml
- parameters:
- is1ESPipeline: false
- args:
- targetPath: '$(Build.ArtifactStagingDirectory)/artifacts/log'
- artifactName: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)') }}
- displayName: 'Publish logs'
- continueOnError: true
- condition: always()
- sbomEnabled: false # we don't need SBOM for logs
-
- - ${{ if ne(parameters.enablePublishBuildArtifacts, 'false') }}:
+ - ${{ if ne(parameters.artifacts.publish, '') }}:
+ - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}:
- template: /eng/common/core-templates/steps/publish-build-artifacts.yml
parameters:
is1ESPipeline: false
args:
- displayName: Publish Logs
- pathToPublish: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)'
+ displayName: Publish pipeline artifacts
+ pathToPublish: '$(Build.ArtifactStagingDirectory)/artifacts'
publishLocation: Container
- artifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }}
+ artifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }}
continueOnError: true
condition: always()
-
- - ${{ if eq(parameters.enableBuildRetry, 'true') }}:
+ - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}:
- template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml
parameters:
is1ESPipeline: false
args:
- targetPath: '$(Build.SourcesDirectory)\eng\common\BuildConfiguration'
- artifactName: 'BuildConfiguration'
- displayName: 'Publish build retry configuration'
+ targetPath: '$(Build.ArtifactStagingDirectory)/artifacts/log'
+ artifactName: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)') }}
+ displayName: 'Publish logs'
continueOnError: true
- sbomEnabled: false # we don't need SBOM for BuildConfiguration
+ condition: always()
+ sbomEnabled: false # we don't need SBOM for logs
+
+ - ${{ if ne(parameters.enablePublishBuildArtifacts, 'false') }}:
+ - template: /eng/common/core-templates/steps/publish-build-artifacts.yml
+ parameters:
+ is1ESPipeline: false
+ args:
+ displayName: Publish Logs
+ pathToPublish: '$(Build.ArtifactStagingDirectory)/artifacts/log/$(_BuildConfig)'
+ publishLocation: Container
+ artifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }}
+ continueOnError: true
+ condition: always()
+
+ - ${{ if eq(parameters.enableBuildRetry, 'true') }}:
+ - template: /eng/common/core-templates/steps/publish-pipeline-artifacts.yml
+ parameters:
+ is1ESPipeline: false
+ args:
+ targetPath: '$(Build.SourcesDirectory)\eng\common\BuildConfiguration'
+ artifactName: 'BuildConfiguration'
+ displayName: 'Publish build retry configuration'
+ continueOnError: true
+ sbomEnabled: false # we don't need SBOM for BuildConfiguration
diff --git a/eng/common/templates/steps/source-index-stage1-publish.yml b/eng/common/templates/steps/source-index-stage1-publish.yml
new file mode 100644
index 00000000000..182cec33a7b
--- /dev/null
+++ b/eng/common/templates/steps/source-index-stage1-publish.yml
@@ -0,0 +1,7 @@
+steps:
+- template: /eng/common/core-templates/steps/source-index-stage1-publish.yml
+ parameters:
+ is1ESPipeline: false
+
+ ${{ each parameter in parameters }}:
+ ${{ parameter.key }}: ${{ parameter.value }}
diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1
index 9574f4eb9df..22954477a57 100644
--- a/eng/common/tools.ps1
+++ b/eng/common/tools.ps1
@@ -900,7 +900,7 @@ function IsWindowsPlatform() {
}
function Get-Darc($version) {
- $darcPath = "$TempDir\darc\$(New-Guid)"
+ $darcPath = "$TempDir\darc\$([guid]::NewGuid())"
if ($version -ne $null) {
& $PSScriptRoot\darc-init.ps1 -toolpath $darcPath -darcVersion $version | Out-Host
} else {
diff --git a/github-merge-flow.jsonc b/github-merge-flow.jsonc
index 8dd6479f54e..9232211c085 100644
--- a/github-merge-flow.jsonc
+++ b/github-merge-flow.jsonc
@@ -1,7 +1,23 @@
// IMPORTANT: This file is read by the merge flow from main branch only.
{
"merge-flow-configurations": {
+ // Automate opening PRs to merge release/8.0 to release/9.0
"release/8.0":{
+ "MergeToBranch": "release/9.0",
+ "ExtraSwitches": "-QuietComments"
+ },
+ // Automate opening PRs to merge release/9.0-rc1 to release/9.0
+ "release/9.0-rc1":{
+ "MergeToBranch": "release/9.0",
+ "ExtraSwitches": "-QuietComments"
+ },
+ // Automate opening PRs to merge release/9.0-rc2 to release/9.0
+ "release/9.0-rc2":{
+ "MergeToBranch": "release/9.0",
+ "ExtraSwitches": "-QuietComments"
+ },
+ // Automate opening PRs to merge release/9.0 to main
+ "release/9.0":{
"MergeToBranch": "main",
"ExtraSwitches": "-QuietComments"
}
diff --git a/global.json b/global.json
index ff47bc6399d..03a0a166315 100644
--- a/global.json
+++ b/global.json
@@ -1,11 +1,11 @@
{
"sdk": {
- "version": "9.0.100-preview.5.24307.3",
+ "version": "9.0.100-rc.1.24452.12",
"allowPrerelease": true,
"rollForward": "latestMajor"
},
"tools": {
- "dotnet": "9.0.100-preview.5.24307.3",
+ "dotnet": "9.0.100-rc.1.24452.12",
"runtimes": {
"dotnet": [
"$(MicrosoftNETCoreAppRuntimewinx64Version)"
@@ -13,7 +13,7 @@
}
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.24408.2",
- "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.24408.2"
+ "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.24504.4",
+ "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.24504.4"
}
}
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index f202e2801f2..e65be73b154 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -10,7 +10,7 @@
-
+
diff --git a/src/EFCore.Abstractions/DeleteBehaviorAttribute.cs b/src/EFCore.Abstractions/DeleteBehaviorAttribute.cs
index bc282f5b2a0..bb71f6e145a 100644
--- a/src/EFCore.Abstractions/DeleteBehaviorAttribute.cs
+++ b/src/EFCore.Abstractions/DeleteBehaviorAttribute.cs
@@ -19,9 +19,7 @@ public sealed class DeleteBehaviorAttribute : Attribute
///
/// The to be configured.
public DeleteBehaviorAttribute(DeleteBehavior behavior)
- {
- Behavior = behavior;
- }
+ => Behavior = behavior;
///
/// Gets the to be configured.
diff --git a/src/EFCore.Abstractions/UnicodeAttribute.cs b/src/EFCore.Abstractions/UnicodeAttribute.cs
index 1cc4559bd02..1c7a9253fa9 100644
--- a/src/EFCore.Abstractions/UnicodeAttribute.cs
+++ b/src/EFCore.Abstractions/UnicodeAttribute.cs
@@ -17,9 +17,7 @@ public sealed class UnicodeAttribute : Attribute
///
/// A value indicating whether the property can contain unicode characters or not.
public UnicodeAttribute(bool unicode = true)
- {
- IsUnicode = unicode;
- }
+ => IsUnicode = unicode;
///
/// A value indicating whether the property can contain unicode characters or not.
diff --git a/src/EFCore.Analyzers/EFCore.Analyzers.csproj b/src/EFCore.Analyzers/EFCore.Analyzers.csproj
index beacecfce80..4b145c8c2a1 100644
--- a/src/EFCore.Analyzers/EFCore.Analyzers.csproj
+++ b/src/EFCore.Analyzers/EFCore.Analyzers.csproj
@@ -10,7 +10,7 @@
$(MSBuildThisFileDirectory)..\..\rulesets\EFCore.noxmldocs.rulesettruetrue
- NU5128
+ $(NoWarn);NU5128
@@ -29,8 +29,7 @@
-
-
+
@@ -60,4 +59,8 @@
+
+
+
+
diff --git a/src/EFCore.Cosmos/ChangeTracking/Internal/NullableEqualityComparer.cs b/src/EFCore.Cosmos/ChangeTracking/Internal/NullableEqualityComparer.cs
index 7a83743c708..5d5e3abe2aa 100644
--- a/src/EFCore.Cosmos/ChangeTracking/Internal/NullableEqualityComparer.cs
+++ b/src/EFCore.Cosmos/ChangeTracking/Internal/NullableEqualityComparer.cs
@@ -21,9 +21,7 @@ public class NullableEqualityComparer : IEqualityComparer
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public NullableEqualityComparer(IEqualityComparer underlyingComparer)
- {
- _underlyingComparer = underlyingComparer;
- }
+ => _underlyingComparer = underlyingComparer;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Cosmos/ChangeTracking/Internal/StringDictionaryComparer.cs b/src/EFCore.Cosmos/ChangeTracking/Internal/StringDictionaryComparer.cs
index 21b8f7fbf53..aa4589d54b8 100644
--- a/src/EFCore.Cosmos/ChangeTracking/Internal/StringDictionaryComparer.cs
+++ b/src/EFCore.Cosmos/ChangeTracking/Internal/StringDictionaryComparer.cs
@@ -34,9 +34,7 @@ public StringDictionaryComparer(ValueComparer elementComparer)
CompareLambda(elementComparer),
GetHashCodeLambda(elementComparer),
SnapshotLambda(elementComparer))
- {
- ElementComparer = elementComparer;
- }
+ => ElementComparer = elementComparer;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -46,7 +44,8 @@ public StringDictionaryComparer(ValueComparer elementComparer)
///
public ValueComparer ElementComparer { get; }
- ValueComparer IInfrastructure.Instance => ElementComparer;
+ ValueComparer IInfrastructure.Instance
+ => ElementComparer;
private static Expression> CompareLambda(ValueComparer elementComparer)
{
diff --git a/src/EFCore.Cosmos/Diagnostics/CosmosEventId.cs b/src/EFCore.Cosmos/Diagnostics/CosmosEventId.cs
index dfbc3cceb71..aa0940b2bac 100644
--- a/src/EFCore.Cosmos/Diagnostics/CosmosEventId.cs
+++ b/src/EFCore.Cosmos/Diagnostics/CosmosEventId.cs
@@ -41,7 +41,6 @@ private enum Id
// Model validation events
NoPartitionKeyDefined = CoreEventId.ProviderBaseId + 600,
-
}
private static readonly string DatabasePrefix = DbLoggerCategory.Database.Name + ".";
diff --git a/src/EFCore.Cosmos/Diagnostics/Internal/CosmosLoggerExtensions.cs b/src/EFCore.Cosmos/Diagnostics/Internal/CosmosLoggerExtensions.cs
index 1c6d629221d..bf458d70a81 100644
--- a/src/EFCore.Cosmos/Diagnostics/Internal/CosmosLoggerExtensions.cs
+++ b/src/EFCore.Cosmos/Diagnostics/Internal/CosmosLoggerExtensions.cs
@@ -508,7 +508,8 @@ public static void PrimaryKeyValueNotSet(
{
var eventData = new PropertyEventData(
definition,
- (d, p) => ((EventDefinition)d).GenerateMessage(((PropertyEventData)p).Property.DeclaringType.DisplayName(), ((PropertyEventData)p).Property.Name),
+ (d, p) => ((EventDefinition)d).GenerateMessage(
+ ((PropertyEventData)p).Property.DeclaringType.DisplayName(), ((PropertyEventData)p).Property.Name),
property);
diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
}
diff --git a/src/EFCore.Cosmos/EFCore.Cosmos.csproj b/src/EFCore.Cosmos/EFCore.Cosmos.csproj
index 4b064e25562..43c07d376d6 100644
--- a/src/EFCore.Cosmos/EFCore.Cosmos.csproj
+++ b/src/EFCore.Cosmos/EFCore.Cosmos.csproj
@@ -43,12 +43,12 @@
-
+
-
+
diff --git a/src/EFCore.Cosmos/Extensions/CosmosDbFunctionsExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosDbFunctionsExtensions.cs
index f562fa59c36..3dc681450be 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosDbFunctionsExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosDbFunctionsExtensions.cs
@@ -43,7 +43,9 @@ public static bool IsDefined(this DbFunctions _, object? expression)
/// will be returned.
///
/// The expression to be returned if is undefined.
- /// Cosmos coalesce operator
+ ///
+ /// Cosmos coalesce operator
+ ///
public static T CoalesceUndefined(
this DbFunctions _,
T expression1,
@@ -52,7 +54,9 @@ public static T CoalesceUndefined(
///
/// Returns the distance between two vectors, using the distance function and data type defined using
- /// .
+ ///
+ /// .
///
/// The instance.
/// The first vector.
@@ -67,9 +71,11 @@ public static double VectorDistance(this DbFunctions _, ReadOnlyMemory vec
/// The instance.
/// The first vector.
/// The second vector.
- /// A specifying how the computed value is used in an ORDER BY
- /// expression. If , then brute force is used, otherwise any index defined on the vector
- /// property is leveraged.
+ ///
+ /// A specifying how the computed value is used in an ORDER BY
+ /// expression. If , then brute force is used, otherwise any index defined on the vector
+ /// property is leveraged.
+ ///
[Experimental(EFDiagnostics.CosmosVectorSearchExperimental)]
public static double VectorDistance(
this DbFunctions _,
@@ -85,9 +91,11 @@ public static double VectorDistance(
/// The first vector.
/// The second vector.
/// The distance function to use.
- /// A specifying how the computed value is used in an ORDER BY
- /// expression. If , then brute force is used, otherwise any index defined on the vector
- /// property is leveraged.
+ ///
+ /// A specifying how the computed value is used in an ORDER BY
+ /// expression. If , then brute force is used, otherwise any index defined on the vector
+ /// property is leveraged.
+ ///
[Experimental(EFDiagnostics.CosmosVectorSearchExperimental)]
public static double VectorDistance(
this DbFunctions _,
@@ -99,7 +107,9 @@ public static double VectorDistance(
///
/// Returns the distance between two vectors, using the distance function and data type defined using
- /// .
+ ///
+ /// .
///
/// The instance.
/// The first vector.
@@ -114,9 +124,11 @@ public static double VectorDistance(this DbFunctions _, ReadOnlyMemory ve
/// The instance.
/// The first vector.
/// The second vector.
- /// A specifying how the computed value is used in an ORDER BY
- /// expression. If , then brute force is used, otherwise any index defined on the vector
- /// property is leveraged.
+ ///
+ /// A specifying how the computed value is used in an ORDER BY
+ /// expression. If , then brute force is used, otherwise any index defined on the vector
+ /// property is leveraged.
+ ///
[Experimental(EFDiagnostics.CosmosVectorSearchExperimental)]
public static double VectorDistance(
this DbFunctions _,
@@ -132,9 +144,11 @@ public static double VectorDistance(
/// The first vector.
/// The second vector.
/// The distance function to use.
- /// A specifying how the computed value is used in an ORDER BY
- /// expression. If , then brute force is used, otherwise any index defined on the vector
- /// property is leveraged.
+ ///
+ /// A specifying how the computed value is used in an ORDER BY
+ /// expression. If , then brute force is used, otherwise any index defined on the vector
+ /// property is leveraged.
+ ///
[Experimental(EFDiagnostics.CosmosVectorSearchExperimental)]
public static double VectorDistance(
this DbFunctions _,
@@ -146,7 +160,9 @@ public static double VectorDistance(
///
/// Returns the distance between two vectors, using the distance function and data type defined using
- /// .
+ ///
+ /// .
///
/// The instance.
/// The first vector.
@@ -161,9 +177,11 @@ public static double VectorDistance(this DbFunctions _, ReadOnlyMemory ve
/// The instance.
/// The first vector.
/// The second vector.
- /// A specifying how the computed value is used in an ORDER BY
- /// expression. If , then brute force is used, otherwise any index defined on the vector
- /// property is leveraged.
+ ///
+ /// A specifying how the computed value is used in an ORDER BY
+ /// expression. If , then brute force is used, otherwise any index defined on the vector
+ /// property is leveraged.
+ ///
[Experimental(EFDiagnostics.CosmosVectorSearchExperimental)]
public static double VectorDistance(
this DbFunctions _,
@@ -179,9 +197,11 @@ public static double VectorDistance(
/// The first vector.
/// The second vector.
/// The distance function to use.
- /// A specifying how the computed value is used in an ORDER BY
- /// expression. If , then brute force is used, otherwise any index defined on the vector
- /// property is leveraged.
+ ///
+ /// A specifying how the computed value is used in an ORDER BY
+ /// expression. If , then brute force is used, otherwise any index defined on the vector
+ /// property is leveraged.
+ ///
[Experimental(EFDiagnostics.CosmosVectorSearchExperimental)]
public static double VectorDistance(
this DbFunctions _,
diff --git a/src/EFCore.Cosmos/Extensions/CosmosEntityTypeBuilderExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosEntityTypeBuilderExtensions.cs
index f62d3e37777..e801beb2235 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosEntityTypeBuilderExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosEntityTypeBuilderExtensions.cs
@@ -531,7 +531,7 @@ public static EntityTypeBuilder HasDiscriminatorInJsonId(
/// to revert to the default setting.
///
/// The same builder instance so that multiple calls can be chained.
- public static EntityTypeBuilder IncludeRootDiscriminatorInJsonId(
+ public static EntityTypeBuilder HasRootDiscriminatorInJsonId(
this EntityTypeBuilder entityTypeBuilder,
bool? includeDiscriminator = true)
{
@@ -559,7 +559,8 @@ public static EntityTypeBuilder IncludeRootDiscriminatorInJsonId(
///
/// The same builder instance so that multiple calls can be chained.
public static EntityTypeBuilder HasDiscriminatorInJsonId(
- this EntityTypeBuilder entityTypeBuilder, bool? includeDiscriminator = true)
+ this EntityTypeBuilder entityTypeBuilder,
+ bool? includeDiscriminator = true)
where TEntity : class
=> (EntityTypeBuilder)HasDiscriminatorInJsonId((EntityTypeBuilder)entityTypeBuilder, includeDiscriminator);
@@ -577,10 +578,11 @@ public static EntityTypeBuilder HasDiscriminatorInJsonId(
/// to revert to the default setting.
///
/// The same builder instance so that multiple calls can be chained.
- public static EntityTypeBuilder IncludeRootDiscriminatorInJsonId(
- this EntityTypeBuilder entityTypeBuilder, bool? includeDiscriminator = true)
+ public static EntityTypeBuilder HasRootDiscriminatorInJsonId(
+ this EntityTypeBuilder entityTypeBuilder,
+ bool? includeDiscriminator = true)
where TEntity : class
- => (EntityTypeBuilder)IncludeRootDiscriminatorInJsonId((EntityTypeBuilder)entityTypeBuilder, includeDiscriminator);
+ => (EntityTypeBuilder)HasRootDiscriminatorInJsonId((EntityTypeBuilder)entityTypeBuilder, includeDiscriminator);
///
/// Includes the discriminator value of the entity type in the JSON "id" value. This was the default behavior before EF Core 9.
@@ -597,7 +599,9 @@ public static EntityTypeBuilder IncludeRootDiscriminatorInJsonIdIndicates whether the configuration was specified using a data annotation.
/// The same builder instance if the configuration was applied, otherwise.
public static IConventionEntityTypeBuilder? HasDiscriminatorInJsonId(
- this IConventionEntityTypeBuilder entityTypeBuilder, bool? includeDiscriminator, bool fromDataAnnotation = false)
+ this IConventionEntityTypeBuilder entityTypeBuilder,
+ bool? includeDiscriminator,
+ bool fromDataAnnotation = false)
{
if (!entityTypeBuilder.CanSetDiscriminatorInJsonId(includeDiscriminator, fromDataAnnotation))
{
@@ -629,10 +633,12 @@ public static EntityTypeBuilder IncludeRootDiscriminatorInJsonId
/// Indicates whether the configuration was specified using a data annotation.
/// The same builder instance if the configuration was applied, otherwise.
- public static IConventionEntityTypeBuilder? IncludeRootDiscriminatorInJsonId(
- this IConventionEntityTypeBuilder entityTypeBuilder, bool? includeDiscriminator, bool fromDataAnnotation = false)
+ public static IConventionEntityTypeBuilder? HasRootDiscriminatorInJsonId(
+ this IConventionEntityTypeBuilder entityTypeBuilder,
+ bool? includeDiscriminator,
+ bool fromDataAnnotation = false)
{
- if (!entityTypeBuilder.CanSetIncludeRootDiscriminatorInJsonId(includeDiscriminator, fromDataAnnotation))
+ if (!entityTypeBuilder.CanSetRootDiscriminatorInJsonId(includeDiscriminator, fromDataAnnotation))
{
return null;
}
@@ -678,7 +684,6 @@ public static bool CanSetDiscriminatorInJsonId(
: IdDiscriminatorMode.None, fromDataAnnotation);
}
-
///
/// Returns a value indicating whether the setting for including the discriminator can be set
/// from the current configuration source
@@ -694,7 +699,7 @@ public static bool CanSetDiscriminatorInJsonId(
///
/// Indicates whether the configuration was specified using a data annotation.
/// if the configuration can be applied.
- public static bool CanSetIncludeRootDiscriminatorInJsonId(
+ public static bool CanSetRootDiscriminatorInJsonId(
this IConventionEntityTypeBuilder entityTypeBuilder,
bool? includeDiscriminator,
bool fromDataAnnotation = false)
diff --git a/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs
index 9ca08c71e0a..f162fb711ef 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs
@@ -72,8 +72,12 @@ public static void SetContainer(this IMutableEntityType entityType, string? name
/// The entity type to get the containing property name for.
/// The name of the parent property to which the entity type is mapped.
public static string? GetContainingPropertyName(this IReadOnlyEntityType entityType)
- => entityType[CosmosAnnotationNames.PropertyName] as string
- ?? GetDefaultContainingPropertyName(entityType);
+ {
+ var propertyName = entityType.FindAnnotation(CosmosAnnotationNames.PropertyName);
+ return propertyName == null
+ ? GetDefaultContainingPropertyName(entityType)
+ : (string?)propertyName.Value;
+ }
private static string? GetDefaultContainingPropertyName(IReadOnlyEntityType entityType)
=> entityType.FindOwnership() is IReadOnlyForeignKey ownership
@@ -194,17 +198,17 @@ public static void SetPartitionKeyPropertyName(this IMutableEntityType entityTyp
/// Returns the names of the properties that are used to store the hierarchical partition key, if any.
///
/// The entity type.
- /// The names of the partition key properties, or if not set.
+ /// The names of the partition key properties, or if not set.
public static IReadOnlyList GetPartitionKeyPropertyNames(this IReadOnlyEntityType entityType)
=> entityType[CosmosAnnotationNames.PartitionKeyNames] as IReadOnlyList
?? entityType.BaseType?.GetPartitionKeyPropertyNames()
- ?? Array.Empty();
+ ?? [];
///
/// Sets the names of the properties that are used to store the hierarchical partition key.
///
/// The entity type.
- /// The names to set, or to clear all names.
+ /// The names to set, or to clear all names.
public static void SetPartitionKeyPropertyNames(this IMutableEntityType entityType, IReadOnlyList? names)
=> entityType.SetOrRemoveAnnotation(
CosmosAnnotationNames.PartitionKeyNames, names is null ? names : Check.HasNoEmptyElements(names, nameof(names)));
@@ -352,7 +356,7 @@ public static void SetETagPropertyName(this IMutableEntityType entityType, strin
///
/// to force __id creation, to not force __id creation,
/// to revert to the default setting.
- /// .
+ ///
public static bool? GetHasShadowId(this IReadOnlyEntityType entityType)
=> (entityType.BaseType != null
? entityType.GetRootType().GetHasShadowId()
@@ -389,7 +393,7 @@ public static void SetHasShadowId(this IMutableEntityType entityType, bool? alwa
CosmosAnnotationNames.HasShadowId, alwaysCreate, fromDataAnnotation)?.Value;
///
- /// Gets the for .
+ /// Gets the for .
///
/// The entity typer.
/// The .
@@ -401,7 +405,7 @@ public static void SetHasShadowId(this IMutableEntityType entityType, bool? alwa
/// Prior to EF Core 9, it was always included. Starting with EF Core 9, it is not included by default.
///
/// The entity type.
- /// The or if not set.
+ /// The or if not set.
public static IdDiscriminatorMode? GetDiscriminatorInKey(this IReadOnlyEntityType entityType)
=> (entityType.BaseType != null
? entityType.GetRootType().GetDiscriminatorInKey()
@@ -423,12 +427,14 @@ public static void SetDiscriminatorInKey(this IMutableEntityType entityType, IdD
/// The behavior to use, or to reset the behavior to the default.
/// Indicates whether the configuration was specified using a data annotation.
public static IdDiscriminatorMode? SetDiscriminatorInKey(
- this IConventionEntityType entityType, IdDiscriminatorMode? behavior, bool fromDataAnnotation = false)
+ this IConventionEntityType entityType,
+ IdDiscriminatorMode? behavior,
+ bool fromDataAnnotation = false)
=> (IdDiscriminatorMode?)entityType.SetOrRemoveAnnotation(
CosmosAnnotationNames.DiscriminatorInKey, behavior, fromDataAnnotation)?.Value;
///
- /// Gets the for .
+ /// Gets the for .
///
/// The entity typer.
/// The .
diff --git a/src/EFCore.Cosmos/Extensions/CosmosIndexBuilderExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosIndexBuilderExtensions.cs
index ed0a40b8ce7..217e3691eae 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosIndexBuilderExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosIndexBuilderExtensions.cs
@@ -8,7 +8,7 @@
namespace Microsoft.EntityFrameworkCore;
///
-/// Azure Cosmos DB-specific extension methods for .
+/// Azure Cosmos DB-specific extension methods for .
///
///
/// See Modeling entity types and relationships, and
diff --git a/src/EFCore.Cosmos/Extensions/CosmosIndexExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosIndexExtensions.cs
index 9692a128068..6f9ab7b6184 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosIndexExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosIndexExtensions.cs
@@ -55,7 +55,7 @@ public static void SetVectorIndexType(this IMutableIndex index, VectorIndexType?
fromDataAnnotation)?.Value;
///
- /// Returns the for whether the .
+ /// Returns the for whether the .
///
/// The property.
/// The for whether the index is clustered.
diff --git a/src/EFCore.Cosmos/Extensions/CosmosModelBuilderExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosModelBuilderExtensions.cs
index f4a8c1e91df..ad6064ebab2 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosModelBuilderExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosModelBuilderExtensions.cs
@@ -165,7 +165,7 @@ public static ModelBuilder HasDiscriminatorInJsonIds(
/// to revert to the default setting.
///
/// The same builder instance so that multiple calls can be chained.
- public static ModelBuilder IncludeRootDiscriminatorInJsonId(
+ public static ModelBuilder HasRootDiscriminatorInJsonId(
this ModelBuilder modelBuilder,
bool? includeDiscriminator = true)
{
diff --git a/src/EFCore.Cosmos/Extensions/CosmosModelExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosModelExtensions.cs
index e187219cb8b..da9671f361a 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosModelExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosModelExtensions.cs
@@ -96,7 +96,7 @@ public static void SetHasShadowIds(this IMutableModel model, bool? alwaysCreate)
///
/// Gets the
- /// for .
+ /// for .
///
/// The model.
/// The .
@@ -108,7 +108,7 @@ public static void SetHasShadowIds(this IMutableModel model, bool? alwaysCreate)
/// Prior to EF Core 9, it was always included. Starting with EF Core 9, it is not included by default.
///
/// The model.
- /// The or if not set.
+ /// The or if not set.
public static IdDiscriminatorMode? GetDiscriminatorInKey(this IReadOnlyModel model)
=> (IdDiscriminatorMode?)model[CosmosAnnotationNames.DiscriminatorInKey];
@@ -135,7 +135,7 @@ public static void SetDiscriminatorInKey(this IMutableModel model, IdDiscriminat
///
/// Gets the
- /// for .
+ /// for .
///
/// The model.
/// The .
diff --git a/src/EFCore.Cosmos/Extensions/CosmosPropertyExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosPropertyExtensions.cs
index c4e0cb43466..6439a41eee7 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosPropertyExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosPropertyExtensions.cs
@@ -109,7 +109,10 @@ public static void SetVectorType(this IMutableProperty property, CosmosVectorTyp
/// Indicates whether the configuration was specified using a data annotation.
/// The configured value.
[Experimental(EFDiagnostics.CosmosVectorSearchExperimental)]
- public static CosmosVectorType? SetVectorType(this IConventionProperty property, CosmosVectorType? vectorType, bool fromDataAnnotation = false)
+ public static CosmosVectorType? SetVectorType(
+ this IConventionProperty property,
+ CosmosVectorType? vectorType,
+ bool fromDataAnnotation = false)
=> (CosmosVectorType?)property.SetOrRemoveAnnotation(
CosmosAnnotationNames.VectorType,
vectorType,
diff --git a/src/EFCore.Cosmos/Extensions/CosmosQueryableExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosQueryableExtensions.cs
index 68417190ef9..70bc240dfb8 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosQueryableExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosQueryableExtensions.cs
@@ -18,14 +18,24 @@ namespace Microsoft.EntityFrameworkCore;
///
public static class CosmosQueryableExtensions
{
- internal static readonly MethodInfo WithPartitionKeyMethodInfo
+ internal static readonly MethodInfo WithPartitionKeyMethodInfo1
+ = typeof(CosmosQueryableExtensions).GetTypeInfo()
+ .GetDeclaredMethods(nameof(WithPartitionKey))
+ .Single(mi => mi.GetParameters().Length == 2);
+
+ internal static readonly MethodInfo WithPartitionKeyMethodInfo2
= typeof(CosmosQueryableExtensions).GetTypeInfo()
.GetDeclaredMethods(nameof(WithPartitionKey))
.Single(mi => mi.GetParameters().Length == 3);
+ internal static readonly MethodInfo WithPartitionKeyMethodInfo3
+ = typeof(CosmosQueryableExtensions).GetTypeInfo()
+ .GetDeclaredMethods(nameof(WithPartitionKey))
+ .Single(mi => mi.GetParameters().Length == 4);
+
///
- /// Specify the partition key value for partition used for the query. Required when using
- /// a resource token that provides permission based on a partition key for authentication.
+ /// Specify the partition key for partition used for the query.
+ /// Required when using a resource token that provides permission based on a partition key for authentication,
///
///
/// See Querying data with EF Core, and
@@ -33,15 +43,27 @@ internal static readonly MethodInfo WithPartitionKeyMethodInfo
///
/// The type of entity being queried.
/// The source query.
- /// The partition key value.
+ /// The partition key value.
/// A new query with the set partition key.
- public static IQueryable WithPartitionKey(this IQueryable source, string partitionKey)
+ public static IQueryable WithPartitionKey(this IQueryable source, object partitionKeyValue)
where TEntity : class
- => WithPartitionKey(source, partitionKey, []);
+ {
+ Check.NotNull(partitionKeyValue, nameof(partitionKeyValue));
+
+ return
+ source.Provider is EntityQueryProvider
+ ? source.Provider.CreateQuery(
+ Expression.Call(
+ instance: null,
+ method: WithPartitionKeyMethodInfo1.MakeGenericMethod(typeof(TEntity)),
+ source.Expression,
+ Expression.Constant(partitionKeyValue, typeof(object))))
+ : source;
+ }
///
- /// Specify the partition key for partition used for the query. Required when using
- /// a resource token that provides permission based on a partition key for authentication,
+ /// Specify the partition key for partition used for the query.
+ /// Required when using a resource token that provides permission based on a partition key for authentication,
///
///
/// See Querying data with EF Core, and
@@ -49,27 +71,65 @@ public static IQueryable WithPartitionKey(this IQueryable
/// The type of entity being queried.
/// The source query.
- /// The partition key value.
- /// Additional values for hierarchical partitions.
+ /// The first value in a hierarchical partition key.
+ /// The second value in a hierarchical partition key.
/// A new query with the set partition key.
public static IQueryable WithPartitionKey(
this IQueryable source,
- object partitionKeyValue,
- params object[] additionalPartitionKeyValues)
+ object partitionKeyValue1,
+ object partitionKeyValue2)
where TEntity : class
{
- Check.NotNull(partitionKeyValue, nameof(partitionKeyValue));
- Check.HasNoNulls(additionalPartitionKeyValues, nameof(additionalPartitionKeyValues));
+ Check.NotNull(partitionKeyValue1, nameof(partitionKeyValue1));
+ Check.NotNull(partitionKeyValue2, nameof(partitionKeyValue2));
+
+ return
+ source.Provider is EntityQueryProvider
+ ? source.Provider.CreateQuery(
+ Expression.Call(
+ instance: null,
+ method: WithPartitionKeyMethodInfo2.MakeGenericMethod(typeof(TEntity)),
+ source.Expression,
+ Expression.Constant(partitionKeyValue1, typeof(object)),
+ Expression.Constant(partitionKeyValue2, typeof(object))))
+ : source;
+ }
+
+ ///
+ /// Specify the partition key for partition used for the query.
+ /// Required when using a resource token that provides permission based on a partition key for authentication,
+ ///
+ ///
+ /// See Querying data with EF Core, and
+ /// Accessing Azure Cosmos DB with EF Core for more information and examples.
+ ///
+ /// The type of entity being queried.
+ /// The source query.
+ /// The first value in a hierarchical partition key.
+ /// The second value in a hierarchical partition key.
+ /// The third value in a hierarchical partition key.
+ /// A new query with the set partition key.
+ public static IQueryable WithPartitionKey(
+ this IQueryable source,
+ object partitionKeyValue1,
+ object partitionKeyValue2,
+ object partitionKeyValue3)
+ where TEntity : class
+ {
+ Check.NotNull(partitionKeyValue1, nameof(partitionKeyValue1));
+ Check.NotNull(partitionKeyValue2, nameof(partitionKeyValue2));
+ Check.NotNull(partitionKeyValue3, nameof(partitionKeyValue3));
return
source.Provider is EntityQueryProvider
? source.Provider.CreateQuery(
Expression.Call(
instance: null,
- method: WithPartitionKeyMethodInfo.MakeGenericMethod(typeof(TEntity)),
+ method: WithPartitionKeyMethodInfo3.MakeGenericMethod(typeof(TEntity)),
source.Expression,
- Expression.Constant(partitionKeyValue, typeof(object)),
- Expression.Constant(additionalPartitionKeyValues, typeof(object[]))))
+ Expression.Constant(partitionKeyValue1, typeof(object)),
+ Expression.Constant(partitionKeyValue2, typeof(object)),
+ Expression.Constant(partitionKeyValue3, typeof(object))))
: source;
}
@@ -180,7 +240,6 @@ private static FromSqlQueryRootExpression GenerateFromSqlQueryRoot(
internal static readonly MethodInfo ToPageAsyncMethodInfo
= typeof(CosmosQueryableExtensions).GetMethod(nameof(ToPageAsync))!;
-
///
/// Allows paginating through query results by repeatedly executing the same query, passing continuation tokens to retrieve
/// successive pages of the result set, and specifying the maximum number of results per page.
diff --git a/src/EFCore.Cosmos/Extensions/Embedding.cs b/src/EFCore.Cosmos/Extensions/Embedding.cs
index f700da1653c..e46c274d887 100644
--- a/src/EFCore.Cosmos/Extensions/Embedding.cs
+++ b/src/EFCore.Cosmos/Extensions/Embedding.cs
@@ -13,6 +13,7 @@ internal class Embedding : IEquatable
public VectorDataType DataType { get; set; }
public int Dimensions { get; set; }
public DistanceFunction DistanceFunction { get; set; }
+
public bool Equals(Embedding? that)
=> Equals(Path, that?.Path) && Equals(DataType, that?.DataType) && Equals(Dimensions, that.Dimensions);
}
diff --git a/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs b/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs
index 85a1cd37136..32305e525b4 100644
--- a/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs
+++ b/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs
@@ -34,9 +34,7 @@ public class CosmosDbContextOptionsBuilder : ICosmosDbContextOptionsBuilderInfra
///
/// The options builder.
public CosmosDbContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder)
- {
- _optionsBuilder = optionsBuilder;
- }
+ => _optionsBuilder = optionsBuilder;
///
DbContextOptionsBuilder ICosmosDbContextOptionsBuilderInfrastructure.OptionsBuilder
diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs
index a948636884c..f2545174ac3 100644
--- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs
+++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs
@@ -5,7 +5,6 @@
using System.Net;
using System.Text;
using Azure.Core;
-using Microsoft.EntityFrameworkCore.Cosmos.Internal;
namespace Microsoft.EntityFrameworkCore.Cosmos.Infrastructure.Internal;
diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelRuntimeInitializer.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelRuntimeInitializer.cs
index 40ebe958f43..3d368546f6d 100644
--- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelRuntimeInitializer.cs
+++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelRuntimeInitializer.cs
@@ -25,9 +25,7 @@ public CosmosModelRuntimeInitializer(
ModelRuntimeInitializerDependencies dependencies,
CosmosModelRuntimeInitializerDependencies cosmosDependencies)
: base(dependencies)
- {
- CosmosDependencies = cosmosDependencies;
- }
+ => CosmosDependencies = cosmosDependencies;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelRuntimeInitializerDependencies.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelRuntimeInitializerDependencies.cs
index c82e63a9056..298f96599c1 100644
--- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelRuntimeInitializerDependencies.cs
+++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosModelRuntimeInitializerDependencies.cs
@@ -29,9 +29,7 @@ public sealed record CosmosModelRuntimeInitializerDependencies
///
[EntityFrameworkInternal]
public CosmosModelRuntimeInitializerDependencies(IJsonIdDefinitionFactory jsonIdDefinitionFactory)
- {
- JsonIdDefinitionFactory = jsonIdDefinitionFactory;
- }
+ => JsonIdDefinitionFactory = jsonIdDefinitionFactory;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs
index dd911b7206c..af29229cfa5 100644
--- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs
+++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosSingletonOptions.cs
@@ -141,7 +141,7 @@ public class CosmosSingletonOptions : ICosmosSingletonOptions
/// 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? EnableContentResponseOnWrite { get; private set; }
+ public virtual bool? EnableContentResponseOnWrite { get; }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Cosmos/Metadata/Conventions/ContextContainerConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/ContextContainerConvention.cs
index a5654dec91b..e981912ae68 100644
--- a/src/EFCore.Cosmos/Metadata/Conventions/ContextContainerConvention.cs
+++ b/src/EFCore.Cosmos/Metadata/Conventions/ContextContainerConvention.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Metadata.Conventions;
///
@@ -18,9 +19,7 @@ public class ContextContainerConvention : IModelInitializedConvention
///
/// Parameter object containing dependencies for this convention.
public ContextContainerConvention(ProviderConventionSetBuilderDependencies dependencies)
- {
- Dependencies = dependencies;
- }
+ => Dependencies = dependencies;
///
/// Dependencies for this service.
diff --git a/src/EFCore.Cosmos/Metadata/Conventions/CosmosDiscriminatorConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/CosmosDiscriminatorConvention.cs
index ea91a2777fe..fd42271c82b 100644
--- a/src/EFCore.Cosmos/Metadata/Conventions/CosmosDiscriminatorConvention.cs
+++ b/src/EFCore.Cosmos/Metadata/Conventions/CosmosDiscriminatorConvention.cs
@@ -30,13 +30,13 @@ public CosmosDiscriminatorConvention(ProviderConventionSetBuilderDependencies de
{
}
- ///
+ ///
public virtual void ProcessEntityTypeAdded(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionContext context)
=> ProcessEntityType(entityTypeBuilder);
- ///
+ ///
public virtual void ProcessForeignKeyOwnershipChanged(
IConventionForeignKeyBuilder relationshipBuilder,
IConventionContext context)
@@ -46,7 +46,7 @@ public virtual void ProcessForeignKeyOwnershipChanged(
ProcessEntityType(entityType.Builder);
}
- ///
+ ///
public virtual void ProcessForeignKeyRemoved(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionForeignKey foreignKey,
@@ -59,7 +59,7 @@ public virtual void ProcessForeignKeyRemoved(
}
}
- ///
+ ///
public virtual void ProcessEntityTypeAnnotationChanged(
IConventionEntityTypeBuilder entityTypeBuilder,
string name,
@@ -95,7 +95,7 @@ private static void ProcessEntityType(IConventionEntityTypeBuilder entityTypeBui
}
}
- ///
+ ///
public override void ProcessDiscriminatorPropertySet(
IConventionEntityTypeBuilder entityTypeBuilder,
string? name,
@@ -108,7 +108,7 @@ public override void ProcessDiscriminatorPropertySet(
}
}
- ///
+ ///
public override void ProcessEntityTypeBaseTypeChanged(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionEntityType? newBaseType,
@@ -145,7 +145,7 @@ public override void ProcessEntityTypeBaseTypeChanged(
}
}
- ///
+ ///
protected override void SetDefaultDiscriminatorValues(
IEnumerable entityTypes,
IConventionDiscriminatorBuilder discriminatorBuilder)
@@ -156,7 +156,7 @@ protected override void SetDefaultDiscriminatorValues(
}
}
- ///
+ ///
public override void ProcessEntityTypeRemoved(
IConventionModelBuilder modelBuilder,
IConventionEntityType entityType,
@@ -164,7 +164,7 @@ public override void ProcessEntityTypeRemoved(
{
}
- ///
+ ///
public virtual void ProcessEmbeddedDiscriminatorName(
IConventionModelBuilder modelBuilder,
string? newName,
diff --git a/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs
index 0d19d7cb3ab..130041d556d 100644
--- a/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs
+++ b/src/EFCore.Cosmos/Metadata/Conventions/CosmosJsonIdConvention.cs
@@ -51,7 +51,7 @@ public class CosmosJsonIdConvention
/// Creates a new instance of .
///
/// Parameter object containing dependencies for this convention.
- /// The factory to create a for each entity type.
+ /// The factory to create a for each entity type.
public CosmosJsonIdConvention(
ProviderConventionSetBuilderDependencies dependencies,
IJsonIdDefinitionFactory definitionFactory)
@@ -66,7 +66,7 @@ public CosmosJsonIdConvention(
protected virtual ProviderConventionSetBuilderDependencies Dependencies { get; }
///
- /// The factory to create a for each entity type.
+ /// The factory to create a for each entity type.
///
protected virtual IJsonIdDefinitionFactory DefinitionFactory { get; }
@@ -78,22 +78,22 @@ private void ProcessEntityType(IConventionEntityType entityType, IConventionCont
var primaryKey = entityType.FindPrimaryKey();
if (entityType.BaseType != null // Requires: IEntityTypeBaseTypeChangedConvention
|| !entityType.IsDocumentRoot() // Requires: IEntityTypeAnnotationChangedConvention (ContainerName)
- || entityType.GetForeignKeys().Any(fk => fk.IsOwnership) // Requires: IForeignKeyOwnershipChangedConvention, IForeignKeyRemovedConvention
+ || entityType.GetForeignKeys()
+ .Any(fk => fk.IsOwnership) // Requires: IForeignKeyOwnershipChangedConvention, IForeignKeyRemovedConvention
|| primaryKey == null) // Requires: IKeyAddedConvention, IKeyRemovedConvention
{
// If the entity type is not a keyed, root document in the container, then it doesn't have an `id` mapping, so
// undo anything that was done by previous execution of this convention.
- if (jsonIdProperty != null)
+ if (jsonIdProperty is not null)
{
jsonIdProperty.Builder.ToJsonProperty(null);
- entityType.Builder.HasNoProperty(jsonIdProperty);
+ entityType.Builder.RemoveUnusedImplicitProperties([jsonIdProperty]);
}
- if (computedIdProperty != null
+ if (computedIdProperty is not null
&& computedIdProperty != jsonIdProperty)
{
- entityType.Builder.HasNoProperty(computedIdProperty);
- }
+ entityType.Builder.RemoveUnusedImplicitProperties([computedIdProperty]); }
return;
}
@@ -114,10 +114,17 @@ private void ProcessEntityType(IConventionEntityType entityType, IConventionCont
// - IDiscriminatorPropertySetConvention
// - IEntityTypeBaseTypeChangedConvention
var idDefinition = DefinitionFactory.Create((IEntityType)entityType)!;
- var keyProperty = (IConventionProperty?)idDefinition.Properties.FirstOrDefault();
if (idDefinition is { IncludesDiscriminator: false, Properties.Count: 1 })
{
- var clrType = keyProperty!.GetValueConverter()?.ProviderClrType ?? keyProperty.ClrType;
+ // If the property maps to a string in the JSON document, then we can use it directly, even if a value converter
+ // is applied. On the other hand, if it maps to a numeric or bool, then we need to duplicate this to preserve the
+ // non-string value for queries.
+ var keyProperty = (IConventionProperty)idDefinition.Properties.First();
+ var mapping = Dependencies.TypeMappingSource.FindMapping((IProperty)keyProperty);
+ var clrType = mapping?.Converter?.ProviderClrType
+ ?? mapping?.ClrType
+ ?? keyProperty!.ClrType;
+
if (clrType == typeof(string))
{
// We are at the point where we are going to map the `id` directly to the PK.
@@ -227,6 +234,7 @@ public virtual void ProcessEntityTypeAnnotationChanged(
{
ProcessEntityType(entityTypeBuilder.Metadata, context);
}
+
break;
case CosmosAnnotationNames.HasShadowId:
@@ -235,12 +243,15 @@ public virtual void ProcessEntityTypeAnnotationChanged(
{
ProcessEntityType(entityTypeBuilder.Metadata, context);
}
+
break;
}
}
///
- public virtual void ProcessForeignKeyOwnershipChanged(IConventionForeignKeyBuilder relationshipBuilder, IConventionContext context)
+ public virtual void ProcessForeignKeyOwnershipChanged(
+ IConventionForeignKeyBuilder relationshipBuilder,
+ IConventionContext context)
=> ProcessEntityType(relationshipBuilder.Metadata.DeclaringEntityType, context);
///
@@ -262,7 +273,9 @@ public virtual void ProcessKeyRemoved(
}
///
- public virtual void ProcessPropertyAdded(IConventionPropertyBuilder propertyBuilder, IConventionContext context)
+ public virtual void ProcessPropertyAdded(
+ IConventionPropertyBuilder propertyBuilder,
+ IConventionContext context)
=> ProcessEntityType(propertyBuilder.Metadata.DeclaringType.ContainingEntityType, context);
///
diff --git a/src/EFCore.Cosmos/Metadata/Conventions/CosmosManyToManyJoinEntityTypeConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/CosmosManyToManyJoinEntityTypeConvention.cs
index 187b574e268..b64b38b1341 100644
--- a/src/EFCore.Cosmos/Metadata/Conventions/CosmosManyToManyJoinEntityTypeConvention.cs
+++ b/src/EFCore.Cosmos/Metadata/Conventions/CosmosManyToManyJoinEntityTypeConvention.cs
@@ -188,5 +188,6 @@ private static bool ShouldSharePartitionKey(IConventionSkipNavigation skipNaviga
=> skipNavigation.DeclaringEntityType.GetContainer() == skipNavigation.TargetEntityType.GetContainer()
&& skipNavigation.DeclaringEntityType.GetPartitionKeyPropertyNames().Any()
&& (skipNavigation.Inverse?.DeclaringEntityType.GetPartitionKeyPropertyNames()
- .SequenceEqual(skipNavigation.DeclaringEntityType.GetPartitionKeyPropertyNames(), StringComparer.Ordinal) == true);
+ .SequenceEqual(skipNavigation.DeclaringEntityType.GetPartitionKeyPropertyNames(), StringComparer.Ordinal)
+ == true);
}
diff --git a/src/EFCore.Cosmos/Metadata/Conventions/CosmosPartitionKeyInPrimaryKeyConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/CosmosPartitionKeyInPrimaryKeyConvention.cs
index 5b2f2ad33cd..1b328cc683f 100644
--- a/src/EFCore.Cosmos/Metadata/Conventions/CosmosPartitionKeyInPrimaryKeyConvention.cs
+++ b/src/EFCore.Cosmos/Metadata/Conventions/CosmosPartitionKeyInPrimaryKeyConvention.cs
@@ -40,9 +40,7 @@ public class CosmosPartitionKeyInPrimaryKeyConvention :
///
/// Parameter object containing dependencies for this convention.
public CosmosPartitionKeyInPrimaryKeyConvention(ProviderConventionSetBuilderDependencies dependencies)
- {
- Dependencies = dependencies;
- }
+ => Dependencies = dependencies;
///
/// Dependencies for this service.
@@ -73,7 +71,6 @@ private static void ProcessIdProperty(IConventionEntityTypeBuilder entityTypeBui
primaryKeyProperties.Add(partitionKeyProperty!);
keyContainsPartitionProperties = true;
}
-
}
if (keyContainsPartitionProperties)
@@ -171,10 +168,12 @@ public virtual void ProcessEntityTypePrimaryKeyChanged(
IConventionKey? previousPrimaryKey,
IConventionContext context)
{
- if ((newPrimaryKey != null && newPrimaryKey.Properties
- .Any(p => p.GetJsonPropertyName() == CosmosJsonIdConvention.IdPropertyJsonName))
- || (previousPrimaryKey != null && previousPrimaryKey.Properties
- .Any(p => p.GetJsonPropertyName() == CosmosJsonIdConvention.IdPropertyJsonName)))
+ if ((newPrimaryKey != null
+ && newPrimaryKey.Properties
+ .Any(p => p.GetJsonPropertyName() == CosmosJsonIdConvention.IdPropertyJsonName))
+ || (previousPrimaryKey != null
+ && previousPrimaryKey.Properties
+ .Any(p => p.GetJsonPropertyName() == CosmosJsonIdConvention.IdPropertyJsonName)))
{
ProcessIdProperty(entityTypeBuilder);
}
diff --git a/src/EFCore.Cosmos/Metadata/Conventions/CosmosRelationshipDiscoveryConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/CosmosRelationshipDiscoveryConvention.cs
index 2063d27add5..1674300f1e6 100644
--- a/src/EFCore.Cosmos/Metadata/Conventions/CosmosRelationshipDiscoveryConvention.cs
+++ b/src/EFCore.Cosmos/Metadata/Conventions/CosmosRelationshipDiscoveryConvention.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Metadata.Conventions;
///
diff --git a/src/EFCore.Cosmos/Metadata/Conventions/ETagPropertyConvention.cs b/src/EFCore.Cosmos/Metadata/Conventions/ETagPropertyConvention.cs
index bb8b9a22f99..5169424e588 100644
--- a/src/EFCore.Cosmos/Metadata/Conventions/ETagPropertyConvention.cs
+++ b/src/EFCore.Cosmos/Metadata/Conventions/ETagPropertyConvention.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Metadata.Conventions;
///
diff --git a/src/EFCore.Cosmos/Metadata/Conventions/Internal/CosmosConventionSetBuilder.cs b/src/EFCore.Cosmos/Metadata/Conventions/Internal/CosmosConventionSetBuilder.cs
index bf1566146db..6756b34abf2 100644
--- a/src/EFCore.Cosmos/Metadata/Conventions/Internal/CosmosConventionSetBuilder.cs
+++ b/src/EFCore.Cosmos/Metadata/Conventions/Internal/CosmosConventionSetBuilder.cs
@@ -23,19 +23,17 @@ public CosmosConventionSetBuilder(
ProviderConventionSetBuilderDependencies dependencies,
IJsonIdDefinitionFactory definitionFactory)
: base(dependencies)
- {
- DefinitionFactory = definitionFactory;
- }
+ => DefinitionFactory = definitionFactory;
///
- /// The factory to create a for each entity type.
+ /// The factory to create a for each entity type.
///
///
/// 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 IJsonIdDefinitionFactory DefinitionFactory { get; }
///
diff --git a/src/EFCore.Cosmos/Metadata/IdDiscriminatorMode.cs b/src/EFCore.Cosmos/Metadata/IdDiscriminatorMode.cs
index fee33def274..ad4682614ac 100644
--- a/src/EFCore.Cosmos/Metadata/IdDiscriminatorMode.cs
+++ b/src/EFCore.Cosmos/Metadata/IdDiscriminatorMode.cs
@@ -2,10 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Metadata;
///
-/// Defines the behavior for including discriminator values in the JSON "id" value.
+/// Defines the behavior for including discriminator values in the JSON "id" value.
///
public enum IdDiscriminatorMode
{
diff --git a/src/EFCore.Cosmos/Metadata/Internal/CosmosEntityTypeExtensions.cs b/src/EFCore.Cosmos/Metadata/Internal/CosmosEntityTypeExtensions.cs
index 99fd4be5bf2..1ac3eb0bc0d 100644
--- a/src/EFCore.Cosmos/Metadata/Internal/CosmosEntityTypeExtensions.cs
+++ b/src/EFCore.Cosmos/Metadata/Internal/CosmosEntityTypeExtensions.cs
@@ -25,12 +25,14 @@ public static bool IsDocumentRoot(this IReadOnlyEntityType entityType)
|| entityType[CosmosAnnotationNames.ContainerName] != null);
///
- /// Returns the JSON `id` definition, or if there is none.
+ /// Returns the JSON `id` definition, or if there is none.
///
/// The entity type.
public static IJsonIdDefinition? GetJsonIdDefinition(this IEntityType entityType)
- => entityType.GetOrAddRuntimeAnnotationValue(CosmosAnnotationNames.JsonIdDefinition,
+ => entityType.GetOrAddRuntimeAnnotationValue(
+ CosmosAnnotationNames.JsonIdDefinition,
static e =>
((CosmosModelRuntimeInitializerDependencies)e!.Model.FindRuntimeAnnotationValue(
CosmosAnnotationNames.ModelDependencies)!).JsonIdDefinitionFactory.Create(e),
- entityType);}
+ entityType);
+}
diff --git a/src/EFCore.Cosmos/Metadata/Internal/IJsonIdDefinition.cs b/src/EFCore.Cosmos/Metadata/Internal/IJsonIdDefinition.cs
index b17b17ca649..cb1e79088fb 100644
--- a/src/EFCore.Cosmos/Metadata/Internal/IJsonIdDefinition.cs
+++ b/src/EFCore.Cosmos/Metadata/Internal/IJsonIdDefinition.cs
@@ -20,7 +20,7 @@ public interface IJsonIdDefinition
IReadOnlyList Properties { get; }
///
- /// if the discriminator is included in the key.
+ /// if the discriminator is included in the key.
///
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -31,7 +31,7 @@ public interface IJsonIdDefinition
bool IncludesDiscriminator { get; }
///
- /// if the discriminator is for the root entity type.
+ /// if the discriminator is for the root entity type.
///
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Cosmos/Metadata/Internal/JsonIdDefinition.cs b/src/EFCore.Cosmos/Metadata/Internal/JsonIdDefinition.cs
index 98165a02cee..92e2709cadb 100644
--- a/src/EFCore.Cosmos/Metadata/Internal/JsonIdDefinition.cs
+++ b/src/EFCore.Cosmos/Metadata/Internal/JsonIdDefinition.cs
@@ -24,9 +24,7 @@ public class JsonIdDefinition : IJsonIdDefinition
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public JsonIdDefinition(IReadOnlyList properties)
- {
- Properties = properties;
- }
+ => Properties = properties;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -52,6 +50,7 @@ public JsonIdDefinition(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual IReadOnlyList Properties { get; }
+
///
/// 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.Cosmos/Properties/CosmosStrings.Designer.cs b/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs
index fcde6e3de80..d9571fc2d05 100644
--- a/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs
+++ b/src/EFCore.Cosmos/Properties/CosmosStrings.Designer.cs
@@ -163,14 +163,6 @@ public static string IdNonStringStoreType(object? idProperty, object? entityType
GetString("IdNonStringStoreType", nameof(idProperty), nameof(entityType), nameof(propertyType)),
idProperty, entityType, propertyType);
- ///
- /// {actual} partition key values were provided, but the entity type '{entityType}' has {expected} partition key values defined.
- ///
- public static string IncorrectPartitionKeyNumber(object? entityType, object? actual, object? expected)
- => string.Format(
- GetString("IncorrectPartitionKeyNumber", nameof(entityType), nameof(actual), nameof(expected)),
- entityType, actual, expected);
-
///
/// The entity type '{entityType}' has an index defined over properties '{properties}'. The Azure Cosmos DB provider for EF Core currently does not support index definitions.
///
diff --git a/src/EFCore.Cosmos/Properties/CosmosStrings.resx b/src/EFCore.Cosmos/Properties/CosmosStrings.resx
index 59db5844f85..171d85f852c 100644
--- a/src/EFCore.Cosmos/Properties/CosmosStrings.resx
+++ b/src/EFCore.Cosmos/Properties/CosmosStrings.resx
@@ -174,9 +174,6 @@
The type of the '{idProperty}' property on '{entityType}' is '{propertyType}'. All 'id' properties must be strings or have a string value converter.
-
- {actual} partition key values were provided, but the entity type '{entityType}' has {expected} partition key values defined.
-
The entity type '{entityType}' has an index defined over properties '{properties}'. The Azure Cosmos DB provider for EF Core currently does not support index definitions.
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosAliasManager.cs b/src/EFCore.Cosmos/Query/Internal/CosmosAliasManager.cs
index 3e686495b86..bb43035c4f0 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosAliasManager.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosAliasManager.cs
@@ -48,18 +48,19 @@ public class CosmosAliasManager
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual string GenerateSourceAlias(Expression expression, string? fallback = null)
- => GenerateSourceAlias(expression switch
- {
- IAccessExpression { PropertyName: string propertyName } => propertyName,
- FromSqlExpression => "sql",
- SqlFunctionExpression { Name: "ARRAY_SLICE", Arguments: [var array, ..] } => GenerateSourceAlias(array),
- ObjectFunctionExpression { Name: "ARRAY_SLICE", Arguments: [var array, ..] } => GenerateSourceAlias(array),
- SqlFunctionExpression { Name: var name } => name,
- ObjectFunctionExpression { Name: var name } => name,
- ArrayConstantExpression => "array",
+ => GenerateSourceAlias(
+ expression switch
+ {
+ IAccessExpression { PropertyName: string propertyName } => propertyName,
+ FromSqlExpression => "sql",
+ SqlFunctionExpression { Name: "ARRAY_SLICE", Arguments: [var array, ..] } => GenerateSourceAlias(array),
+ ObjectFunctionExpression { Name: "ARRAY_SLICE", Arguments: [var array, ..] } => GenerateSourceAlias(array),
+ SqlFunctionExpression { Name: var name } => name,
+ ObjectFunctionExpression { Name: var name } => name,
+ ArrayConstantExpression => "array",
- _ => fallback ?? "value"
- });
+ _ => fallback ?? "value"
+ });
///
/// Generates an alias based on the given .
@@ -145,7 +146,7 @@ public virtual Expression PostprocessAliases(Expression expression)
}
else
{
- bitmap = aliasBitmaps[aliasBase] = new(aliasNum + 1);
+ bitmap = aliasBitmaps[aliasBase] = new BitArray(aliasNum + 1);
}
bitmap[aliasNum] = true;
@@ -173,7 +174,7 @@ public virtual Expression PostprocessAliases(Expression expression)
var j = i - numHoles;
var newAlias = aliasBase + (j == 0 ? "" : (j - 1).ToString());
- aliasRewritingMap ??= new();
+ aliasRewritingMap ??= new Dictionary();
aliasRewritingMap[oldAlias] = newAlias;
}
}
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryCompilationContextFactory.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryCompilationContextFactory.cs
index c1c41af7c4c..dc5b69cb0f4 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryCompilationContextFactory.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryCompilationContextFactory.cs
@@ -18,9 +18,7 @@ public class CosmosQueryCompilationContextFactory : IQueryCompilationContextFact
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public CosmosQueryCompilationContextFactory(QueryCompilationContextDependencies dependencies)
- {
- Dependencies = dependencies;
- }
+ => Dependencies = dependencies;
///
/// Dependencies for this service.
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryRootProcessor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryRootProcessor.cs
index f7d4f62bd99..c1e56ee9847 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryRootProcessor.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryRootProcessor.cs
@@ -21,9 +21,7 @@ public class CosmosQueryRootProcessor : QueryRootProcessor
///
public CosmosQueryRootProcessor(QueryTranslationPreprocessorDependencies dependencies, QueryCompilationContext queryCompilationContext)
: base(dependencies, queryCompilationContext)
- {
- _model = queryCompilationContext.Model;
- }
+ => _model = queryCompilationContext.Model;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQuerySqlGenerator.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQuerySqlGenerator.cs
index 6dd2963c6e1..67b1437208a 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosQuerySqlGenerator.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosQuerySqlGenerator.cs
@@ -518,7 +518,7 @@ protected override Expression VisitSqlBinary(SqlBinaryExpression sqlBinaryExpres
op = " || ";
}
else if (sqlBinaryExpression.OperatorType == ExpressionType.ExclusiveOr
- && sqlBinaryExpression.Type == typeof(bool))
+ && sqlBinaryExpression.Type == typeof(bool))
{
op = " != ";
}
@@ -743,7 +743,6 @@ protected sealed override Expression VisitSource(SourceExpression sourceExpressi
.Append(sourceExpression.Alias)
.Append(" IN ");
-
VisitContainerExpression(sourceExpression.Expression);
}
else
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessor.cs
index bc82944976e..cf11f40219e 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessor.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessor.cs
@@ -10,9 +10,9 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public class CosmosQueryTranslationPostprocessor(
- QueryTranslationPostprocessorDependencies dependencies,
- ISqlExpressionFactory sqlExpressionFactory,
- CosmosQueryCompilationContext queryCompilationContext)
+ QueryTranslationPostprocessorDependencies dependencies,
+ ISqlExpressionFactory sqlExpressionFactory,
+ CosmosQueryCompilationContext queryCompilationContext)
: QueryTranslationPostprocessor(dependencies, queryCompilationContext)
{
///
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessorFactory.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessorFactory.cs
index 313976e3e92..ee03bfcfd04 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessorFactory.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessorFactory.cs
@@ -10,8 +10,8 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public class CosmosQueryTranslationPostprocessorFactory(
- QueryTranslationPostprocessorDependencies dependencies,
- ISqlExpressionFactory sqlExpressionFactory)
+ QueryTranslationPostprocessorDependencies dependencies,
+ ISqlExpressionFactory sqlExpressionFactory)
: IQueryTranslationPostprocessorFactory
{
///
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPreprocessor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPreprocessor.cs
index cc8cadf8b0d..f96c98fdb10 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPreprocessor.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPreprocessor.cs
@@ -10,8 +10,8 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public class CosmosQueryTranslationPreprocessor(
- QueryTranslationPreprocessorDependencies dependencies,
- CosmosQueryCompilationContext cosmosQueryCompilationContext)
+ QueryTranslationPreprocessorDependencies dependencies,
+ CosmosQueryCompilationContext cosmosQueryCompilationContext)
: QueryTranslationPreprocessor(dependencies, cosmosQueryCompilationContext)
{
///
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs
index 18d2d9bc2de..9b446a78152 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs
@@ -1,9 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Diagnostics.CodeAnalysis;
using Microsoft.EntityFrameworkCore.Cosmos.Internal;
-using Microsoft.EntityFrameworkCore.Cosmos.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Query.Internal.Expressions;
using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
using Microsoft.EntityFrameworkCore.Internal;
@@ -141,12 +139,13 @@ public override Expression Translate(Expression expression)
// Wrap the shaper for the entire query in a PagingExpression which also contains the paging arguments, and update
// the final cardinality to Single (since we'll be returning a single Page).
return shapedQuery
- .UpdateShaperExpression(new PagingExpression(
- shapedQuery.ShaperExpression,
- translatedMaxItemCount,
- translatedContinuationToken,
- translatedResponseContinuationTokenLimitInKb,
- typeof(CosmosPage<>).MakeGenericType(shapedQuery.ShaperExpression.Type)))
+ .UpdateShaperExpression(
+ new PagingExpression(
+ shapedQuery.ShaperExpression,
+ translatedMaxItemCount,
+ translatedContinuationToken,
+ translatedResponseContinuationTokenLimitInKb,
+ typeof(CosmosPage<>).MakeGenericType(shapedQuery.ShaperExpression.Type)))
.UpdateResultCardinality(ResultCardinality.Single);
}
@@ -173,23 +172,15 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
var innerQueryable = Visit(methodCallExpression.Arguments[0]);
- var firstValue = _sqlTranslator.Translate(methodCallExpression.Arguments[1], applyDefaultTypeMapping: false);
- if (firstValue is not SqlConstantExpression and not SqlParameterExpression)
+ for (var i = 1; i < methodCallExpression.Arguments.Count; i++)
{
- throw new InvalidOperationException(CosmosStrings.WithPartitionKeyNotConstantOrParameter);
- }
-
- _queryCompilationContext.PartitionKeyPropertyValues.Add(firstValue);
-
- if (methodCallExpression.Arguments.Count == 3)
- {
- var remainingValuesArray = _sqlTranslator.Translate(methodCallExpression.Arguments[2], applyDefaultTypeMapping: false);
- if (remainingValuesArray is not SqlParameterExpression)
+ var value = _sqlTranslator.Translate(methodCallExpression.Arguments[i], applyDefaultTypeMapping: false);
+ if (value is not SqlConstantExpression and not SqlParameterExpression)
{
throw new InvalidOperationException(CosmosStrings.WithPartitionKeyNotConstantOrParameter);
}
- _queryCompilationContext.PartitionKeyPropertyValues.Add(remainingValuesArray);
+ _queryCompilationContext.PartitionKeyPropertyValues.Add(value);
}
return innerQueryable;
@@ -223,7 +214,7 @@ when methodCallExpression.Arguments[0] is MethodCallExpression
{
#pragma warning disable EF1001 // Internal EF Core API usage.
var translation = _sqlTranslator.Translate(
- Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions.CreateEFPropertyExpression(
+ EntityFrameworkCore.Infrastructure.ExpressionExtensions.CreateEFPropertyExpression(
elementAtTranslation.ShaperExpression,
elementAtTranslation.ShaperExpression.Type,
boundMember.Type,
@@ -340,8 +331,9 @@ protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVis
selectExpression,
_sqlExpressionFactory.In(
(SqlExpression)discriminatorColumn,
- concreteEntityTypes.Select(et => _sqlExpressionFactory.Constant(et.GetDiscriminatorValue(), discriminatorColumn.Type))
- .ToArray()));
+ concreteEntityTypes.Select(
+ et => _sqlExpressionFactory.Constant(et.GetDiscriminatorValue(), discriminatorColumn.Type))
+ .ToArray()));
Check.DebugAssert(success, "Couldn't apply predicate when creating a new ShapedQueryExpression");
}
}
@@ -422,7 +414,7 @@ private ShapedQueryExpression CreateShapedQueryExpression(SelectExpression selec
var simplifiedTranslation = _sqlExpressionFactory.GreaterThan(
_sqlExpressionFactory.Function(
"ARRAY_LENGTH", new[] { array }, typeof(int), _typeMappingSource.FindMapping(typeof(int))),
- _sqlExpressionFactory.Constant(0));
+ _sqlExpressionFactory.Constant(0));
var select = new SelectExpression(simplifiedTranslation);
return source.Update(select, new ProjectionBindingExpression(select, new ProjectionMember(), typeof(int)));
@@ -519,7 +511,7 @@ protected override ShapedQueryExpression TranslateCast(ShapedQueryExpression sou
// Translate to EXISTS
var anyLambdaParameter = Expression.Parameter(item.Type, "p");
var anyLambda = Expression.Lambda(
- Microsoft.EntityFrameworkCore.Infrastructure.ExpressionExtensions.CreateEqualsExpression(anyLambdaParameter, item),
+ EntityFrameworkCore.Infrastructure.ExpressionExtensions.CreateEqualsExpression(anyLambdaParameter, item),
anyLambdaParameter);
return TranslateAny(source, anyLambda);
@@ -598,7 +590,7 @@ protected override ShapedQueryExpression TranslateCast(ShapedQueryExpression sou
// ElementAtOrDefault over an array of scalars
case SqlExpression scalarArray when projection is SqlExpression element:
{
- SqlExpression translation = _sqlExpressionFactory.ArrayIndex(
+ var translation = _sqlExpressionFactory.ArrayIndex(
scalarArray, translatedIndex, element.Type, element.TypeMapping);
// ElementAt may access indexes beyond the end of the array; Cosmos returns undefined for those cases.
@@ -633,7 +625,8 @@ protected override ShapedQueryExpression TranslateCast(ShapedQueryExpression sou
}
var translatedSelect =
- new SelectExpression(new EntityProjectionExpression(translation, (IEntityType)projectedStructuralTypeShaper.StructuralType));
+ new SelectExpression(
+ new EntityProjectionExpression(translation, (IEntityType)projectedStructuralTypeShaper.StructuralType));
return source.Update(
translatedSelect,
new StructuralTypeShaperExpression(
@@ -1283,7 +1276,10 @@ protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression s
case SqlExpression scalarArray when projection is SqlExpression element:
{
// Take() is composed over Skip(), combine the two together to a single ARRAY_SLICE()
- var slice = array is SqlFunctionExpression { Name: "ARRAY_SLICE", Arguments: [var nestedArray, var skipCount] } previousSlice
+ var slice = array is SqlFunctionExpression
+ {
+ Name: "ARRAY_SLICE", Arguments: [var nestedArray, var skipCount]
+ } previousSlice
? previousSlice.Update([nestedArray, skipCount, translatedCount])
: _sqlExpressionFactory.Function(
"ARRAY_SLICE", [scalarArray, TranslateExpression(Expression.Constant(0))!, translatedCount], scalarArray.Type,
@@ -1301,10 +1297,14 @@ protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression s
case not null when projectedStructuralTypeShaper is not null:
{
// Take() is composed over Skip(), combine the two together to a single ARRAY_SLICE()
- var slice = array is ObjectFunctionExpression { Name: "ARRAY_SLICE", Arguments: [var nestedArray, var skipCount] } previousSlice
+ var slice = array is ObjectFunctionExpression
+ {
+ Name: "ARRAY_SLICE", Arguments: [var nestedArray, var skipCount]
+ } previousSlice
? previousSlice.Update([nestedArray, skipCount, translatedCount])
: new ObjectFunctionExpression(
- "ARRAY_SLICE", [array, TranslateExpression(Expression.Constant(0))!, translatedCount], projectedStructuralTypeShaper.Type);
+ "ARRAY_SLICE", [array, TranslateExpression(Expression.Constant(0))!, translatedCount],
+ projectedStructuralTypeShaper.Type);
var alias = _aliasManager.GenerateSourceAlias(slice);
var translatedSelect = SelectExpression.CreateForCollection(
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosReadItemAndPartitionKeysExtractor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosReadItemAndPartitionKeysExtractor.cs
index 03d412a8b04..a0311a5c457 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosReadItemAndPartitionKeysExtractor.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosReadItemAndPartitionKeysExtractor.cs
@@ -6,7 +6,8 @@
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
-/// Identifies Cosmos queries that can be transformed to optimized ReadItem form and performs the transformation.
+/// Identifies Cosmos queries that can be transformed to optimized ReadItem form and performs the transformation; also extracts out
+/// partition key comparisons from the predicate.
///
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -23,7 +24,7 @@ public class CosmosReadItemAndPartitionKeysExtractor : ExpressionVisitor
private bool _discriminatorHandled;
private string? _discriminatorJsonPropertyName;
private Dictionary _jsonIdPropertyValues = null!;
- private Dictionary _partitionKeyPropertyValues = null!;
+ private Dictionary _partitionKeyPropertyValues = null!;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -74,51 +75,48 @@ public virtual Expression ExtractPartitionKeysAndId(
_jsonIdPropertyValues = jsonIdProperties.ToDictionary(p => p, _ => (Expression?)null);
var partitionKeyProperties = _entityType.GetPartitionKeyProperties();
- _partitionKeyPropertyValues = partitionKeyProperties.ToDictionary(p => p, _ => (Expression?)null);
+ _partitionKeyPropertyValues = partitionKeyProperties.ToDictionary(
+ p => p, _ => (ValueExpression: (Expression?)null, (Expression?)null));
+
_discriminatorJsonPropertyName = _entityType.FindDiscriminatorProperty()?.GetJsonPropertyName();
// Visit the predicate.
- // This will populate _jsonIdPropertyValues and _partitionKeyPropertyValues with comparisons found in the predicate, and return
- // a rewritten predicate where the partition key comparisons have been removed.
- var predicateWithoutPartitionKeyComparisons = (SqlExpression)Visit(predicate);
+ // This will populate _jsonIdPropertyValues and _partitionKeyPropertyValues with comparisons found in the predicate.
+ // It does not modify the predicate (this may happen below if we lift our partition key comparisons).
+ var samePredicate = (SqlExpression)Visit(predicate);
+ Check.DebugAssert(ReferenceEquals(samePredicate, predicate), "Visitation shouldn't have changed the predicate.");
var allIdPropertiesSpecified =
_jsonIdPropertyValues.Values.All(p => p is not null) && _jsonIdPropertyValues.Count > 0;
- var allPartitionKeyPropertiesSpecified = _partitionKeyPropertyValues.Values.All(p => p is not null);
-
- // First, take care of the partition key properties; if the visitation above returned a different predicate, that means that some
- // partition key comparisons were extracted (and therefore found). Lift these up to the query compilation context and rewrite
- // the SelectExpression with the new, reduced predicate.
- // Note that if the user called WithPartitionKey(), we'll have already populated the partition key property values from there, and
- // we skip lifting the predicate comparisons.
- if (allPartitionKeyPropertiesSpecified
- && queryCompilationContext.PartitionKeyPropertyValues.Count == 0)
+
+ // First, go over the partition key properties and lift them from the predicate to the query compilation context, as possible.
+ // We do this only as long as all partition key values are provided; the moment there's a gap we stop (so if PK1 and PK3 are
+ // provided but not PK2, only PK1 will be lifted out).
+ // Note that if the user called WithPartitionKey(), we'll have already populated the partition key property values from there; for
+ // this case, we skip lifting the predicate comparisons and leave the predicate exactly as it is (it may conflict with the values
+ // given in WithPartitionKey and return zero results - that's the expected behavior).
+ var liftPartitionKeys = queryCompilationContext.PartitionKeyPropertyValues.Count == 0;
+ foreach (var property in partitionKeyProperties)
{
- foreach (var partitionKeyProperty in partitionKeyProperties)
+ if (liftPartitionKeys && _partitionKeyPropertyValues[property].ValueExpression is Expression valueExpression)
{
- queryCompilationContext.PartitionKeyPropertyValues.Add(_partitionKeyPropertyValues[partitionKeyProperty]!);
+ queryCompilationContext.PartitionKeyPropertyValues.Add(valueExpression);
+ }
+ else
+ {
+ // We either have a gap in the partition key comparisons in the predicate (so we can't lift later ones), or the user
+ // specified a partition key value via WithPartitionKey. In either case, we need to not lift out comparisons and null out
+ // _partitionKeyPropertyValues, to prevent us removing the comparisons from the predicate below.
+ liftPartitionKeys = false;
+ _partitionKeyPropertyValues[property] = (null, null);
}
-
- select = select.Update(
- select.Sources.ToList(),
- predicateWithoutPartitionKeyComparisons is SqlConstantExpression { Value: true }
- ? null
- : predicateWithoutPartitionKeyComparisons,
- select.Projection.ToList(),
- select.Orderings.ToList(),
- select.Offset,
- select.Limit);
-
- shapedQuery = shapedQuery.UpdateQueryExpression(select);
}
- // Now, attempt to also transform the query to ReadItem form if possible.
+ // Now, attempt to also transform the query to ReadItem form; this is only possible if all JSON ID properties were compared in the
+ // predicate, and *all* partition key values are specified(in the predicate or via WithPartitionKey)
if (_isPredicateCompatibleWithReadItem
&& allIdPropertiesSpecified
- // Note that queryCompilationContext.PartitionKeyPropertyValues may have been populated with WithPartitionKey(), which has
- // a params object[] argument that gets parameterized as a single array. So the number of property values may not match the
- // number of partition key properties.
- && (partitionKeyProperties.Count == 0 || queryCompilationContext.PartitionKeyPropertyValues.Count > 0)
+ && queryCompilationContext.PartitionKeyPropertyValues.Count == partitionKeyProperties.Count
&& select is
{
Offset: null or SqlConstantExpression { Value: 0 },
@@ -132,6 +130,28 @@ public virtual Expression ExtractPartitionKeysAndId(
return shapedQuery.UpdateQueryExpression(select.WithReadItemInfo(new ReadItemInfo(_jsonIdPropertyValues!)));
}
+ // We couldn't transform to ReadItem - some JSON ID or partition key property comparison was missing in the predicate.
+ // However, comparisons might still be there for some (or all) of the partition key properties. These have already been lifted
+ // up to the query compilation context (above), but we still need to remove them from the predicate.
+ if (partitionKeyProperties.Count > 0 && _partitionKeyPropertyValues[partitionKeyProperties[0]].ValueExpression is not null)
+ {
+ var predicateWithoutPartitionKeyComparisons = (SqlExpression)new PredicateComparisonRemover(
+ _sqlExpressionFactory,
+ _partitionKeyPropertyValues.Values.Select(p => p.OriginalExpression).OfType().ToList())
+ .Visit(predicate);
+ Check.DebugAssert(!ReferenceEquals(predicateWithoutPartitionKeyComparisons, predicate), "Predicate should have changed.");
+
+ select = select.Update(
+ select.Sources.ToList(),
+ predicateWithoutPartitionKeyComparisons,
+ select.Projection.ToList(),
+ select.Orderings.ToList(),
+ select.Offset,
+ select.Limit);
+
+ shapedQuery = shapedQuery.UpdateQueryExpression(select);
+ }
+
return shapedQuery;
Expression Unwrap(Expression shaper)
@@ -194,6 +214,7 @@ protected override Expression VisitExtension(Expression node)
_isPredicateCompatibleWithReadItem = false;
return node;
}
+
case SqlBinaryExpression { OperatorType: ExpressionType.Equal, Left: var left, Right: var right } binary:
{
// TODO: Handle property accesses into complex types/owned entity types, #25548
@@ -209,7 +230,8 @@ left is ScalarAccessExpression leftScalarAccess
if (scalarAccess?.Object is ObjectReferenceExpression { Name: var referencedSourceAlias }
&& referencedSourceAlias == _rootAlias)
{
- return ProcessPropertyComparison(scalarAccess.PropertyName, propertyValue!, binary);
+ ProcessPropertyComparison(scalarAccess.PropertyName, propertyValue!, binary);
+ return node;
}
_isPredicateCompatibleWithReadItem = false;
@@ -218,7 +240,8 @@ left is ScalarAccessExpression leftScalarAccess
// Bool property access (e.g. Where(b => b.BoolPartitionKey))
case ScalarAccessExpression { PropertyName: var propertyName } scalarAccess:
- return ProcessPropertyComparison(propertyName, _sqlExpressionFactory.Constant(true), scalarAccess);
+ ProcessPropertyComparison(propertyName, _sqlExpressionFactory.Constant(true), scalarAccess);
+ return node;
// Negated bool property access (e.g. Where(b => !b.BoolPartitionKey))
case SqlUnaryExpression
@@ -226,15 +249,11 @@ left is ScalarAccessExpression leftScalarAccess
OperatorType: ExpressionType.Not,
Operand: ScalarAccessExpression { PropertyName: var propertyName }
} unary:
- return ProcessPropertyComparison(propertyName, _sqlExpressionFactory.Constant(false), unary);
+ ProcessPropertyComparison(propertyName, _sqlExpressionFactory.Constant(false), unary);
+ return node;
case SqlBinaryExpression { OperatorType: ExpressionType.AndAlso } binary:
- return _sqlExpressionFactory.MakeBinary(
- ExpressionType.AndAlso,
- (SqlExpression)Visit(binary.Left),
- (SqlExpression)Visit(binary.Right),
- binary.TypeMapping,
- binary)!;
+ return binary.Update((SqlExpression)Visit(binary.Left), (SqlExpression)Visit(binary.Right));
default:
// Anything else in the predicate, e.g. an OR, immediately disqualifies it from being a ReadItem query, and means we
@@ -243,7 +262,7 @@ left is ScalarAccessExpression leftScalarAccess
return node;
}
- SqlExpression ProcessPropertyComparison(string propertyName, SqlExpression propertyValue, SqlExpression originalExpression)
+ void ProcessPropertyComparison(string propertyName, SqlExpression propertyValue, SqlExpression originalExpression)
{
// We assume that the comparison is incompatible with ReadItem until proven otherwise, i.e. the comparison is for a JSON ID
// property, a partition key property, or certain cases involving the discriminator property.
@@ -271,6 +290,7 @@ SqlExpression ProcessPropertyComparison(string propertyName, SqlExpression prope
_jsonIdPropertyValues[property] = propertyValue;
isCompatibleComparisonForReadItem = true;
}
+
break;
}
}
@@ -282,11 +302,11 @@ SqlExpression ProcessPropertyComparison(string propertyName, SqlExpression prope
// Extract its value expression and elide the comparison from the predicate - it'll be lifted out to the Cosmos SDK
// call. Note that this is always considered a compatible comparison for ReadItem.
if (propertyName == property.GetJsonPropertyName()
- && _partitionKeyPropertyValues.TryGetValue(property, out var previousValue)
- && (previousValue is null || previousValue.Equals(propertyValue)))
+ && _partitionKeyPropertyValues.TryGetValue(property, out var previousValues)
+ && (previousValues.ValueExpression is null || previousValues.Equals(propertyValue)))
{
- _partitionKeyPropertyValues[property] = propertyValue;
- return _sqlExpressionFactory.Constant(true);
+ _partitionKeyPropertyValues[property] = (ValueExpression: propertyValue, OriginalExpression: originalExpression);
+ return;
}
}
@@ -294,8 +314,29 @@ SqlExpression ProcessPropertyComparison(string propertyName, SqlExpression prope
{
_isPredicateCompatibleWithReadItem = false;
}
-
- return originalExpression;
}
}
+
+ private sealed class PredicateComparisonRemover(ISqlExpressionFactory sqlExpressionFactory, List comparisonsToRemove)
+ : ExpressionVisitor
+ {
+ protected override Expression VisitExtension(Expression node)
+ => node switch
+ {
+ _ when comparisonsToRemove.Contains(node)
+ => sqlExpressionFactory.Constant(true),
+
+ // This elides `AND true` from the predicate.
+ // TODO: We shouldn't need to do this explicitly, see #34556.
+ SqlBinaryExpression { OperatorType: ExpressionType.AndAlso } binary
+ => sqlExpressionFactory.MakeBinary(
+ ExpressionType.AndAlso,
+ (SqlExpression)Visit(binary.Left),
+ (SqlExpression)Visit(binary.Right),
+ binary.TypeMapping,
+ binary)!,
+
+ _ => base.VisitExtension(node)
+ };
+ }
}
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs
index f327c511cd3..d349d73e609 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs
@@ -97,6 +97,7 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
{
storeName = projection.Alias;
}
+
break;
}
@@ -114,7 +115,8 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
{
case ObjectArrayAccessExpression objectArrayProjectionExpression:
_projectionBindings[objectArrayProjectionExpression] = parameterExpression;
- valueExpression = CreateGetValueExpression(objectArrayProjectionExpression.Object, storeName, parameterExpression.Type);
+ valueExpression = CreateGetValueExpression(
+ objectArrayProjectionExpression.Object, storeName, parameterExpression.Type);
break;
case EntityProjectionExpression entityProjectionExpression:
@@ -144,11 +146,13 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
{
valueExpression = CreateGetValueExpression(valueExpression, storeNames[i], typeof(JObject));
}
+
break;
default:
throw new InvalidOperationException(
CoreStrings.TranslationFailed(binaryExpression.Print()));
}
+
break;
default:
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.InExpressionValuesExpandingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.InExpressionValuesExpandingExpressionVisitor.cs
index 173bd1403ed..98563fd0a2a 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.InExpressionValuesExpandingExpressionVisitor.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.InExpressionValuesExpandingExpressionVisitor.cs
@@ -4,7 +4,6 @@
#nullable disable
using System.Collections;
-using Microsoft.EntityFrameworkCore.Cosmos.Internal;
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
@@ -37,6 +36,7 @@ protected override Expression VisitExtension(Expression expression)
{
mutableValues.Add(sqlExpressionFactory.Constant(value, value?.GetType() ?? typeof(object), typeMapping));
}
+
values = mutableValues;
break;
}
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.PagingQueryingEnumerable.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.PagingQueryingEnumerable.cs
index 006d87c4645..e90c24664a5 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.PagingQueryingEnumerable.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.PagingQueryingEnumerable.cs
@@ -3,9 +3,7 @@
#nullable disable
-using System.Collections;
using Microsoft.EntityFrameworkCore.Cosmos.Diagnostics.Internal;
-using Microsoft.EntityFrameworkCore.Cosmos.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
using Newtonsoft.Json.Linq;
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.ReadItemQueryingEnumerable.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.ReadItemQueryingEnumerable.cs
index 2aa09cd6614..9e833adbb3c 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.ReadItemQueryingEnumerable.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.ReadItemQueryingEnumerable.cs
@@ -74,7 +74,8 @@ public string ToQueryString()
private bool TryGetResourceId(out string resourceId)
{
var jsonIdDefinition = _rootEntityType.GetJsonIdDefinition();
- Check.DebugAssert(jsonIdDefinition != null,
+ Check.DebugAssert(
+ jsonIdDefinition != null,
"Should not be using this enumerable if not using ReadItem, which needs an id definition.");
var values = new List
public class CosmosSqlTranslatingExpressionVisitor(
- QueryCompilationContext queryCompilationContext,
- ISqlExpressionFactory sqlExpressionFactory,
- ITypeMappingSource typeMappingSource,
- IMemberTranslatorProvider memberTranslatorProvider,
- IMethodCallTranslatorProvider methodCallTranslatorProvider,
- QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor)
+ QueryCompilationContext queryCompilationContext,
+ ISqlExpressionFactory sqlExpressionFactory,
+ ITypeMappingSource typeMappingSource,
+ IMemberTranslatorProvider memberTranslatorProvider,
+ IMethodCallTranslatorProvider methodCallTranslatorProvider,
+ QueryableMethodTranslatingExpressionVisitor queryableMethodTranslatingExpressionVisitor)
: ExpressionVisitor
{
private const string RuntimeParameterPrefix = QueryCompilationContext.QueryParameterPrefix + "entity_equality_";
@@ -346,22 +347,22 @@ protected override Expression VisitExtension(Expression extensionExpression)
case StructuralTypeShaperExpression shaper:
return new EntityReferenceExpression(shaper);
- // var result = Visit(entityShaperExpression.ValueBufferExpression);
- //
- // if (result is UnaryExpression
- // {
- // NodeType: ExpressionType.Convert,
- // Operand.NodeType: ExpressionType.Convert
- // } outerUnary
- // && outerUnary.Type == typeof(ValueBuffer)
- // && outerUnary.Operand.Type == typeof(object))
- // {
- // result = ((UnaryExpression)outerUnary.Operand).Operand;
- // }
- //
- // return result is EntityProjectionExpression entityProjectionExpression
- // ? new EntityReferenceExpression(entityProjectionExpression)
- // : QueryCompilationContext.NotTranslatedExpression;
+ // var result = Visit(entityShaperExpression.ValueBufferExpression);
+ //
+ // if (result is UnaryExpression
+ // {
+ // NodeType: ExpressionType.Convert,
+ // Operand.NodeType: ExpressionType.Convert
+ // } outerUnary
+ // && outerUnary.Type == typeof(ValueBuffer)
+ // && outerUnary.Operand.Type == typeof(object))
+ // {
+ // result = ((UnaryExpression)outerUnary.Operand).Operand;
+ // }
+ //
+ // return result is EntityProjectionExpression entityProjectionExpression
+ // ? new EntityReferenceExpression(entityProjectionExpression)
+ // : QueryCompilationContext.NotTranslatedExpression;
case ProjectionBindingExpression projectionBindingExpression:
return projectionBindingExpression.ProjectionMember != null
@@ -517,10 +518,9 @@ protected override Expression VisitMemberInit(MemberInitExpression memberInitExp
///
protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
- if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var propertyName)
- || methodCallExpression.TryGetIndexerArguments(_model, out source, out propertyName))
+ if (IsMemberAccess(methodCallExpression, _model, out var source, out var memberIdentity))
{
- return TryBindMember(Visit(source), MemberIdentity.Create(propertyName), out var result, out _)
+ return TryBindMember(Visit(source), memberIdentity, out var result, out _)
? result
: QueryCompilationContext.NotTranslatedExpression;
}
@@ -820,18 +820,28 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression)
ExpressionType.Negate or ExpressionType.NegateChecked
=> sqlExpressionFactory.Negate(sqlOperand!),
- ExpressionType.Convert or ExpressionType.ConvertChecked
- when operand.Type.IsInterface
- && unaryExpression.Type.GetInterfaces().Any(e => e == operand.Type)
+ // Convert nodes can be an explicit user gesture in the query, or they may get introduced by the compiler (e.g. when a Child is
+ // passed as an argument for a parameter of type Parent). The latter type should generally get stripped out as a pure C#/LINQ
+ // artifact that shouldn't affect translation, but the latter may be an indication from the user that they want to apply a
+ // type change.
+ ExpressionType.Convert or ExpressionType.ConvertChecked or ExpressionType.TypeAs
+ when operand.Type.IsInterface && unaryExpression.Type.GetInterfaces().Any(e => e == operand.Type)
+ // We strip out implicit conversions, e.g. float[] -> ReadOnlyMemory (for vector search)
+ || (unaryExpression.Method is { IsSpecialName: true, Name: "op_Implicit" }
+ && IsReadOnlyMemory(unaryExpression.Type.UnwrapNullableType()))
|| unaryExpression.Type.UnwrapNullableType() == operand.Type
|| unaryExpression.Type.UnwrapNullableType() == typeof(Enum)
// Object convert needs to be converted to explicit cast when mismatching types
- // But we let is pass here since we don't have explicit cast mechanism here and in some cases object convert is due to value types
+ // But we let it pass here since we don't have explicit cast mechanism here and in some cases object convert is due to value types
|| unaryExpression.Type == typeof(object)
=> sqlOperand!,
_ => QueryCompilationContext.NotTranslatedExpression
};
+
+ static bool IsReadOnlyMemory(Type type)
+ => type is { IsGenericType: true, IsGenericTypeDefinition: false }
+ && type.GetGenericTypeDefinition() == typeof(ReadOnlyMemory<>);
}
///
@@ -855,7 +865,7 @@ protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExp
MemberIdentity.Create(entityType.GetDiscriminatorPropertyName()),
out var discriminatorMember,
out _)
- && discriminatorMember is SqlExpression discriminatorColumn)
+ && discriminatorMember is SqlExpression discriminatorColumn)
{
var concreteEntityTypes = derivedType.GetConcreteDerivedTypesInclusive().ToList();
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ArrayConstantExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ArrayConstantExpression.cs
index 05c0700da2a..3ecc6cdcc46 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ArrayConstantExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ArrayConstantExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/CollectionShaperExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/CollectionShaperExpression.cs
index 2e158a9f39a..0a1621ee8fe 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/CollectionShaperExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/CollectionShaperExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ExistsExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ExistsExpression.cs
index 2548d510b5b..e11956a62ee 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ExistsExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ExistsExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
@@ -30,9 +31,7 @@ public class ExistsExpression : SqlExpression
///
public ExistsExpression(SelectExpression subquery, CoreTypeMapping? typeMapping)
: base(typeof(bool), typeMapping)
- {
- Subquery = subquery;
- }
+ => Subquery = subquery;
///
/// The subquery for which to check for element existence.
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/FragmentExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/FragmentExpression.cs
index 0a14e2f7430..cdb0bbff323 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/FragmentExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/FragmentExpression.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.
-using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
+
// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/FromSqlExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/FromSqlExpression.cs
index 728e7a1c736..93f181e2413 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/FromSqlExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/FromSqlExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/FromSqlQueryRootExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/FromSqlQueryRootExpression.cs
index d303f31ab28..80cb5f8d077 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/FromSqlQueryRootExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/FromSqlQueryRootExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/IAccessExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/IAccessExpression.cs
index ab442871509..50c94d21dce 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/IAccessExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/IAccessExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectArrayExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectArrayExpression.cs
index 56101fa3537..34ca49ea85d 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectArrayExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectArrayExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectArrayIndexExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectArrayIndexExpression.cs
index 3bda0c53100..d29a23d05ad 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectArrayIndexExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectArrayIndexExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectBinaryExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectBinaryExpression.cs
index 433fc59c4dc..273167825ae 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectBinaryExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectBinaryExpression.cs
@@ -85,7 +85,7 @@ public override ExpressionType NodeType
///
protected override Expression VisitChildren(ExpressionVisitor visitor)
{
- var left = (Expression)visitor.Visit(Left);
+ var left = visitor.Visit(Left);
var right = (Expression)visitor.Visit(Right);
return Update(left, right);
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectFunctionExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectFunctionExpression.cs
index 13b66a39449..8b0f1641486 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectFunctionExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectFunctionExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectReferenceExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectReferenceExpression.cs
index 5e5b55e3d36..b2fee0d2605 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectReferenceExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ObjectReferenceExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/OrderingExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/OrderingExpression.cs
index 88b8357145f..e5291c27787 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/OrderingExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/OrderingExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ProjectionExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ProjectionExpression.cs
index 3ce6b129add..b4378b9e757 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ProjectionExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ProjectionExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ReadItemInfo.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ReadItemInfo.cs
index 9a93540830f..9a023f49544 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ReadItemInfo.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ReadItemInfo.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarAccessExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarAccessExpression.cs
index 23df20780ea..412180a6f8e 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarAccessExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarAccessExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarArrayExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarArrayExpression.cs
index 3e483420717..ad1d5d59713 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarArrayExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarArrayExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarReferenceExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarReferenceExpression.cs
index 7879c8a3cda..e11286240fd 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarReferenceExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarReferenceExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarSubqueryExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarSubqueryExpression.cs
index 2a1f8638671..7fa2c1affcc 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarSubqueryExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/ScalarSubqueryExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
@@ -31,15 +32,11 @@ public ScalarSubqueryExpression(SelectExpression subquery)
subquery.Projection[0].Expression is SqlExpression sqlExpression
? sqlExpression.TypeMapping
: throw new UnreachableException("Can't construct scalar subquery over SelectExpression with non-SqlExpression projection"))
- {
- Subquery = subquery;
- }
+ => Subquery = subquery;
private ScalarSubqueryExpression(SelectExpression subquery, CoreTypeMapping? typeMapping)
: base(subquery.Projection[0].Type, typeMapping)
- {
- Subquery = subquery;
- }
+ => Subquery = subquery;
///
/// The subquery projecting single row with single scalar projection.
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/SelectExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/SelectExpression.cs
index 1b90e563fc3..3f31abdf5a8 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/SelectExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/SelectExpression.cs
@@ -1,8 +1,8 @@
// 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.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Internal;
+using Microsoft.EntityFrameworkCore.Internal;
// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
@@ -48,7 +48,7 @@ public SelectExpression(
SqlExpression? limit)
{
_sources = sources;
- Predicate = predicate;
+ Predicate = predicate is SqlConstantExpression { Value: true } ? null : predicate;
_projection = projections;
IsDistinct = distinct;
_orderings = orderings;
@@ -534,7 +534,7 @@ public override Type Type
/// 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 sealed override ExpressionType NodeType
+ public override ExpressionType NodeType
=> ExpressionType.Extension;
///
@@ -632,8 +632,7 @@ public SelectExpression Update(
return new SelectExpression(sources, predicate, projections, IsDistinct, orderings, offset, limit)
{
- _projectionMapping = projectionMapping,
- ReadItemInfo = ReadItemInfo
+ _projectionMapping = projectionMapping, ReadItemInfo = ReadItemInfo
};
}
@@ -646,8 +645,7 @@ public SelectExpression Update(
public SelectExpression WithReadItemInfo(ReadItemInfo readItemInfo)
=> new(Sources.ToList(), Predicate, Projection.ToList(), IsDistinct, Orderings.ToList(), Offset, Limit)
{
- _projectionMapping = _projectionMapping,
- ReadItemInfo = readItemInfo
+ _projectionMapping = _projectionMapping, ReadItemInfo = readItemInfo
};
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/SourceExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/SourceExpression.cs
index 8c0a8a716af..1bc688fcb60 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/SourceExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/SourceExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/SqlConditionalExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/SqlConditionalExpression.cs
index f321fd1ecef..241ba92f2c5 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/SqlConditionalExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/SqlConditionalExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/SqlFunctionExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/SqlFunctionExpression.cs
index 9b1513285dd..91b53ca7039 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/SqlFunctionExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/SqlFunctionExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/SqlParameterExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/SqlParameterExpression.cs
index f8f7dfa640a..fe317979f5a 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/SqlParameterExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/SqlParameterExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/ISqlExpressionFactory.cs b/src/EFCore.Cosmos/Query/Internal/ISqlExpressionFactory.cs
index 1745ba0d8a2..12c46524fc0 100644
--- a/src/EFCore.Cosmos/Query/Internal/ISqlExpressionFactory.cs
+++ b/src/EFCore.Cosmos/Query/Internal/ISqlExpressionFactory.cs
@@ -282,5 +282,4 @@ public interface ISqlExpressionFactory
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
SqlExpression Constant(object? value, Type type, CoreTypeMapping? typeMapping = null);
-
}
diff --git a/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs b/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs
index 9a25d3f209b..b4cfb0938a5 100644
--- a/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs
+++ b/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs
@@ -166,7 +166,9 @@ private SqlExpression ApplyTypeMappingOnSqlBinary(
// TODO: This infers based on the CLR type; need to properly infer based on the element type mapping
// TODO: being applied here (e.g. WHERE @p[1] = c.PropertyWithValueConverter). #34026
var arrayTypeMapping = left.TypeMapping
- ?? (typeMapping is null ? null : typeMappingSource.FindMapping(typeof(IEnumerable<>).MakeGenericType(typeMapping.ClrType)));
+ ?? (typeMapping is null
+ ? null
+ : typeMappingSource.FindMapping(typeof(IEnumerable<>).MakeGenericType(typeMapping.ClrType)));
return new SqlBinaryExpression(
ExpressionType.ArrayIndex,
ApplyTypeMapping(left, arrayTypeMapping),
@@ -435,12 +437,14 @@ private SqlExpression AndAlso(SqlExpression left, SqlExpression right, SqlExpres
{
return left;
}
+
// true && x -> x
// x && false -> false
if (left is SqlConstantExpression { Value: true } || right is SqlConstantExpression { Value: false })
{
return right;
}
+
// x is null && x is not null -> false
// x is not null && x is null -> false
if (left is SqlUnaryExpression { OperatorType: ExpressionType.Equal or ExpressionType.NotEqual } leftUnary
@@ -450,6 +454,7 @@ private SqlExpression AndAlso(SqlExpression left, SqlExpression right, SqlExpres
// the case in which left and right are the same expression is handled above
return Constant(false);
}
+
if (existingExpression is SqlBinaryExpression { OperatorType: ExpressionType.AndAlso } binaryExpr
&& left == binaryExpr.Left
&& right == binaryExpr.Right)
@@ -480,6 +485,7 @@ private SqlExpression OrElse(SqlExpression left, SqlExpression right, SqlExpress
{
return left;
}
+
// false || x -> x
// x || true -> true
if (left is SqlConstantExpression { Value: false }
@@ -487,6 +493,7 @@ private SqlExpression OrElse(SqlExpression left, SqlExpression right, SqlExpress
{
return right;
}
+
// x is null || x is not null -> true
// x is not null || x is null -> true
if (left is SqlUnaryExpression { OperatorType: ExpressionType.Equal or ExpressionType.NotEqual } leftUnary
@@ -496,6 +503,7 @@ private SqlExpression OrElse(SqlExpression left, SqlExpression right, SqlExpress
// the case in which left and right are the same expression is handled above
return Constant(true);
}
+
if (existingExpression is SqlBinaryExpression { OperatorType: ExpressionType.OrElse } binaryExpr
&& left == binaryExpr.Left
&& right == binaryExpr.Right)
diff --git a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMemberTranslator.cs b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMemberTranslator.cs
index 7ab28e58fcd..508995534f0 100644
--- a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMemberTranslator.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMemberTranslator.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMethodTranslator.cs b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMethodTranslator.cs
index bbec5002a50..c2e4504dcb3 100644
--- a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMethodTranslator.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosDateTimeMethodTranslator.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosEqualsTranslator.cs b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosEqualsTranslator.cs
index 89610cb278a..87a9b8b8e4d 100644
--- a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosEqualsTranslator.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosEqualsTranslator.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosMathTranslator.cs b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosMathTranslator.cs
index 66dade5a1f0..3dab9722b31 100644
--- a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosMathTranslator.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosMathTranslator.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosRandomTranslator.cs b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosRandomTranslator.cs
index 60b4d81d5d4..5269e7d3ba5 100644
--- a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosRandomTranslator.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosRandomTranslator.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosStringMemberTranslator.cs b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosStringMemberTranslator.cs
index 2823f3f77ac..ab59359ce7b 100644
--- a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosStringMemberTranslator.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosStringMemberTranslator.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosStringMethodTranslator.cs b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosStringMethodTranslator.cs
index 7a15a8ace21..845409fdc99 100644
--- a/src/EFCore.Cosmos/Query/Internal/Translators/CosmosStringMethodTranslator.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Translators/CosmosStringMethodTranslator.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
///
diff --git a/src/EFCore.Cosmos/Storage/Internal/ByteArrayConverter.cs b/src/EFCore.Cosmos/Storage/Internal/ByteArrayConverter.cs
index 75ca94f1a4e..fe63cd57d67 100644
--- a/src/EFCore.Cosmos/Storage/Internal/ByteArrayConverter.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/ByteArrayConverter.cs
@@ -51,7 +51,7 @@ public override void WriteJson(
public override object ReadJson(
JsonReader reader,
Type objectType,
- object existingValue,
+ object? existingValue,
JsonSerializer serializer)
{
if (reader.TokenType != JsonToken.StartArray)
diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs
index 7eeb334862f..a5c89b6b37d 100644
--- a/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs
@@ -359,7 +359,7 @@ private static async Task CreateItemOnceAsync(
response.Diagnostics.GetClientElapsedTime(),
response.Headers.RequestCharge,
response.Headers.ActivityId,
- parameters.Document["id"].ToString(),
+ parameters.Document["id"]!.ToString(),
parameters.ContainerId,
partitionKeyValue);
diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseCreator.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseCreator.cs
index d55b7a63f9e..79a6f678d0c 100644
--- a/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseCreator.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseCreator.cs
@@ -18,6 +18,8 @@ public class CosmosDatabaseCreator : IDatabaseCreator
private readonly IDesignTimeModel _designTimeModel;
private readonly IUpdateAdapterFactory _updateAdapterFactory;
private readonly IDatabase _database;
+ private readonly ICurrentDbContext _currentContext;
+ private readonly IDbContextOptions _contextOptions;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -29,12 +31,16 @@ public CosmosDatabaseCreator(
ICosmosClientWrapper cosmosClient,
IDesignTimeModel designTimeModel,
IUpdateAdapterFactory updateAdapterFactory,
- IDatabase database)
+ IDatabase database,
+ ICurrentDbContext currentContext,
+ IDbContextOptions contextOptions)
{
_cosmosClient = cosmosClient;
_designTimeModel = designTimeModel;
_updateAdapterFactory = updateAdapterFactory;
_database = database;
+ _currentContext = currentContext;
+ _contextOptions = contextOptions;
}
///
@@ -55,9 +61,11 @@ public virtual bool EnsureCreated()
if (created)
{
- Seed();
+ InsertData();
}
+ SeedData(created);
+
return created;
}
@@ -81,9 +89,11 @@ public virtual async Task EnsureCreatedAsync(CancellationToken cancellatio
if (created)
{
- await SeedAsync(cancellationToken).ConfigureAwait(false);
+ await InsertDataAsync(cancellationToken).ConfigureAwait(false);
}
+ await SeedDataAsync(created, cancellationToken).ConfigureAwait(false);
+
return created;
}
@@ -122,6 +132,7 @@ private static IEnumerable GetContainersToCreate(IModel mod
{
partitionKeyStoreNames = GetPartitionKeyStoreNames(entityType);
}
+
analyticalTtl ??= entityType.GetAnalyticalStoreTimeToLive();
defaultTtl ??= entityType.GetDefaultTimeToLive();
throughput ??= entityType.GetThroughput();
@@ -153,9 +164,9 @@ private static IEnumerable GetContainersToCreate(IModel mod
/// 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 Seed()
+ public virtual void InsertData()
{
- var updateAdapter = AddSeedData();
+ var updateAdapter = AddModelData();
_database.SaveChanges(updateAdapter.GetEntriesToSave());
}
@@ -166,14 +177,14 @@ public virtual void Seed()
/// 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 SeedAsync(CancellationToken cancellationToken = default)
+ public virtual Task InsertDataAsync(CancellationToken cancellationToken = default)
{
- var updateAdapter = AddSeedData();
+ var updateAdapter = AddModelData();
return _database.SaveChangesAsync(updateAdapter.GetEntriesToSave(), cancellationToken);
}
- private IUpdateAdapter AddSeedData()
+ private IUpdateAdapter AddModelData()
{
var updateAdapter = _updateAdapterFactory.CreateStandalone();
foreach (var entityType in _designTimeModel.Model.GetEntityTypes())
@@ -189,6 +200,52 @@ private IUpdateAdapter AddSeedData()
return updateAdapter;
}
+ ///
+ /// 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 SeedData(bool created)
+ {
+ var coreOptionsExtension =
+ _contextOptions.FindExtension()
+ ?? new CoreOptionsExtension();
+
+ var seed = coreOptionsExtension.Seeder;
+ if (seed != null)
+ {
+ seed(_currentContext.Context, created);
+ }
+ else if (coreOptionsExtension.AsyncSeeder != null)
+ {
+ throw new InvalidOperationException(CoreStrings.MissingSeeder);
+ }
+ }
+
+ ///
+ /// 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 async Task SeedDataAsync(bool created, CancellationToken cancellationToken = default)
+ {
+ var coreOptionsExtension =
+ _contextOptions.FindExtension()
+ ?? new CoreOptionsExtension();
+
+ var seedAsync = coreOptionsExtension.AsyncSeeder;
+ if (seedAsync != null)
+ {
+ await seedAsync(_currentContext.Context, created, cancellationToken).ConfigureAwait(false);
+ }
+ else if (coreOptionsExtension.Seeder != null)
+ {
+ throw new InvalidOperationException(CoreStrings.MissingSeeder);
+ }
+ }
+
///
/// 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.Cosmos/Storage/Internal/CosmosDatabaseWrapper.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseWrapper.cs
index 4eed39b426f..0fa8e4e7025 100644
--- a/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseWrapper.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/CosmosDatabaseWrapper.cs
@@ -246,7 +246,7 @@ private bool Save(IUpdateEntry entry)
if (propertyName != null)
{
document[propertyName] =
- JToken.FromObject(entityType.GetDiscriminatorValue(), CosmosClientWrapper.Serializer);
+ JToken.FromObject(entityType.GetDiscriminatorValue()!, CosmosClientWrapper.Serializer);
}
}
@@ -377,7 +377,7 @@ private Task SaveAsync(IUpdateEntry entry, CancellationToken cancellationT
if (propertyName != null)
{
document[propertyName] =
- JToken.FromObject(entityType.GetDiscriminatorValue(), CosmosClientWrapper.Serializer);
+ JToken.FromObject(entityType.GetDiscriminatorValue()!, CosmosClientWrapper.Serializer);
}
}
diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs
index 7588e248520..25f36155528 100644
--- a/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/CosmosTypeMappingSource.cs
@@ -28,8 +28,7 @@ public class CosmosTypeMappingSource : TypeMappingSource
///
public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
: base(dependencies)
- {
- _clrTypeMappings
+ => _clrTypeMappings
= new Dictionary
{
{
@@ -37,8 +36,6 @@ public CosmosTypeMappingSource(TypeMappingSourceDependencies dependencies)
typeof(JObject), jsonValueReaderWriter: dependencies.JsonValueReaderWriterSource.FindReaderWriter(typeof(JObject)))
}
};
- }
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosVectorTypeMapping.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosVectorTypeMapping.cs
index 717e7f92d34..e9279c00e62 100644
--- a/src/EFCore.Cosmos/Storage/Internal/CosmosVectorTypeMapping.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/CosmosVectorTypeMapping.cs
@@ -24,9 +24,9 @@ public class CosmosVectorTypeMapping : CosmosTypeMapping
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public static new CosmosVectorTypeMapping Default { get; }
- // Note that this default is not valid because dimensions cannot be zero. But since there is no reasonable
- // default dimensions size for a vector type, this is intentionally not valid rather than just being wrong.
- // The fundamental problem here is that type mappings are "required" to have some default now.
+ // Note that this default is not valid because dimensions cannot be zero. But since there is no reasonable
+ // default dimensions size for a vector type, this is intentionally not valid rather than just being wrong.
+ // The fundamental problem here is that type mappings are "required" to have some default now.
= new(typeof(byte[]), new CosmosVectorType(DistanceFunction.Cosine, 0));
///
@@ -64,7 +64,8 @@ public CosmosVectorTypeMapping(CosmosTypeMapping mapping, CosmosVectorType vecto
: this(
new CoreTypeMappingParameters(
mapping.ClrType,
- converter: mapping.Converter,
+ // This is a hack to allow both arrays and ROM types without different function overloads or type mappings.
+ converter: mapping.Converter?.GetType() == typeof(BytesToStringConverter) ? null : mapping.Converter,
mapping.Comparer,
mapping.KeyComparer,
elementMapping: mapping.ElementTypeMapping,
@@ -81,9 +82,7 @@ public CosmosVectorTypeMapping(CosmosTypeMapping mapping, CosmosVectorType vecto
///
protected CosmosVectorTypeMapping(CoreTypeMappingParameters parameters, CosmosVectorType vectorType)
: base(parameters)
- {
- VectorType = vectorType;
- }
+ => VectorType = vectorType;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -117,4 +116,35 @@ public override CoreTypeMapping WithComposedConverter(
///
protected override CoreTypeMapping Clone(CoreTypeMappingParameters parameters)
=> new CosmosVectorTypeMapping(parameters, VectorType);
+
+ ///
+ /// 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 JToken? GenerateJToken(object? value)
+ {
+ // This is a hack to allow both arrays and ROM types without different function overloads or type mappings.
+ var type = value?.GetType();
+ if (type?.IsArray is false)
+ {
+ if (type == typeof(ReadOnlyMemory))
+ {
+ value = ((ReadOnlyMemory)value!).ToArray();
+ }
+ else if (type == typeof(ReadOnlyMemory))
+ {
+ value = ((ReadOnlyMemory)value!).ToArray();
+ }
+ else if (type == typeof(ReadOnlyMemory))
+ {
+ value = ((ReadOnlyMemory)value!).ToArray();
+ }
+ }
+
+ return value == null
+ ? null
+ : JToken.FromObject(value, CosmosClientWrapper.Serializer);
+ }
}
diff --git a/src/EFCore.Cosmos/Storage/Internal/HttpException.cs b/src/EFCore.Cosmos/Storage/Internal/HttpException.cs
index 7a3b43ac727..6948fe6a121 100644
--- a/src/EFCore.Cosmos/Storage/Internal/HttpException.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/HttpException.cs
@@ -19,10 +19,8 @@ public class HttpException : Exception
///
public HttpException(HttpResponseMessage response)
: base(response.ReasonPhrase)
- {
- // An error occurred while sending the request.
- Response = response;
- }
+ // An error occurred while sending the request.
+ => Response = response;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Cosmos/Storage/Internal/JsonCosmosSerializer.cs b/src/EFCore.Cosmos/Storage/Internal/JsonCosmosSerializer.cs
index 9028cd3b0e6..291e6747e20 100644
--- a/src/EFCore.Cosmos/Storage/Internal/JsonCosmosSerializer.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/JsonCosmosSerializer.cs
@@ -28,7 +28,7 @@ public override T FromStream(Stream stream)
using var streamReader = new StreamReader(stream);
using var jsonTextReader = new JsonTextReader(streamReader);
- return GetSerializer().Deserialize(jsonTextReader);
+ return GetSerializer().Deserialize(jsonTextReader)!;
}
}
diff --git a/src/EFCore.Cosmos/Storage/Internal/ReadOnlyMemoryConverter.cs b/src/EFCore.Cosmos/Storage/Internal/ReadOnlyMemoryConverter.cs
index e086b4ccaa0..811e8c1d546 100644
--- a/src/EFCore.Cosmos/Storage/Internal/ReadOnlyMemoryConverter.cs
+++ b/src/EFCore.Cosmos/Storage/Internal/ReadOnlyMemoryConverter.cs
@@ -56,7 +56,7 @@ public static T[] ToArray(ReadOnlyMemory memory)
public static ReadOnlyMemory ToMemory(T[] array)
// If the array is empty, then return the default ReadOnlyMemory instance because this will compare the same as other empty
// ReadOnlyMemory instances, while the instance created with an empty array is considered not equal to the default.
- => array.Length == 0 ? default : new(array);
+ => array.Length == 0 ? default : new ReadOnlyMemory(array);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Design/Design/FluentApiCodeFragment.cs b/src/EFCore.Design/Design/FluentApiCodeFragment.cs
index 7ab2eb3d67a..e6bc6f0f24d 100644
--- a/src/EFCore.Design/Design/FluentApiCodeFragment.cs
+++ b/src/EFCore.Design/Design/FluentApiCodeFragment.cs
@@ -15,9 +15,7 @@ public class FluentApiCodeFragment : IMethodCallCodeFragment
///
/// The method's name.
public FluentApiCodeFragment(string method)
- {
- Method = method;
- }
+ => Method = method;
///
/// Gets the namespace of the method's declaring type.
diff --git a/src/EFCore.Design/Design/Internal/CSharpHelper.cs b/src/EFCore.Design/Design/Internal/CSharpHelper.cs
index cb9b7394f84..bcdc1e862e7 100644
--- a/src/EFCore.Design/Design/Internal/CSharpHelper.cs
+++ b/src/EFCore.Design/Design/Internal/CSharpHelper.cs
@@ -8,11 +8,8 @@
using System.Security;
using System.Text;
using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
-using Microsoft.CodeAnalysis.Simplification;
-using Microsoft.CodeAnalysis.Text;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;
@@ -1024,11 +1021,11 @@ protected virtual string GetCompositeEnumValue(Type type, Enum flags, bool fullN
}
return allValues.Aggregate(
- (string?)null,
- (previous, current) =>
- previous == null
- ? GetSimpleEnumValue(type, Enum.GetName(type, current)!, fullName)
- : previous + " | " + GetSimpleEnumValue(type, Enum.GetName(type, current)!, fullName))
+ (string?)null,
+ (previous, current) =>
+ previous == null
+ ? GetSimpleEnumValue(type, Enum.GetName(type, current)!, fullName)
+ : previous + " | " + GetSimpleEnumValue(type, Enum.GetName(type, current)!, fullName))
?? $"({Reference(type)}){UnknownLiteral(Convert.ChangeType(flags, Enum.GetUnderlyingType(type)))}";
}
diff --git a/src/EFCore.Design/Design/Internal/DbContextOperations.cs b/src/EFCore.Design/Design/Internal/DbContextOperations.cs
index 0c4ae03d1af..d076d3ac68e 100644
--- a/src/EFCore.Design/Design/Internal/DbContextOperations.cs
+++ b/src/EFCore.Design/Design/Internal/DbContextOperations.cs
@@ -1,14 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.IO;
using System.Text;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editing;
-using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.MSBuild;
-using Microsoft.CodeAnalysis.Simplification;
using Microsoft.EntityFrameworkCore.Infrastructure.Internal;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
@@ -134,7 +131,13 @@ public virtual string ScriptDbContext(string? contextType)
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual IReadOnlyList Optimize(
- string? outputDir, string? modelNamespace, string? contextTypeName, string? suffix, bool scaffoldModel, bool precompileQueries)
+ string? outputDir,
+ string? modelNamespace,
+ string? contextTypeName,
+ string? suffix,
+ bool scaffoldModel,
+ bool precompileQueries,
+ bool nativeAot)
{
var optimizeAllInAssembly = contextTypeName == "*";
var contexts = optimizeAllInAssembly ? CreateAllContexts() : [CreateContext(contextTypeName)];
@@ -154,6 +157,7 @@ public virtual IReadOnlyList Optimize(
precompileQueries,
context,
optimizeAllInAssembly,
+ nativeAot,
generatedFiles,
generatedFileNames);
contextOptimized = true;
@@ -184,6 +188,7 @@ private void Optimize(
bool precompileQueries,
DbContext context,
bool optimizeAllInAssembly,
+ bool nativeAot,
List generatedFiles,
HashSet generatedFileNames)
{
@@ -195,7 +200,9 @@ private void Optimize(
if (scaffoldModel
&& (!optimizeAllInAssembly || contextType.Assembly == _assembly))
{
- generatedFiles.AddRange(ScaffoldCompiledModel(outputDir, modelNamespace, context, suffix, services, generatedFileNames));
+ generatedFiles.AddRange(
+ ScaffoldCompiledModel(
+ outputDir, modelNamespace, context, suffix, nativeAot, services, generatedFileNames));
if (precompileQueries)
{
memberAccessReplacements = ((IRuntimeModel)context.GetService().Model).GetUnsafeAccessors();
@@ -204,13 +211,14 @@ private void Optimize(
if (precompileQueries)
{
- generatedFiles.AddRange(PrecompileQueries(
- outputDir,
- context,
- suffix,
- services,
- memberAccessReplacements ?? ((IRuntimeModel)context.Model).GetUnsafeAccessors(),
- generatedFileNames));
+ generatedFiles.AddRange(
+ PrecompileQueries(
+ outputDir,
+ context,
+ suffix,
+ services,
+ memberAccessReplacements ?? ((IRuntimeModel)context.Model).GetUnsafeAccessors(),
+ generatedFileNames));
}
}
@@ -219,14 +227,16 @@ private IReadOnlyList ScaffoldCompiledModel(
string? modelNamespace,
DbContext context,
string? suffix,
+ bool nativeAot,
IServiceProvider services,
ISet generatedFileNames)
{
var contextType = context.GetType();
if (contextType.Assembly != _assembly)
{
- _reporter.WriteWarning(DesignStrings.ContextAssemblyMismatch(
- _assembly.GetName().Name, contextType.ShortDisplayName(), contextType.Assembly.GetName().Name));
+ _reporter.WriteWarning(
+ DesignStrings.ContextAssemblyMismatch(
+ _assembly.GetName().Name, contextType.ShortDisplayName(), contextType.Assembly.GetName().Name));
}
if (outputDir == null)
@@ -257,6 +267,7 @@ private IReadOnlyList ScaffoldCompiledModel(
Language = _language,
UseNullableReferenceTypes = _nullable,
Suffix = suffix,
+ ForNativeAot = nativeAot,
GeneratedFileNames = generatedFileNames
});
@@ -277,7 +288,13 @@ private IReadOnlyList ScaffoldCompiledModel(
return scaffoldedFiles;
}
- private IReadOnlyList PrecompileQueries(string? outputDir, DbContext context, string? suffix, IServiceProvider services, IReadOnlyDictionary memberAccessReplacements, ISet generatedFileNames)
+ private IReadOnlyList PrecompileQueries(
+ string? outputDir,
+ DbContext context,
+ string? suffix,
+ IServiceProvider services,
+ IReadOnlyDictionary memberAccessReplacements,
+ ISet generatedFileNames)
{
outputDir = Path.GetFullPath(Path.Combine(_projectDir, outputDir ?? "Generated"));
@@ -285,6 +302,7 @@ private IReadOnlyList PrecompileQueries(string? outputDir, DbContext con
{
MSBuildLocator.RegisterDefaults();
}
+
// TODO: pass through properties
var workspace = MSBuildWorkspace.Create();
workspace.LoadMetadataForReferencedProjects = true;
@@ -293,6 +311,7 @@ private IReadOnlyList PrecompileQueries(string? outputDir, DbContext con
{
throw new NotSupportedException(DesignStrings.UncompilableProject(_project));
}
+
var compilation = project.GetCompilationAsync().GetAwaiter().GetResult()!;
var errorDiagnostics = compilation.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error).ToArray();
if (errorDiagnostics.Any())
@@ -456,8 +475,9 @@ private DbContext CreateContext(string? contextType, KeyValuePair
public DesignTimeConnectionStringResolver(Func? applicationServiceProviderAccessor)
- {
- _applicationServiceProviderAccessor = applicationServiceProviderAccessor;
- }
+ => _applicationServiceProviderAccessor = applicationServiceProviderAccessor;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Design/Design/Internal/LanguageBasedSelector.cs b/src/EFCore.Design/Design/Internal/LanguageBasedSelector.cs
index ef792f73cd3..3c586217eb7 100644
--- a/src/EFCore.Design/Design/Internal/LanguageBasedSelector.cs
+++ b/src/EFCore.Design/Design/Internal/LanguageBasedSelector.cs
@@ -21,9 +21,7 @@ public abstract class LanguageBasedSelector
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected LanguageBasedSelector(IEnumerable services)
- {
- Services = services;
- }
+ => Services = services;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Design/Design/Internal/MigrationsOperations.cs b/src/EFCore.Design/Design/Internal/MigrationsOperations.cs
index 419d658cd2e..e841e680ac9 100644
--- a/src/EFCore.Design/Design/Internal/MigrationsOperations.cs
+++ b/src/EFCore.Design/Design/Internal/MigrationsOperations.cs
@@ -220,8 +220,7 @@ public virtual void UpdateDatabase(
EnsureServices(services);
var migrator = services.GetRequiredService();
-
- migrator.Migrate(targetMigration: targetMigration);
+ migrator.Migrate(targetMigration);
}
_reporter.WriteInformation(DesignStrings.Done);
diff --git a/src/EFCore.Design/Design/Internal/OperationLoggerProvider.cs b/src/EFCore.Design/Design/Internal/OperationLoggerProvider.cs
index d03a884a82b..5bc19daf586 100644
--- a/src/EFCore.Design/Design/Internal/OperationLoggerProvider.cs
+++ b/src/EFCore.Design/Design/Internal/OperationLoggerProvider.cs
@@ -20,9 +20,7 @@ public class OperationLoggerProvider : ILoggerProvider
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public OperationLoggerProvider(IOperationReporter reporter)
- {
- _reporter = reporter;
- }
+ => _reporter = reporter;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Design/Design/Internal/OperationReporter.cs b/src/EFCore.Design/Design/Internal/OperationReporter.cs
index 84c3e097e39..80522b4a3ba 100644
--- a/src/EFCore.Design/Design/Internal/OperationReporter.cs
+++ b/src/EFCore.Design/Design/Internal/OperationReporter.cs
@@ -20,9 +20,7 @@ public class OperationReporter : IOperationReporter
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public OperationReporter(IOperationReportHandler? handler)
- {
- _handler = handler;
- }
+ => _handler = handler;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Design/Design/OperationExecutor.cs b/src/EFCore.Design/Design/OperationExecutor.cs
index 70953d2bc88..2c01e920389 100644
--- a/src/EFCore.Design/Design/OperationExecutor.cs
+++ b/src/EFCore.Design/Design/OperationExecutor.cs
@@ -541,13 +541,29 @@ public OptimizeContext(
var suffix = (string?)args["suffix"];
var scaffoldModel = (bool)(args["scaffoldModel"] ?? true);
var precompileQueries = (bool)(args["precompileQueries"] ?? false);
+ var nativeAot = (bool)(args["nativeAot"] ?? false);
- Execute(() => executor.OptimizeContextImpl(outputDir, modelNamespace, contextType, suffix, scaffoldModel, precompileQueries));
+ Execute(
+ () => executor.OptimizeContextImpl(
+ outputDir,
+ modelNamespace,
+ contextType,
+ suffix,
+ scaffoldModel,
+ precompileQueries,
+ nativeAot));
}
}
+
private IReadOnlyList OptimizeContextImpl(
- string? outputDir, string? modelNamespace, string? contextType, string? suffix, bool scaffoldModel, bool precompileQueries)
- => ContextOperations.Optimize(outputDir, modelNamespace, contextType, suffix, scaffoldModel, precompileQueries);
+ string? outputDir,
+ string? modelNamespace,
+ string? contextType,
+ string? suffix,
+ bool scaffoldModel,
+ bool precompileQueries,
+ bool nativeAot)
+ => ContextOperations.Optimize(outputDir, modelNamespace, contextType, suffix, scaffoldModel, precompileQueries, nativeAot);
///
/// Represents an operation to scaffold a and entity types for a database.
@@ -748,9 +764,7 @@ public abstract class OperationBase : MarshalByRefObject
///
/// The .
protected OperationBase(IOperationResultHandler resultHandler)
- {
- _resultHandler = resultHandler;
- }
+ => _resultHandler = resultHandler;
///
/// Executes an action passing exceptions to the .
diff --git a/src/EFCore.Design/EFCore.Design.csproj b/src/EFCore.Design/EFCore.Design.csproj
index 8916afeb532..caae39e9f4f 100644
--- a/src/EFCore.Design/EFCore.Design.csproj
+++ b/src/EFCore.Design/EFCore.Design.csproj
@@ -8,7 +8,7 @@
truetruetrue
- EF9100
+ $(NoWarn);EF9100
@@ -56,13 +56,13 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGenerator.cs
index fd3f544b2c2..ec5b7528229 100644
--- a/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGenerator.cs
+++ b/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGenerator.cs
@@ -19,9 +19,7 @@ public class CSharpMigrationOperationGenerator : ICSharpMigrationOperationGenera
///
/// The dependencies.
public CSharpMigrationOperationGenerator(CSharpMigrationOperationGeneratorDependencies dependencies)
- {
- Dependencies = dependencies;
- }
+ => Dependencies = dependencies;
///
/// Dependencies for this service.
diff --git a/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGeneratorDependencies.cs b/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGeneratorDependencies.cs
index cfb4ba2f596..0be3c5a308b 100644
--- a/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGeneratorDependencies.cs
+++ b/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGeneratorDependencies.cs
@@ -38,9 +38,7 @@ public sealed record CSharpMigrationOperationGeneratorDependencies
///
[EntityFrameworkInternal]
public CSharpMigrationOperationGeneratorDependencies(ICSharpHelper csharpHelper)
- {
- CSharpHelper = csharpHelper;
- }
+ => CSharpHelper = csharpHelper;
///
/// The C# helper.
diff --git a/src/EFCore.Design/Migrations/Design/CSharpMigrationsGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpMigrationsGenerator.cs
index 5695838b618..80222f83196 100644
--- a/src/EFCore.Design/Migrations/Design/CSharpMigrationsGenerator.cs
+++ b/src/EFCore.Design/Migrations/Design/CSharpMigrationsGenerator.cs
@@ -21,9 +21,7 @@ public CSharpMigrationsGenerator(
MigrationsCodeGeneratorDependencies dependencies,
CSharpMigrationsGeneratorDependencies csharpDependencies)
: base(dependencies)
- {
- CSharpDependencies = csharpDependencies;
- }
+ => CSharpDependencies = csharpDependencies;
///
/// Dependencies for this service.
diff --git a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
index 78412fd398e..d5efd7f6a81 100644
--- a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
+++ b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs
@@ -36,9 +36,7 @@ private static readonly MethodInfo HasTypeAnnotationMethodInfo
///
/// The dependencies.
public CSharpSnapshotGenerator(CSharpSnapshotGeneratorDependencies dependencies)
- {
- Dependencies = dependencies;
- }
+ => Dependencies = dependencies;
///
/// Dependencies for this service.
@@ -445,7 +443,8 @@ protected virtual void GenerateProperty(
var clrType = (FindValueConverter(property)?.ProviderClrType ?? property.ClrType)
.MakeNullable(property.IsNullable);
- var propertyBuilderName = $"{entityTypeBuilderName}.Property<{Code.Reference(clrType)}>({Code.Literal(property.Name)})";
+ var propertyCall = property.IsPrimitiveCollection ? "PrimitiveCollection" : "Property";
+ var propertyBuilderName = $"{entityTypeBuilderName}.{propertyCall}<{Code.Reference(clrType)}>({Code.Literal(property.Name)})";
stringBuilder
.AppendLine()
diff --git a/src/EFCore.Design/Migrations/Design/MigrationsCodeGenerator.cs b/src/EFCore.Design/Migrations/Design/MigrationsCodeGenerator.cs
index cea08eaa47d..6fdb07fc11f 100644
--- a/src/EFCore.Design/Migrations/Design/MigrationsCodeGenerator.cs
+++ b/src/EFCore.Design/Migrations/Design/MigrationsCodeGenerator.cs
@@ -19,9 +19,7 @@ public abstract class MigrationsCodeGenerator : IMigrationsCodeGenerator
///
/// The dependencies.
protected MigrationsCodeGenerator(MigrationsCodeGeneratorDependencies dependencies)
- {
- Dependencies = dependencies;
- }
+ => Dependencies = dependencies;
///
/// Gets the file extension code files should use.
diff --git a/src/EFCore.Design/Query/Internal/CSharpToLinqTranslator.cs b/src/EFCore.Design/Query/Internal/CSharpToLinqTranslator.cs
index ff6284edc1e..7fef6d89de0 100644
--- a/src/EFCore.Design/Query/Internal/CSharpToLinqTranslator.cs
+++ b/src/EFCore.Design/Query/Internal/CSharpToLinqTranslator.cs
@@ -78,9 +78,9 @@ private readonly Stack> _parame
///
/// The Roslyn syntax node to be translated.
///
- /// The for the Roslyn of which is a part.
+ /// The for the Roslyn of which is a part.
///
- /// A LINQ expression tree translated from the provided .
+ /// A LINQ expression tree translated from the provided .
///
/// 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
@@ -158,8 +158,9 @@ public override Expression VisitAnonymousObjectCreationExpression(AnonymousObjec
var position = Array.FindIndex(parameters, p => p.Name == name);
var parameter = parameters[position];
- var parameterType = ResolveType(parameter.Type) ?? throw new InvalidOperationException(
- "Could not resolve type symbol for: " + parameter.Type);
+ var parameterType = ResolveType(parameter.Type)
+ ?? throw new InvalidOperationException(
+ "Could not resolve type symbol for: " + parameter.Type);
parameterInfos[position] = new FakeParameterInfo(name, parameterType, position);
arguments[position] = Visit(initializer.Expression);
@@ -240,7 +241,8 @@ public override Expression VisitBinaryExpression(BinaryExpressionSyntax binary)
// String concatenation
SyntaxKind.AddExpression
when left.Type == typeof(string) && right.Type == typeof(string)
- => Add(left, right,
+ => Add(
+ left, right,
_stringConcatMethod ??=
typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) })),
@@ -258,11 +260,15 @@ public override Expression VisitBinaryExpression(BinaryExpressionSyntax binary)
// For bitwise operations over enums, we cast the enum to its underlying type before the bitwise operation, and then back to the
// enum afterwards (this is corresponds to the LINQ expression tree that the compiler generates)
SyntaxKind.BitwiseOrExpression when left.Type.IsEnum || right.Type.IsEnum
- => Convert(Or(Convert(left, left.Type.GetEnumUnderlyingType()), Convert(right, right.Type.GetEnumUnderlyingType())), left.Type),
+ => Convert(
+ Or(Convert(left, left.Type.GetEnumUnderlyingType()), Convert(right, right.Type.GetEnumUnderlyingType())), left.Type),
SyntaxKind.BitwiseAndExpression when left.Type.IsEnum || right.Type.IsEnum
- => Convert(And(Convert(left, left.Type.GetEnumUnderlyingType()), Convert(right, right.Type.GetEnumUnderlyingType())), left.Type),
+ => Convert(
+ And(Convert(left, left.Type.GetEnumUnderlyingType()), Convert(right, right.Type.GetEnumUnderlyingType())), left.Type),
SyntaxKind.ExclusiveOrExpression when left.Type.IsEnum || right.Type.IsEnum
- => Convert(ExclusiveOr(Convert(left, left.Type.GetEnumUnderlyingType()), Convert(right, right.Type.GetEnumUnderlyingType())), left.Type),
+ => Convert(
+ ExclusiveOr(Convert(left, left.Type.GetEnumUnderlyingType()), Convert(right, right.Type.GetEnumUnderlyingType())),
+ left.Type),
SyntaxKind.BitwiseOrExpression => Or(left, right),
SyntaxKind.BitwiseAndExpression => And(left, right),
@@ -274,14 +280,16 @@ SyntaxKind.ExclusiveOrExpression when left.Type.IsEnum || right.Type.IsEnum
SyntaxKind.LessThanOrEqualExpression => LessThanOrEqual(left, right),
SyntaxKind.GreaterThanExpression => GreaterThan(left, right),
SyntaxKind.GreaterThanOrEqualExpression => GreaterThanOrEqual(left, right),
- SyntaxKind.IsExpression => TypeIs(left, right is ConstantExpression { Value : Type type }
- ? type
- : throw new InvalidOperationException(
- $"Encountered {SyntaxKind.IsExpression} with non-constant type right argument: {right}")),
- SyntaxKind.AsExpression => TypeAs(left, right is ConstantExpression { Value : Type type }
- ? type
- : throw new InvalidOperationException(
- $"Encountered {SyntaxKind.AsExpression} with non-constant type right argument: {right}")),
+ SyntaxKind.IsExpression => TypeIs(
+ left, right is ConstantExpression { Value : Type type }
+ ? type
+ : throw new InvalidOperationException(
+ $"Encountered {SyntaxKind.IsExpression} with non-constant type right argument: {right}")),
+ SyntaxKind.AsExpression => TypeAs(
+ left, right is ConstantExpression { Value : Type type }
+ ? type
+ : throw new InvalidOperationException(
+ $"Encountered {SyntaxKind.AsExpression} with non-constant type right argument: {right}")),
SyntaxKind.CoalesceExpression => Coalesce(left, right),
_ => throw new ArgumentOutOfRangeException($"BinaryExpressionSyntax with {binary.Kind()}")
@@ -323,7 +331,8 @@ public override Expression VisitElementAccessExpression(ElementAccessExpressionS
switch (_semanticModel.GetTypeInfo(elementAccessExpression.Expression).ConvertedType)
{
case IArrayTypeSymbol:
- Check.DebugAssert(elementAccessExpression.ArgumentList.Arguments.Count == 1,
+ Check.DebugAssert(
+ elementAccessExpression.ArgumentList.Arguments.Count == 1,
$"ElementAccessExpressionSyntax over array with {arguments.Count} arguments");
return ArrayIndex(visitedExpression, Visit(arguments[0].Expression));
@@ -471,6 +480,7 @@ public override Expression VisitInterpolatedStringExpression(InterpolatedStringE
{
interpolationExpression = Convert(interpolationExpression, typeof(object));
}
+
arguments.Add(interpolationExpression);
formatBuilder.Append('{').Append(arguments.Count - 1).Append('}');
break;
@@ -545,48 +555,50 @@ public override Expression VisitInvocationExpression(InvocationExpressionSyntax
var typeTypeParameterMap = new Dictionary(GetTypeTypeParameters(methodSymbol.ContainingType));
var definitionMethodInfos = declaringType.GetMethods()
- .Where(m =>
- {
- if (m.Name == methodSymbol.Name
- && m.IsGenericMethodDefinition
- && m.GetGenericArguments() is var candidateGenericArguments
- && candidateGenericArguments.Length == originalDefinition.TypeParameters.Length
- && m.GetParameters() is var candidateParams
- && candidateParams.Length == originalDefinition.Parameters.Length)
+ .Where(
+ m =>
{
- var methodTypeParameterMap = new Dictionary(typeTypeParameterMap);
-
- // Prepare a dictionary that will be used to resolve generic type parameters (ITypeParameterSymbol) to the
- // corresponding reflection Type. This is needed to correctly (and recursively) resolve the type of parameters
- // below.
- foreach (var (symbol, type) in methodSymbol.TypeParameters.Zip(candidateGenericArguments))
+ if (m.Name == methodSymbol.Name
+ && m.IsGenericMethodDefinition
+ && m.GetGenericArguments() is var candidateGenericArguments
+ && candidateGenericArguments.Length == originalDefinition.TypeParameters.Length
+ && m.GetParameters() is var candidateParams
+ && candidateParams.Length == originalDefinition.Parameters.Length)
{
- if (symbol.Name != type.Name)
+ var methodTypeParameterMap = new Dictionary(typeTypeParameterMap);
+
+ // Prepare a dictionary that will be used to resolve generic type parameters (ITypeParameterSymbol) to the
+ // corresponding reflection Type. This is needed to correctly (and recursively) resolve the type of parameters
+ // below.
+ foreach (var (symbol, type) in methodSymbol.TypeParameters.Zip(candidateGenericArguments))
{
- return false;
- }
+ if (symbol.Name != type.Name)
+ {
+ return false;
+ }
- methodTypeParameterMap[symbol.Name] = type;
- }
+ methodTypeParameterMap[symbol.Name] = type;
+ }
- for (var i = 0; i < candidateParams.Length; i++)
- {
- var translatedParamType = ResolveType(originalDefinition.Parameters[i].Type, methodTypeParameterMap);
- if (translatedParamType != candidateParams[i].ParameterType)
+ for (var i = 0; i < candidateParams.Length; i++)
{
- return false;
+ var translatedParamType = ResolveType(originalDefinition.Parameters[i].Type, methodTypeParameterMap);
+ if (translatedParamType != candidateParams[i].ParameterType)
+ {
+ return false;
+ }
}
- }
- return true;
- }
+ return true;
+ }
- return false;
- }).ToArray();
+ return false;
+ }).ToArray();
if (definitionMethodInfos.Length != 1)
{
- throw new InvalidOperationException($"Invocation: Found {definitionMethodInfos.Length} matches for generic method: {invocation}");
+ throw new InvalidOperationException(
+ $"Invocation: Found {definitionMethodInfos.Length} matches for generic method: {invocation}");
}
var definitionMethodInfo = definitionMethodInfos[0];
@@ -1025,8 +1037,8 @@ private Expression VisitLambdaExpression(AnonymousFunctionExpressionSyntax lambd
var translatedParameters = new List();
foreach (var parameter in lambdaParameters)
{
- if (_semanticModel.GetDeclaredSymbol(parameter) is not { } parameterSymbol ||
- ResolveType(parameterSymbol.Type) is not { } parameterType)
+ if (_semanticModel.GetDeclaredSymbol(parameter) is not { } parameterSymbol
+ || ResolveType(parameterSymbol.Type) is not { } parameterType)
{
throw new InvalidOperationException("Could not found symbol for parameter lambda: " + parameter);
}
@@ -1034,8 +1046,11 @@ private Expression VisitLambdaExpression(AnonymousFunctionExpressionSyntax lambd
translatedParameters.Add(Parameter(parameterType, parameter.Identifier.Text));
}
- _parameterStack.Push(_parameterStack.Peek()
- .AddRange(translatedParameters.Select(p => new KeyValuePair(p.Name ?? throw new NotImplementedException(), p))));
+ _parameterStack.Push(
+ _parameterStack.Peek()
+ .AddRange(
+ translatedParameters.Select(
+ p => new KeyValuePair(p.Name ?? throw new NotImplementedException(), p))));
try
{
@@ -1074,7 +1089,8 @@ private Type ResolveType(ITypeSymbol typeSymbol, Dictionary? gener
case INamedTypeSymbol { IsAnonymousType: true } anonymousTypeSymbol:
_anonymousTypeDefinitions ??= LoadAnonymousTypes(anonymousTypeSymbol.ContainingAssembly);
var properties = anonymousTypeSymbol.GetMembers().OfType().ToArray();
- var found = _anonymousTypeDefinitions.TryGetValue(properties.Select(p => p.Name).ToArray(),
+ var found = _anonymousTypeDefinitions.TryGetValue(
+ properties.Select(p => p.Name).ToArray(),
out var anonymousTypeGenericDefinition);
Check.DebugAssert(found, "Anonymous type not found");
@@ -1241,7 +1257,8 @@ public override bool IsDefined(Type attributeType, bool inherit)
public override string Name { get; } = name;
- public override Type? ReflectedType => null;
+ public override Type? ReflectedType
+ => null;
// We implement GetValue since ExpressionTreeFuncletizer calls it to get the parameter value. In AOT generation time, we obviously
// have no parameter values, nor do we need them for the first part of the query pipeline.
@@ -1252,7 +1269,11 @@ public override bool IsDefined(Type attributeType, bool inherit)
? ""
: null;
- public override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder,
+ public override void SetValue(
+ object? obj,
+ object? value,
+ BindingFlags invokeAttr,
+ Binder? binder,
CultureInfo? culture)
=> throw new NotSupportedException();
@@ -1296,11 +1317,18 @@ public override MethodAttributes Attributes
public override RuntimeMethodHandle MethodHandle
=> throw new NotSupportedException();
- public override object Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters,
+ public override object Invoke(
+ object? obj,
+ BindingFlags invokeAttr,
+ Binder? binder,
+ object?[]? parameters,
CultureInfo? culture)
=> throw new NotSupportedException();
- public override object Invoke(BindingFlags invokeAttr, Binder? binder, object?[]? parameters,
+ public override object Invoke(
+ BindingFlags invokeAttr,
+ Binder? binder,
+ object?[]? parameters,
CultureInfo? culture)
=> throw new NotSupportedException();
}
diff --git a/src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs b/src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs
index 7e3537c1559..ce59c297756 100644
--- a/src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs
+++ b/src/EFCore.Design/Query/Internal/LinqToCSharpSyntaxTranslator.cs
@@ -50,6 +50,7 @@ internal LiftedState CreateChild()
{
child.Variables.Add(parameter, name);
}
+
child.VariableNames.UnionWith(VariableNames);
return child;
@@ -81,9 +82,7 @@ internal LiftedState CreateChild()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public LinqToCSharpSyntaxTranslator(SyntaxGenerator syntaxGenerator)
- {
- _g = syntaxGenerator;
- }
+ => _g = syntaxGenerator;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -645,10 +644,8 @@ protected override Expression VisitBlock(BlockExpression block)
{
throw new NotImplementedException("Label on last expression of an expression block");
}
- else
- {
- statements.Add(pendingLabeledStatement.WithStatement(EmptyStatement()));
- }
+
+ statements.Add(pendingLabeledStatement.WithStatement(EmptyStatement()));
}
// Above we transform top-level assignments (i = 8) to var-declarations with initializers (var i = 8); those variables have
@@ -657,7 +654,8 @@ protected override Expression VisitBlock(BlockExpression block)
// and either add them to the block, or lift them if we're an expression block.
var unassignedVariableDeclarations =
unassignedVariables.Select(
- v => (LocalDeclarationStatementSyntax)_g.LocalDeclarationStatement(Generate(v.Type), LookupVariableName(v), initializer: _g.DefaultExpression(Generate(v.Type))));
+ v => (LocalDeclarationStatementSyntax)_g.LocalDeclarationStatement(
+ Generate(v.Type), LookupVariableName(v), initializer: _g.DefaultExpression(Generate(v.Type))));
if (blockContext == ExpressionContext.Expression)
{
@@ -990,25 +988,25 @@ protected virtual ExpressionSyntax GenerateValue(object? value)
return value switch
{
int or long or uint or ulong or short or sbyte or ushort or byte or double or float or decimal or char
- or string or bool or null
+ or string or bool or null
=> (ExpressionSyntax)_g.LiteralExpression(value),
Type t => TypeOfExpression(Generate(t)),
Enum e => HandleEnum(e),
Guid g => ObjectCreationExpression(IdentifierName(nameof(Guid)))
- .WithArgumentList(
- ArgumentList(
- SingletonSeparatedList(
- Argument(
- LiteralExpression(
- SyntaxKind.StringLiteralExpression,
- Literal(g.ToString())))))),
+ .WithArgumentList(
+ ArgumentList(
+ SingletonSeparatedList(
+ Argument(
+ LiteralExpression(
+ SyntaxKind.StringLiteralExpression,
+ Literal(g.ToString())))))),
ITuple tuple
when tuple.GetType() is { IsGenericType: true } tupleType
- && tupleType.Name.StartsWith("ValueTuple`", StringComparison.Ordinal)
- && tupleType.Namespace == "System"
+ && tupleType.Name.StartsWith("ValueTuple`", StringComparison.Ordinal)
+ && tupleType.Namespace == "System"
=> HandleValueTuple(tuple),
ReferenceEqualityComparer equalityComparer
@@ -1184,7 +1182,8 @@ protected virtual ExpressionSyntax GenerateUnknownValue(object value)
throw new NotSupportedException(
$"Encountered a constant of unsupported type '{value.GetType().Name}'. Only primitive constant nodes are supported."
- + Environment.NewLine + value);
+ + Environment.NewLine
+ + value);
}
///
@@ -1339,30 +1338,19 @@ protected virtual bool TryGenerate(Type type, [NotNullWhen(true)] out TypeSyntax
{
return false;
}
- genericArguments.Add(syntax);
- }
- var generic = GenericName(
- Identifier(type.Name.Substring(0, type.Name.IndexOf('`'))),
- TypeArgumentList(SeparatedList(genericArguments)));
- if (type.IsNested)
- {
- result = QualifiedName(
- (NameSyntax)Generate(type.DeclaringType!),
- generic);
- return true;
+ genericArguments.Add(syntax);
}
- AddNamespace(type);
-
- result = generic;
+ result = GenerateGenericType(type, genericArguments, genericArguments.Count);
return true;
}
if (type.IsArray)
{
result = ArrayType(Generate(type.GetElementType()!))
- .WithRankSpecifiers(SingletonList(ArrayRankSpecifier(SingletonSeparatedList(OmittedArraySizeExpression()))));
+ .WithRankSpecifiers(
+ SingletonList(ArrayRankSpecifier(SingletonSeparatedList(OmittedArraySizeExpression()))));
return true;
}
@@ -1470,17 +1458,35 @@ protected virtual bool TryGenerate(Type type, [NotNullWhen(true)] out TypeSyntax
return true;
}
- if (type.IsNested)
- {
- AddNamespace(type.DeclaringType!);
- }
- else if (type.Namespace != null)
+ if (type.Namespace != null)
{
_collectedNamespaces.Add(type.Namespace);
}
result = IdentifierName(type.Name);
return true;
+
+ NameSyntax GenerateGenericType(Type type, List genericArguments, int length)
+ {
+ var offset = type.DeclaringType != null ? type.DeclaringType.GetGenericArguments().Length : 0;
+
+ var genericPartIndex = type.Name.IndexOf('`');
+ SimpleNameSyntax nameSyntax = genericPartIndex <= 0
+ ? IdentifierName(type.Name)
+ : GenericName(
+ Identifier(type.Name.Substring(0, genericPartIndex)),
+ TypeArgumentList(SeparatedList(genericArguments.Skip(offset).Take(length - offset))));
+ if (type.DeclaringType == null)
+ {
+ AddNamespace(type);
+
+ return nameSyntax;
+ }
+
+ return QualifiedName(
+ GenerateGenericType(type.DeclaringType, genericArguments, offset),
+ nameSyntax);
+ }
}
///
@@ -1537,7 +1543,7 @@ protected override Expression VisitLambda(Expression lambda)
SeparatedList(
lambda.Parameters.Select(
p => Parameter(Identifier(LookupVariableName(p)))
- .WithType(p.Type.IsAnonymousType() ? null : Generate(p.Type))))),
+ .WithType(p.Type.IsAnonymousType() ? null : Generate(p.Type))))),
blockBody,
expressionBody);
@@ -1617,7 +1623,7 @@ protected override Expression VisitMember(MemberExpression member)
case { Member: FieldInfo closureField, Expression: ConstantExpression constantExpression }
when constantExpression.Type.Attributes.HasFlag(TypeAttributes.NestedPrivate)
- && System.Attribute.IsDefined(constantExpression.Type, typeof(CompilerGeneratedAttribute), inherit: true):
+ && System.Attribute.IsDefined(constantExpression.Type, typeof(CompilerGeneratedAttribute), inherit: true):
// Unwrap closure
VisitConstant(Expression.Constant(closureField.GetValue(constantExpression.Value), member.Type));
break;
@@ -1943,7 +1949,7 @@ protected override Expression VisitMethodCall(MethodCallExpression call)
// Extension syntax
if (call.Method.IsDefined(typeof(ExtensionAttribute), inherit: false)
- && !(arguments[0].Expression is LiteralExpressionSyntax literal && literal.IsKind(SyntaxKind.NullLiteralExpression)))
+ && !IsNull(arguments[0].Expression))
{
Result = InvocationExpression(
MemberAccessExpression(
@@ -1963,7 +1969,7 @@ protected override Expression VisitMethodCall(MethodCallExpression call)
{
var expression = call switch
{
- { Method.IsStatic: true } => GetMemberAccessesForAllDeclaringTypes(call.Method.DeclaringType),
+ { Method.IsStatic: true } => Generate(call.Method.DeclaringType),
// If the member isn't declared on the same type as the expression, (e.g. explicit interface implementation), add
// a cast up to the declaring type.
@@ -1973,14 +1979,6 @@ protected override Expression VisitMethodCall(MethodCallExpression call)
_ => Translate(call.Object)
};
- ExpressionSyntax GetMemberAccessesForAllDeclaringTypes(Type type)
- => type.DeclaringType is null
- ? Generate(type)
- : MemberAccessExpression(
- SyntaxKind.SimpleMemberAccessExpression,
- GetMemberAccessesForAllDeclaringTypes(type.DeclaringType),
- IdentifierName(type.Name));
-
if (call.Method.Name.StartsWith("get_", StringComparison.Ordinal)
&& call.Method.GetParameters().Length == 1
&& call.Method is { IsHideBySig: true, IsSpecialName: true })
@@ -2031,6 +2029,14 @@ void ProcessType(Type type)
}
}
}
+
+ static bool IsNull(ExpressionSyntax expr) => expr switch
+ {
+ LiteralExpressionSyntax literal when literal.IsKind(SyntaxKind.NullLiteralExpression) => true,
+ CastExpressionSyntax cast => IsNull(cast.Expression),
+ ParenthesizedExpressionSyntax parenthesized => IsNull(parenthesized.Expression),
+ _ => false
+ };
}
///
@@ -2704,7 +2710,13 @@ private ExpressionSyntax[] TranslateList(IReadOnlyList list)
var liftedStatementsPosition = _liftedState.Statements.Count;
- var translated = Translate(expression);
+ var translated = expression switch
+ {
+ // Add an explicit cast to avoid overload resolution ambiguity
+ ConstantExpression c
+ when c.Value is null => (ExpressionSyntax)_g.ConvertExpression(Generate(c.Type), GenerateValue(c.Value)),
+ _ => Translate(expression)
+ };
if (_liftedState.Statements.Count > liftedStatementsPosition)
{
diff --git a/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs b/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs
index cde91a000ab..de2957a65d7 100644
--- a/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs
+++ b/src/EFCore.Design/Query/Internal/PrecompiledQueryCodeGenerator.cs
@@ -38,8 +38,9 @@ public class PrecompiledQueryCodeGenerator : IPrecompiledQueryCodeGenerator
private const string InterceptorsNamespace = "Microsoft.EntityFrameworkCore.GeneratedInterceptors";
- ///
- public string? Language => "C#";
+ ///
+ public string? Language
+ => "C#";
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -221,7 +222,7 @@ public virtual IReadOnlyList GeneratePrecompiledQueries(
}
catch (Exception e)
{
- precompilationErrors.Add(new(querySyntax, e));
+ precompilationErrors.Add(new QueryPrecompilationError(querySyntax, e));
continue;
}
@@ -245,6 +246,7 @@ public virtual IReadOnlyList GeneratePrecompiledQueries(
{
_code.AppendLine(unsafeAccessor.NormalizeWhitespace().ToFullString());
}
+
_code.AppendLine("#endregion Unsafe accessors");
}
@@ -314,7 +316,7 @@ public InterceptsLocationAttribute(string filePath, int line, int column) { }
generatedFileNames,
".EFInterceptors" + suffix + Path.GetExtension(syntaxTree.FilePath),
CompiledModelScaffolder.MaxFileNameLength);
- return new(name, _code.ToString());
+ return new ScaffoldedFile(name, _code.ToString());
}
///
@@ -467,7 +469,7 @@ private void GenerateOperatorInterceptor(
? (reducedOperatorSymbol.Parameters[0].Name, reducedOperatorSymbol.Parameters[0].Type)
: ("source", reducedOperatorSymbol.ReceiverType!);
- if (sourceTypeSymbol is not INamedTypeSymbol { TypeArguments: [var sourceElementTypeSymbol]})
+ if (sourceTypeSymbol is not INamedTypeSymbol { TypeArguments: [var sourceElementTypeSymbol] })
{
throw new UnreachableException($"Non-IQueryable first parameter in LINQ operator '{operatorSymbol.Name}'");
}
@@ -498,7 +500,8 @@ when namedReturnType2.AllInterfaces.Prepend(namedReturnType2)
// Output the interceptor method signature preceded by the [InterceptsLocation] attribute.
var startPosition = operatorSyntax.SyntaxTree.GetLineSpan(memberAccessSyntax.Name.Span, cancellationToken).StartLinePosition;
var interceptorName = $"Query{queryNum}_{memberAccessSyntax.Name}{operatorNum}";
- code.AppendLine($"""[InterceptsLocation(@"{operatorSyntax.SyntaxTree.FilePath.Replace("\"","\"\"")}", {startPosition.Line + 1}, {startPosition.Character + 1})]""");
+ code.AppendLine(
+ $"""[InterceptsLocation(@"{operatorSyntax.SyntaxTree.FilePath.Replace("\"", "\"\"")}", {startPosition.Line + 1}, {startPosition.Character + 1})]""");
GenerateInterceptorMethodSignature();
code.AppendLine("{").IncrementIndent();
@@ -550,8 +553,8 @@ when namedReturnType2.AllInterfaces.Prepend(namedReturnType2)
|| genericDefinition == typeof(IAsyncEnumerable<>));
var isQueryable = !isAsync
- && operatorExpression.Type.IsGenericType
- && operatorExpression.Type.GetGenericTypeDefinition() == typeof(IQueryable<>);
+ && operatorExpression.Type.IsGenericType
+ && operatorExpression.Type.GetGenericTypeDefinition() == typeof(IQueryable<>);
var returnValue = isAsync
? $"IAsyncEnumerable<{sourceElementTypeName}>"
@@ -583,7 +586,8 @@ when namedReturnType2.AllInterfaces.Prepend(namedReturnType2)
// TODO: This is an additional runtime allocation; if we had System.Linq.Async we wouldn't need this. We could
// have additional versions of all async terminating operators over IAsyncEnumerable (effectively duplicating
// System.Linq.Async) as an alternative.
- code.AppendLine($"var asyncQueryingEnumerable = new PrecompiledQueryableAsyncEnumerableAdapter<{sourceElementTypeName}>(queryingEnumerable);");
+ code.AppendLine(
+ $"var asyncQueryingEnumerable = new PrecompiledQueryableAsyncEnumerableAdapter<{sourceElementTypeName}>(queryingEnumerable);");
code.Append("return asyncQueryingEnumerable");
}
else
@@ -636,13 +640,16 @@ void GenerateInterceptorMethodSignature()
.Append(' ')
.Append(interceptorName);
- var (typeParameters, constraints) = (reducedOperatorSymbol.IsGenericMethod, reducedOperatorSymbol.ContainingType.IsGenericType) switch
- {
- (true, false) => (reducedOperatorSymbol.TypeParameters, ((MethodDeclarationSyntax)_g.MethodDeclaration(reducedOperatorSymbol)).ConstraintClauses),
- (false, true) => (reducedOperatorSymbol.ContainingType.TypeParameters, ((TypeDeclarationSyntax)_g.Declaration(reducedOperatorSymbol.ContainingType)).ConstraintClauses),
- (false, false) => ([], []),
- (true, true) => throw new NotImplementedException("Generic method on generic type not supported")
- };
+ var (typeParameters, constraints) =
+ (reducedOperatorSymbol.IsGenericMethod, reducedOperatorSymbol.ContainingType.IsGenericType) switch
+ {
+ (true, false) => (reducedOperatorSymbol.TypeParameters,
+ ((MethodDeclarationSyntax)_g.MethodDeclaration(reducedOperatorSymbol)).ConstraintClauses),
+ (false, true) => (reducedOperatorSymbol.ContainingType.TypeParameters,
+ ((TypeDeclarationSyntax)_g.Declaration(reducedOperatorSymbol.ContainingType)).ConstraintClauses),
+ (false, false) => ([], []),
+ (true, true) => throw new NotImplementedException("Generic method on generic type not supported")
+ };
if (typeParameters.Length > 0)
{
@@ -774,7 +781,8 @@ void GenerateCapturedVariableExtractors(
var collectedNamespaces = new HashSet();
var unsafeAccessors = new HashSet();
var roslynPathSegment = _linqToCSharpTranslator.TranslateExpression(
- linqPathSegment, constantReplacements: null, _memberAccessReplacements, collectedNamespaces, unsafeAccessors);
+ linqPathSegment, constantReplacements: null, _memberAccessReplacements, collectedNamespaces,
+ unsafeAccessors);
var variableName = capturedVariablesPathTree.ExpressionType.Name;
variableName = char.ToLower(variableName[0]) + variableName[1..^"Expression".Length] + ++variableCounter;
@@ -1066,10 +1074,14 @@ or nameof(EntityFrameworkQueryableExtensions.ToListAsync)
method.GetParameters()[1].ParameterType.GenericTypeArguments[0].GenericTypeArguments[1])),
// ExecuteDelete/Update behave just like other scalar-returning operators
- nameof(EntityFrameworkQueryableExtensions.ExecuteDeleteAsync) when method.DeclaringType == typeof(EntityFrameworkQueryableExtensions)
- => RewriteToSync(typeof(EntityFrameworkQueryableExtensions).GetMethod(nameof(EntityFrameworkQueryableExtensions.ExecuteDelete))),
- nameof(EntityFrameworkQueryableExtensions.ExecuteUpdateAsync) when method.DeclaringType == typeof(EntityFrameworkQueryableExtensions)
- => RewriteToSync(typeof(EntityFrameworkQueryableExtensions).GetMethod(nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate))),
+ nameof(EntityFrameworkQueryableExtensions.ExecuteDeleteAsync) when method.DeclaringType
+ == typeof(EntityFrameworkQueryableExtensions)
+ => RewriteToSync(
+ typeof(EntityFrameworkQueryableExtensions).GetMethod(nameof(EntityFrameworkQueryableExtensions.ExecuteDelete))),
+ nameof(EntityFrameworkQueryableExtensions.ExecuteUpdateAsync) when method.DeclaringType
+ == typeof(EntityFrameworkQueryableExtensions)
+ => RewriteToSync(
+ typeof(EntityFrameworkQueryableExtensions).GetMethod(nameof(EntityFrameworkQueryableExtensions.ExecuteUpdate))),
// In the regular case (sync terminating operator which needs to stay in the query tree), simply compose the terminating
// operator over the penultimate and return that.
diff --git a/src/EFCore.Design/Query/Internal/QueryLocator.cs b/src/EFCore.Design/Query/Internal/QueryLocator.cs
index aa2ed595246..4e30a445f55 100644
--- a/src/EFCore.Design/Query/Internal/QueryLocator.cs
+++ b/src/EFCore.Design/Query/Internal/QueryLocator.cs
@@ -33,7 +33,6 @@ public class QueryLocator : CSharpSyntaxWalker
private List _locatedQueries = null!;
private List _precompilationErrors = null!;
-
///
/// Loads a new , representing a user project in which to locate queries.
///
@@ -55,7 +54,7 @@ public virtual void Initialize(Compilation compilation)
///
/// A in which to locate EF LINQ queries.
///
- /// A list of errors populated with dynamic LINQ queries detected in .
+ /// A list of errors populated with dynamic LINQ queries detected in .
///
/// A to observe while waiting for the task to complete.
/// A list of EF LINQ queries confirmed to be compatible with precompilation.
@@ -82,7 +81,7 @@ public virtual IReadOnlyList LocateQueries(
_cancellationToken = cancellationToken;
_semanticModel = _compilation.GetSemanticModel(syntaxTree);
- _locatedQueries = new();
+ _locatedQueries = new List();
_precompilationErrors = precompilationErrors;
Visit(syntaxTree.GetRoot(cancellationToken));
@@ -247,7 +246,8 @@ private bool ProcessQueryCandidate(InvocationExpressionSyntax query)
if (innerExpression is QueryExpressionSyntax or ParenthesizedExpressionSyntax { Expression: QueryExpressionSyntax })
{
_precompilationErrors.Add(
- new(query, new InvalidOperationException(DesignStrings.QueryComprehensionSyntaxNotSupportedInPrecompiledQueries)));
+ new PrecompiledQueryCodeGenerator.QueryPrecompilationError(
+ query, new InvalidOperationException(DesignStrings.QueryComprehensionSyntaxNotSupportedInPrecompiledQueries)));
return false;
}
@@ -273,7 +273,9 @@ private bool ProcessQueryCandidate(InvocationExpressionSyntax query)
return true;
}
- _precompilationErrors.Add(new(query, new InvalidOperationException(DesignStrings.DynamicQueryNotSupported)));
+ _precompilationErrors.Add(
+ new PrecompiledQueryCodeGenerator.QueryPrecompilationError(
+ query, new InvalidOperationException(DesignStrings.DynamicQueryNotSupported)));
return false;
bool IsDbContext(ExpressionSyntax expression)
diff --git a/src/EFCore.Design/Query/Internal/RuntimeModelLinqToCSharpSyntaxTranslator.cs b/src/EFCore.Design/Query/Internal/RuntimeModelLinqToCSharpSyntaxTranslator.cs
index def66e6c68c..06ef309ed3d 100644
--- a/src/EFCore.Design/Query/Internal/RuntimeModelLinqToCSharpSyntaxTranslator.cs
+++ b/src/EFCore.Design/Query/Internal/RuntimeModelLinqToCSharpSyntaxTranslator.cs
@@ -1,14 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-// ReSharper disable once CheckNamespace
-
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Design.Internal;
+// ReSharper disable once CheckNamespace
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace Microsoft.EntityFrameworkCore.Query.Internal;
@@ -29,7 +28,8 @@ public class RuntimeModelLinqToCSharpSyntaxTranslator : LinqToCSharpSyntaxTransl
/// 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 RuntimeModelLinqToCSharpSyntaxTranslator(SyntaxGenerator syntaxGenerator) : base(syntaxGenerator)
+ public RuntimeModelLinqToCSharpSyntaxTranslator(SyntaxGenerator syntaxGenerator)
+ : base(syntaxGenerator)
{
}
@@ -118,7 +118,10 @@ protected override void TranslateNonPublicMemberAccess(MemberExpression memberEx
/// 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 override void TranslateNonPublicMemberAssignment(MemberExpression memberExpression, Expression value, SyntaxKind assignmentKind)
+ protected override void TranslateNonPublicMemberAssignment(
+ MemberExpression memberExpression,
+ Expression value,
+ SyntaxKind assignmentKind)
{
var propertyInfo = memberExpression.Member as PropertyInfo;
var member = propertyInfo?.SetMethod ?? memberExpression.Member;
@@ -134,15 +137,18 @@ protected override void TranslateNonPublicMemberAssignment(MemberExpression memb
Result = InvocationExpression(
IdentifierName(methodName.Name),
- ArgumentList(SeparatedList(new[]
- {
- Argument(Translate(memberExpression.Expression)),
- Argument(Translate(value))
- })));
+ ArgumentList(
+ SeparatedList(
+ new[]
+ {
+ Argument(Translate(memberExpression.Expression)),
+ Argument(Translate(value))
+ })));
}
else
{
- Result = AssignmentExpression(assignmentKind,
+ Result = AssignmentExpression(
+ assignmentKind,
InvocationExpression(
IdentifierName(methodName.Name),
ArgumentList(SeparatedList(new[] { Argument(Translate(memberExpression.Expression)) }))),
diff --git a/src/EFCore.Design/Scaffolding/CompiledModelCodeGenerationOptions.cs b/src/EFCore.Design/Scaffolding/CompiledModelCodeGenerationOptions.cs
index 409565d74a8..74774ea74dc 100644
--- a/src/EFCore.Design/Scaffolding/CompiledModelCodeGenerationOptions.cs
+++ b/src/EFCore.Design/Scaffolding/CompiledModelCodeGenerationOptions.cs
@@ -38,6 +38,12 @@ public class CompiledModelCodeGenerationOptions
/// The suffix to attach to the name of all the generated files.
public virtual string? Suffix { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the generated code should be compatible with NativeAOT.
+ ///
+ /// A value indicating whether the generated code should be compatible with NativeAOT.
+ public virtual bool ForNativeAot { get; set; }
+
///
/// Gets or sets the set of file names generated so far.
///
diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpModelGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpModelGenerator.cs
index a2eb3f45bfa..7bdbd7366c3 100644
--- a/src/EFCore.Design/Scaffolding/Internal/CSharpModelGenerator.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/CSharpModelGenerator.cs
@@ -80,10 +80,11 @@ public override ScaffoldedModel GenerateModel(
var resultingFiles = new ScaffoldedModel
{
ContextFile = new ScaffoldedFile
- (options.ContextDir != null
+ (
+ options.ContextDir != null
? Path.Combine(options.ContextDir, dbContextFileName)
: dbContextFileName,
- generatedCode)
+ generatedCode)
};
foreach (var entityType in model.GetEntityTypes())
diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
index bc65c7aeed0..8a6f8f35c99 100644
--- a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
@@ -68,59 +68,63 @@ public virtual IReadOnlyCollection GenerateModel(
var assemblyAttributesCode = CreateAssemblyAttributes(options.ModelNamespace, options.ContextType, nullable);
var assemblyInfoFileName = UniquifyFileName(options.ContextType.ShortDisplayName() + AssemblyAttributesSuffix, options);
- scaffoldedFiles.Add(new(assemblyInfoFileName, assemblyAttributesCode));
+ scaffoldedFiles.Add(new ScaffoldedFile(assemblyInfoFileName, assemblyAttributesCode));
- var unsafeAccessorClassNames = new BidirectionalDictionary();
- var unsafeAccessorTypes = new Dictionary>();
var memberAccessReplacements = new Dictionary();
- foreach (var entityType in model.GetEntityTypes())
+ if (options.ForNativeAot)
{
- RegisterPrivateAccessors(entityType, options, unsafeAccessorClassNames, unsafeAccessorTypes, memberAccessReplacements);
-
- foreach (var navigation in entityType.GetDeclaredNavigations())
+ var unsafeAccessorClassNames = new BidirectionalDictionary();
+ var unsafeAccessorTypes = new Dictionary>();
+ foreach (var entityType in model.GetEntityTypes())
{
- RegisterPrivateAccessors(
- navigation, options.ModelNamespace, unsafeAccessorClassNames, unsafeAccessorTypes, memberAccessReplacements);
- }
+ RegisterPrivateAccessors(entityType, options, unsafeAccessorClassNames, unsafeAccessorTypes, memberAccessReplacements);
- foreach (var navigation in entityType.GetDeclaredSkipNavigations())
- {
- RegisterPrivateAccessors(
- navigation, options.ModelNamespace, unsafeAccessorClassNames, unsafeAccessorTypes, memberAccessReplacements);
+ foreach (var navigation in entityType.GetDeclaredNavigations())
+ {
+ RegisterPrivateAccessors(
+ navigation, options.ModelNamespace, unsafeAccessorClassNames, unsafeAccessorTypes, memberAccessReplacements);
+ }
+
+ foreach (var navigation in entityType.GetDeclaredSkipNavigations())
+ {
+ RegisterPrivateAccessors(
+ navigation, options.ModelNamespace, unsafeAccessorClassNames, unsafeAccessorTypes, memberAccessReplacements);
+ }
}
- }
- foreach (var unsafeAccessorPair in unsafeAccessorTypes)
- {
- (var unsafeAccessorType, var members) = unsafeAccessorPair;
- var generatedCode = GenerateUnsafeAccessorType(
- unsafeAccessorType,
- members,
- options.ModelNamespace,
- unsafeAccessorClassNames[unsafeAccessorType],
- memberAccessReplacements,
- nullable);
+ foreach (var unsafeAccessorPair in unsafeAccessorTypes)
+ {
+ var (unsafeAccessorType, members) = unsafeAccessorPair;
+ var generatedCode = GenerateUnsafeAccessorType(
+ unsafeAccessorType,
+ members,
+ options.ModelNamespace,
+ unsafeAccessorClassNames[unsafeAccessorType],
+ memberAccessReplacements,
+ nullable);
- var entityTypeFileName = UniquifyFileName(unsafeAccessorClassNames[unsafeAccessorType], options);
- scaffoldedFiles.Add(new(entityTypeFileName, generatedCode));
+ var entityTypeFileName = UniquifyFileName(unsafeAccessorClassNames[unsafeAccessorType], options);
+ scaffoldedFiles.Add(new ScaffoldedFile(entityTypeFileName, generatedCode));
+ }
}
var modelCode = CreateModel(options.ModelNamespace, options.ContextType, nullable);
var modelFileName = UniquifyFileName(options.ContextType.ShortDisplayName() + ModelSuffix, options);
- scaffoldedFiles.Add(new(modelFileName, modelCode));
+ scaffoldedFiles.Add(new ScaffoldedFile(modelFileName, modelCode));
var configurationClassNames = new Dictionary();
var modelBuilderCode = CreateModelBuilder(
- model, options.ModelNamespace, options.ContextType, configurationClassNames, nullable);
+ model, options.ModelNamespace, options.ContextType, configurationClassNames, nullable, options.ForNativeAot);
var modelBuilderFileName = UniquifyFileName(options.ContextType.ShortDisplayName() + ModelBuilderSuffix, options);
- scaffoldedFiles.Add(new(modelBuilderFileName, modelBuilderCode));
+ scaffoldedFiles.Add(new ScaffoldedFile(modelBuilderFileName, modelBuilderCode));
foreach (var entityType in model.GetEntityTypesInHierarchicalOrder())
{
- var generatedCode = GenerateEntityType(entityType, options.ModelNamespace, configurationClassNames, memberAccessReplacements, nullable);
+ var generatedCode = GenerateEntityType(
+ entityType, options.ModelNamespace, configurationClassNames, memberAccessReplacements, nullable, options.ForNativeAot);
var entityTypeFileName = UniquifyFileName(configurationClassNames[entityType], options);
- scaffoldedFiles.Add(new(entityTypeFileName, generatedCode));
+ scaffoldedFiles.Add(new ScaffoldedFile(entityTypeFileName, generatedCode));
}
return scaffoldedFiles;
@@ -173,11 +177,7 @@ private string CreateAssemblyAttributes(
bool nullable)
{
var mainBuilder = new IndentedStringBuilder();
- var namespaces = new SortedSet(new NamespaceComparer())
- {
- typeof(DbContextModelAttribute).Namespace!,
- @namespace
- };
+ var namespaces = new SortedSet(new NamespaceComparer()) { typeof(DbContextModelAttribute).Namespace!, @namespace };
AddNamespace(contextType, namespaces);
@@ -188,7 +188,8 @@ private string CreateAssemblyAttributes(
return GenerateHeader(namespaces, currentNamespace: "", nullable) + mainBuilder;
}
- private string GetModelClassName(Type contextType) => _code.Identifier(contextType.ShortDisplayName()) + ModelSuffix;
+ private string GetModelClassName(Type contextType)
+ => _code.Identifier(contextType.ShortDisplayName()) + ModelSuffix;
private string GenerateUnsafeAccessorType(
Type type,
@@ -218,7 +219,7 @@ private string GenerateUnsafeAccessorType(
var genericParameters = type.GetGenericArguments();
mainBuilder
.Append("<")
- .AppendJoin(genericParameters.Select(a => _code.Reference(a)), ", ")
+ .AppendJoin(genericParameters.Select(a => _code.Reference(a)))
.AppendLine(">");
using (mainBuilder.Indent())
@@ -226,7 +227,8 @@ private string GenerateUnsafeAccessorType(
foreach (var genericParameter in genericParameters)
{
if (genericParameter.GetGenericParameterConstraints().Length == 0
- && (genericParameter.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask) == GenericParameterAttributes.None)
+ && (genericParameter.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask)
+ == GenericParameterAttributes.None)
{
continue;
}
@@ -248,12 +250,14 @@ private string GenerateUnsafeAccessorType(
constraintList.Add("class");
}
- if (constraintAttributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint))
+ if (constraintAttributes.HasFlag(GenericParameterAttributes.DefaultConstructorConstraint)
+ && !constraintAttributes.HasFlag(GenericParameterAttributes.NotNullableValueTypeConstraint))
{
constraintList.Add("new()");
}
- Check.DebugAssert(!constraintAttributes.HasFlag(GenericParameterAttributes.VarianceMask),
+ Check.DebugAssert(
+ !constraintAttributes.HasFlag(GenericParameterAttributes.VarianceMask),
"Variance constraints not supported for type: " + type.DisplayName());
}
@@ -262,13 +266,18 @@ private string GenerateUnsafeAccessorType(
{
foreach (var constraint in constraints)
{
+ if (constraint == typeof(ValueType))
+ {
+ continue;
+ }
+
AddNamespace(constraint, namespaces);
constraintList.Add(_code.Reference(constraint));
}
}
mainBuilder
- .AppendJoin(constraintList, ", ")
+ .AppendJoin(constraintList)
.AppendLine();
}
}
@@ -291,7 +300,8 @@ private string GenerateUnsafeAccessorType(
scopeVariables.Inverse,
scopeVariables,
configurationClassNames: [],
- nullable);
+ nullable,
+ nativeAot: true);
mainBuilder
.Append("{");
@@ -347,7 +357,8 @@ private string CreateModel(
.Append("public partial class ").Append(className).AppendLine(" : " + nameof(RuntimeModel))
.AppendLine("{")
.AppendLine(" private static readonly bool _useOldBehavior31751 =")
- .AppendLine(@" System.AppContext.TryGetSwitch(""Microsoft.EntityFrameworkCore.Issue31751"", out var enabled31751) && enabled31751;")
+ .AppendLine(
+ @" System.AppContext.TryGetSwitch(""Microsoft.EntityFrameworkCore.Issue31751"", out var enabled31751) && enabled31751;")
.AppendLine();
using (mainBuilder.Indent())
@@ -407,7 +418,8 @@ private string CreateModelBuilder(
string @namespace,
Type contextType,
Dictionary configurationClassNames,
- bool nullable)
+ bool nullable,
+ bool nativeAot)
{
var mainBuilder = new IndentedStringBuilder();
var methodBuilder = new IndentedStringBuilder();
@@ -581,7 +593,8 @@ private string CreateModelBuilder(
scopeVariables.Inverse,
scopeVariables,
configurationClassNames,
- nullable);
+ nullable,
+ nativeAot);
foreach (var typeConfiguration in model.GetTypeMappingConfigurations())
{
@@ -692,7 +705,8 @@ private string GenerateEntityType(
string @namespace,
Dictionary entityClassNames,
Dictionary memberAccessReplacements,
- bool nullable)
+ bool nullable,
+ bool nativeAot)
{
var mainBuilder = new IndentedStringBuilder();
var methodBuilder = new IndentedStringBuilder();
@@ -717,26 +731,36 @@ private string GenerateEntityType(
.AppendLine("{");
using (mainBuilder.Indent())
{
- CreateEntityType(entityType, @namespace, mainBuilder, methodBuilder, namespaces, entityClassNames, memberAccessReplacements, nullable);
+ CreateEntityType(
+ entityType, @namespace, mainBuilder, methodBuilder, namespaces, entityClassNames, memberAccessReplacements, nullable,
+ nativeAot);
foreach (var complexProperty in entityType.GetDeclaredComplexProperties())
{
- CreateComplexProperty(complexProperty, @namespace, mainBuilder, methodBuilder, namespaces, entityClassNames, memberAccessReplacements, className, nullable);
+ CreateComplexProperty(
+ complexProperty, @namespace, mainBuilder, methodBuilder, namespaces, entityClassNames, memberAccessReplacements,
+ className, nullable, nativeAot);
}
var foreignKeyNumber = 1;
foreach (var foreignKey in entityType.GetDeclaredForeignKeys())
{
- CreateForeignKey(foreignKey, foreignKeyNumber++, @namespace, mainBuilder, methodBuilder, namespaces, entityClassNames, memberAccessReplacements, className, nullable);
+ CreateForeignKey(
+ foreignKey, foreignKeyNumber++, @namespace, mainBuilder, methodBuilder, namespaces, entityClassNames,
+ memberAccessReplacements, className, nullable, nativeAot);
}
var navigationNumber = 1;
foreach (var navigation in entityType.GetDeclaredSkipNavigations())
{
- CreateSkipNavigation(navigation, navigationNumber++, @namespace, mainBuilder, methodBuilder, namespaces, entityClassNames, memberAccessReplacements, className, nullable);
+ CreateSkipNavigation(
+ navigation, navigationNumber++, @namespace, mainBuilder, methodBuilder, namespaces, entityClassNames,
+ memberAccessReplacements, className, nullable, nativeAot);
}
- CreateAnnotations(entityType, @namespace, mainBuilder, methodBuilder, namespaces, entityClassNames, memberAccessReplacements, nullable);
+ CreateAnnotations(
+ entityType, @namespace, mainBuilder, methodBuilder, namespaces, entityClassNames, memberAccessReplacements, nullable,
+ nativeAot);
var methods = methodBuilder.ToString();
if (!string.IsNullOrEmpty(methods))
@@ -764,7 +788,8 @@ private void CreateEntityType(
SortedSet namespaces,
Dictionary configurationClassNames,
Dictionary memberAccessReplacements,
- bool nullable)
+ bool nullable,
+ bool nativeAot)
{
mainBuilder
.Append("public static RuntimeEntityType Create")
@@ -800,7 +825,8 @@ private void CreateEntityType(
scopeVariables.Inverse,
scopeVariables,
configurationClassNames,
- nullable);
+ nullable,
+ nativeAot);
Create(entityType, parameters);
@@ -1155,7 +1181,7 @@ private void Create(
{
AddNamespace(valueComparerType, parameters.Namespaces);
- var valueComparerString = $"new {_code.Reference(valueComparerType)}()" ;
+ var valueComparerString = $"new {_code.Reference(valueComparerType)}()";
if (property.ClrType.IsNullableValueType())
{
var valueComparerElementType = ((ValueComparer)Activator.CreateInstance(valueComparerType)!).Type;
@@ -1209,67 +1235,72 @@ private void Create(
SetPropertyBaseProperties(property, memberAccessReplacements, propertyParameters);
- mainBuilder.Append(variableName).Append(".TypeMapping = ");
- _annotationCodeGenerator.Create(property.GetTypeMapping(), property, propertyParameters);
- mainBuilder.AppendLine(";");
+ var shouldSetConverter = providerClrType == null
+ && valueConverterType == null
+ && converter != null
+ && property[CoreAnnotationNames.ValueConverter] != null
+ && !parameters.ForNativeAot;
+
+ if (parameters.ForNativeAot
+ || (shouldSetConverter && converter!.MappingHints != null))
+ {
+ shouldSetConverter = false;
+ mainBuilder.Append(variableName).Append(".TypeMapping = ");
+ _annotationCodeGenerator.Create(property.GetTypeMapping(), property, propertyParameters);
+ mainBuilder.AppendLine(";");
+ }
+
+ if (parameters.ForNativeAot
+ && (property.IsKey()
+ || property.IsForeignKey()
+ || property.IsUniqueIndex()))
+ {
+ var currentComparerType = CurrentValueComparerFactory.Instance.GetComparerType(property);
+ AddNamespace(currentComparerType, parameters.Namespaces);
+
+ mainBuilder
+ .Append(variableName).Append(".SetCurrentValueComparer(new ")
+ .Append(_code.Reference(currentComparerType))
+ .AppendLine($"({variableName}));");
+ }
+
+ if (shouldSetConverter)
+ {
+ mainBuilder.Append(variableName).Append(".SetValueConverter(");
+ _annotationCodeGenerator.Create(converter!, parameters);
+ mainBuilder.AppendLine(");");
+ }
var valueComparer = property.GetValueComparer();
var typeMappingComparer = property.GetTypeMapping().Comparer;
if (valueComparerType == null
- && valueComparer != typeMappingComparer)
+ && (!parameters.ForNativeAot || valueComparer != typeMappingComparer)
+ && (parameters.ForNativeAot || property[CoreAnnotationNames.ValueComparer] != null))
{
- mainBuilder
- .Append(variableName)
- .Append(".SetValueComparer(");
- CreateValueComparer(valueComparer, typeMappingComparer, nameof(CoreTypeMapping.Comparer), propertyParameters);
-
- mainBuilder
- .AppendLine(");");
+ SetValueComparer(valueComparer, typeMappingComparer, nameof(CoreTypeMapping.Comparer), propertyParameters);
}
var keyValueComparer = property.GetKeyValueComparer();
var typeMappingKeyComparer = property.GetTypeMapping().KeyComparer;
if (valueComparer != keyValueComparer
- && keyValueComparer != typeMappingKeyComparer)
+ && (!parameters.ForNativeAot || keyValueComparer != typeMappingKeyComparer)
+ && (parameters.ForNativeAot || property[CoreAnnotationNames.ValueComparer] != null))
{
- mainBuilder
- .Append(variableName)
- .Append(".SetKeyValueComparer(");
- CreateValueComparer(keyValueComparer, typeMappingKeyComparer, nameof(CoreTypeMapping.KeyComparer), propertyParameters);
-
- mainBuilder
- .AppendLine(");");
+ SetValueComparer(keyValueComparer, typeMappingKeyComparer, nameof(CoreTypeMapping.KeyComparer), propertyParameters);
}
var providerValueComparer = property.GetProviderValueComparer();
var defaultProviderValueComparer = property.ClrType.UnwrapNullableType()
- == (property.GetTypeMapping().Converter?.ProviderClrType ?? property.ClrType).UnwrapNullableType()
- ? property.GetKeyValueComparer()
- : property.GetTypeMapping().ProviderValueComparer;
+ == (property.GetTypeMapping().Converter?.ProviderClrType ?? property.ClrType).UnwrapNullableType()
+ ? property.GetKeyValueComparer()
+ : property.GetTypeMapping().ProviderValueComparer;
if (providerValueComparerType == null
- && providerValueComparer != defaultProviderValueComparer)
- {
- mainBuilder
- .Append(variableName)
- .Append(".SetProviderValueComparer(");
- CreateValueComparer(
- providerValueComparer, property.GetTypeMapping().ProviderValueComparer, nameof(CoreTypeMapping.ProviderValueComparer), propertyParameters);
-
- mainBuilder
- .AppendLine(");");
- }
-
- if (property.IsKey()
- || property.IsForeignKey()
- || property.IsUniqueIndex())
+ && (!parameters.ForNativeAot || providerValueComparer != defaultProviderValueComparer)
+ && (parameters.ForNativeAot || property[CoreAnnotationNames.ProviderValueComparer] != null))
{
- var currentComparerType = CurrentValueComparerFactory.Instance.GetComparerType(property);
- AddNamespace(currentComparerType, parameters.Namespaces);
-
- mainBuilder
- .Append(variableName).Append(".SetCurrentValueComparer(new ")
- .Append(_code.Reference(currentComparerType))
- .AppendLine($"({variableName}));");
+ SetValueComparer(
+ providerValueComparer, property.GetTypeMapping().ProviderValueComparer, nameof(CoreTypeMapping.ProviderValueComparer),
+ propertyParameters);
}
if (sentinel != null
@@ -1288,41 +1319,61 @@ private void Create(
mainBuilder.AppendLine();
}
- private void CreateValueComparer(
+ private void SetValueComparer(
ValueComparer valueComparer,
ValueComparer typeMappingComparer,
string typeMappingComparerProperty,
CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
{
+ var mainBuilder = parameters.MainBuilder;
var valueComparerType = valueComparer.GetType();
if (valueComparer is IInfrastructure { Instance: ValueComparer underlyingValueComparer }
&& typeMappingComparer == underlyingValueComparer
&& valueComparerType.GetDeclaredConstructor([typeof(ValueComparer)]) != null)
{
- AddNamespace(valueComparerType, parameters.Namespaces);
+ if (!parameters.ForNativeAot
+ && valueComparerType.IsGenericType
+ && valueComparerType.GetGenericTypeDefinition() == typeof(NullableValueComparer<>))
+ {
+ return;
+ }
- parameters.MainBuilder
- .Append("new ")
- .Append(_code.Reference(valueComparerType))
- .Append("(")
- .Append(parameters.TargetName)
- .Append(".TypeMapping.")
- .Append(typeMappingComparerProperty)
- .Append(")");
- }
- else
- {
- _annotationCodeGenerator.Create(valueComparer, parameters);
+ if (parameters.ForNativeAot)
+ {
+ AddNamespace(valueComparerType, parameters.Namespaces);
+
+ mainBuilder
+ .Append(parameters.TargetName)
+ .Append(".Set").Append(typeMappingComparerProperty).Append("(")
+ .Append("new ").Append(_code.Reference(valueComparerType)).Append("(")
+ .Append(parameters.TargetName).Append(".TypeMapping.").Append(typeMappingComparerProperty)
+ .AppendLine("));");
+
+ return;
+ }
}
+
+ mainBuilder
+ .Append(parameters.TargetName)
+ .Append(".Set").Append(typeMappingComparerProperty).Append("(");
+
+ _annotationCodeGenerator.Create(valueComparer, parameters);
+
+ mainBuilder
+ .AppendLine(");");
}
private void
SetPropertyBaseProperties(
- IPropertyBase property,
- Dictionary? memberAccessReplacements,
- CSharpRuntimeAnnotationCodeGeneratorParameters parameters,
- bool createPrivateAccessors = true)
+ IPropertyBase property,
+ Dictionary? memberAccessReplacements,
+ CSharpRuntimeAnnotationCodeGeneratorParameters parameters)
{
+ if (!parameters.ForNativeAot)
+ {
+ return;
+ }
+
var variableName = parameters.TargetName;
var mainBuilder = parameters.MainBuilder;
var unsafeAccessors = new HashSet();
@@ -1340,13 +1391,25 @@ private void
mainBuilder
.Append(variableName).AppendLine(".SetGetter(")
.IncrementIndent()
- .AppendLines(_code.Expression(getterExpression, parameters.Namespaces, unsafeAccessors, (IReadOnlyDictionary
public CompiledModelScaffolder(ICompiledModelCodeGeneratorSelector modelCodeGeneratorSelector)
- {
- ModelCodeGeneratorSelector = modelCodeGeneratorSelector;
- }
+ => ModelCodeGeneratorSelector = modelCodeGeneratorSelector;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Design/Scaffolding/Internal/ModelCodeGeneratorSelector.cs b/src/EFCore.Design/Scaffolding/Internal/ModelCodeGeneratorSelector.cs
index 33cb619e2dc..69e0476b7e8 100644
--- a/src/EFCore.Design/Scaffolding/Internal/ModelCodeGeneratorSelector.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/ModelCodeGeneratorSelector.cs
@@ -23,9 +23,7 @@ public class ModelCodeGeneratorSelector : LanguageBasedSelector
public ModelCodeGeneratorSelector(IEnumerable services)
: base(services.Except(services.OfType()).ToList())
- {
- _templatedModelGenerators = services.OfType().ToList();
- }
+ => _templatedModelGenerators = services.OfType().ToList();
///
public virtual IModelCodeGenerator Select(ModelCodeGenerationOptions options)
diff --git a/src/EFCore.Design/Scaffolding/Internal/ScaffoldingTypeMapper.cs b/src/EFCore.Design/Scaffolding/Internal/ScaffoldingTypeMapper.cs
index 7f62b00e9c5..164780f8dd9 100644
--- a/src/EFCore.Design/Scaffolding/Internal/ScaffoldingTypeMapper.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/ScaffoldingTypeMapper.cs
@@ -20,9 +20,7 @@ public class ScaffoldingTypeMapper : IScaffoldingTypeMapper
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public ScaffoldingTypeMapper(IRelationalTypeMappingSource typeMappingSource)
- {
- _typeMappingSource = typeMappingSource;
- }
+ => _typeMappingSource = typeMappingSource;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Design/Scaffolding/Internal/TextTemplatingEngineHost.cs b/src/EFCore.Design/Scaffolding/Internal/TextTemplatingEngineHost.cs
index aa014e1d281..8a78e882446 100644
--- a/src/EFCore.Design/Scaffolding/Internal/TextTemplatingEngineHost.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/TextTemplatingEngineHost.cs
@@ -34,9 +34,7 @@ public class TextTemplatingEngineHost : ITextTemplatingSessionHost, ITextTemplat
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public TextTemplatingEngineHost(IServiceProvider? serviceProvider = null)
- {
- _serviceProvider = serviceProvider;
- }
+ => _serviceProvider = serviceProvider;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Design/Scaffolding/Internal/TextTemplatingModelGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/TextTemplatingModelGenerator.cs
index bd42bfd9600..d6281f2f919 100644
--- a/src/EFCore.Design/Scaffolding/Internal/TextTemplatingModelGenerator.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/TextTemplatingModelGenerator.cs
@@ -179,7 +179,7 @@ public override ScaffoldedModel GenerateModel(IModel model, ModelCodeGenerationO
var entityTypeFileName = entityType.Name + entityTypeExtension;
resultingFiles.AdditionalFiles.Add(
- new ScaffoldedFile(entityTypeFileName, generatedCode));
+ new ScaffoldedFile(entityTypeFileName, generatedCode));
}
}
finally
diff --git a/src/EFCore.Design/Scaffolding/ModelCodeGenerator.cs b/src/EFCore.Design/Scaffolding/ModelCodeGenerator.cs
index 9d1eb72129b..45cf5ae4083 100644
--- a/src/EFCore.Design/Scaffolding/ModelCodeGenerator.cs
+++ b/src/EFCore.Design/Scaffolding/ModelCodeGenerator.cs
@@ -16,9 +16,7 @@ public abstract class ModelCodeGenerator : IModelCodeGenerator
///
/// The dependencies.
protected ModelCodeGenerator(ModelCodeGeneratorDependencies dependencies)
- {
- Dependencies = dependencies;
- }
+ => Dependencies = dependencies;
///
/// Gets the programming language supported by this service.
diff --git a/src/EFCore.InMemory/EFCore.InMemory.csproj b/src/EFCore.InMemory/EFCore.InMemory.csproj
index 9da79937fa6..bf88275bc8f 100644
--- a/src/EFCore.InMemory/EFCore.InMemory.csproj
+++ b/src/EFCore.InMemory/EFCore.InMemory.csproj
@@ -40,7 +40,7 @@
-
+
diff --git a/src/EFCore.InMemory/Infrastructure/InMemoryDbContextOptionsBuilder.cs b/src/EFCore.InMemory/Infrastructure/InMemoryDbContextOptionsBuilder.cs
index 0e8f71dd08d..b5b1e3d208b 100644
--- a/src/EFCore.InMemory/Infrastructure/InMemoryDbContextOptionsBuilder.cs
+++ b/src/EFCore.InMemory/Infrastructure/InMemoryDbContextOptionsBuilder.cs
@@ -28,9 +28,7 @@ public class InMemoryDbContextOptionsBuilder : IInMemoryDbContextOptionsBuilderI
///
/// The options builder.
public InMemoryDbContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder)
- {
- OptionsBuilder = optionsBuilder;
- }
+ => OptionsBuilder = optionsBuilder;
///
/// Clones the configuration in this builder.
diff --git a/src/EFCore.InMemory/Query/Internal/AnonymousObject.cs b/src/EFCore.InMemory/Query/Internal/AnonymousObject.cs
index 329ea44bfad..9fa9bb9a325 100644
--- a/src/EFCore.InMemory/Query/Internal/AnonymousObject.cs
+++ b/src/EFCore.InMemory/Query/Internal/AnonymousObject.cs
@@ -34,9 +34,7 @@ public static readonly ConstructorInfo AnonymousObjectCtor
///
[UsedImplicitly]
public AnonymousObject(object[] values)
- {
- _values = values;
- }
+ => _values = values;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.InMemory/Query/Internal/EntityProjectionExpression.cs b/src/EFCore.InMemory/Query/Internal/EntityProjectionExpression.cs
index 36224942b99..bc0a514aa81 100644
--- a/src/EFCore.InMemory/Query/Internal/EntityProjectionExpression.cs
+++ b/src/EFCore.InMemory/Query/Internal/EntityProjectionExpression.cs
@@ -143,9 +143,7 @@ public virtual void AddNavigationBinding(INavigation navigation, StructuralTypeS
InMemoryStrings.UnableToBindMemberToEntityProjection("navigation", navigation.Name, EntityType.DisplayName()));
}
- return _navigationExpressionsCache.TryGetValue(navigation, out var expression)
- ? expression
- : null;
+ return _navigationExpressionsCache.GetValueOrDefault(navigation);
}
///
diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryQueryContext.cs b/src/EFCore.InMemory/Query/Internal/InMemoryQueryContext.cs
index 9849d15cdf6..aad408a9f4c 100644
--- a/src/EFCore.InMemory/Query/Internal/InMemoryQueryContext.cs
+++ b/src/EFCore.InMemory/Query/Internal/InMemoryQueryContext.cs
@@ -47,9 +47,7 @@ public InMemoryQueryContext(
QueryContextDependencies dependencies,
IInMemoryStore store)
: base(dependencies)
- {
- Store = store;
- }
+ => Store = store;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryQueryTranslationPreprocessorFactory.cs b/src/EFCore.InMemory/Query/Internal/InMemoryQueryTranslationPreprocessorFactory.cs
index ae1f3c595cc..12b62bafc49 100644
--- a/src/EFCore.InMemory/Query/Internal/InMemoryQueryTranslationPreprocessorFactory.cs
+++ b/src/EFCore.InMemory/Query/Internal/InMemoryQueryTranslationPreprocessorFactory.cs
@@ -19,9 +19,7 @@ public class InMemoryQueryTranslationPreprocessorFactory : IQueryTranslationPrep
///
public InMemoryQueryTranslationPreprocessorFactory(
QueryTranslationPreprocessorDependencies dependencies)
- {
- Dependencies = dependencies;
- }
+ => Dependencies = dependencies;
///
/// Dependencies for this service.
diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitorFactory.cs b/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitorFactory.cs
index 4593092b96b..17325d1bc91 100644
--- a/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitorFactory.cs
+++ b/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitorFactory.cs
@@ -19,9 +19,7 @@ public class InMemoryQueryableMethodTranslatingExpressionVisitorFactory : IQuery
///
public InMemoryQueryableMethodTranslatingExpressionVisitorFactory(
QueryableMethodTranslatingExpressionVisitorDependencies dependencies)
- {
- Dependencies = dependencies;
- }
+ => Dependencies = dependencies;
///
/// Dependencies for this service.
diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryExpressionVisitorFactory.cs b/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryExpressionVisitorFactory.cs
index 95bf4f35a07..192cef1760d 100644
--- a/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryExpressionVisitorFactory.cs
+++ b/src/EFCore.InMemory/Query/Internal/InMemoryShapedQueryExpressionVisitorFactory.cs
@@ -19,9 +19,7 @@ public class InMemoryShapedQueryCompilingExpressionVisitorFactory : IShapedQuery
///
public InMemoryShapedQueryCompilingExpressionVisitorFactory(
ShapedQueryCompilingExpressionVisitorDependencies dependencies)
- {
- Dependencies = dependencies;
- }
+ => Dependencies = dependencies;
///
/// Dependencies for this service.
diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryTableExpression.cs b/src/EFCore.InMemory/Query/Internal/InMemoryTableExpression.cs
index 2da22aa50ad..0b4b8cf13b6 100644
--- a/src/EFCore.InMemory/Query/Internal/InMemoryTableExpression.cs
+++ b/src/EFCore.InMemory/Query/Internal/InMemoryTableExpression.cs
@@ -18,9 +18,7 @@ public class InMemoryTableExpression : Expression, IPrintableExpression
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public InMemoryTableExpression(IEntityType entityType)
- {
- EntityType = entityType;
- }
+ => EntityType = entityType;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.InMemory/Storage/Internal/InMemoryDatabaseCreator.cs b/src/EFCore.InMemory/Storage/Internal/InMemoryDatabaseCreator.cs
index 654528b4a6c..69fc4d05515 100644
--- a/src/EFCore.InMemory/Storage/Internal/InMemoryDatabaseCreator.cs
+++ b/src/EFCore.InMemory/Storage/Internal/InMemoryDatabaseCreator.cs
@@ -12,6 +12,8 @@ namespace Microsoft.EntityFrameworkCore.InMemory.Storage.Internal;
public class InMemoryDatabaseCreator : IDatabaseCreator
{
private readonly IDatabase _database;
+ private readonly ICurrentDbContext _currentContext;
+ private readonly IDbContextOptions _contextOptions;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -19,9 +21,14 @@ public class InMemoryDatabaseCreator : IDatabaseCreator
/// 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 InMemoryDatabaseCreator(IDatabase database)
+ public InMemoryDatabaseCreator(
+ IDatabase database,
+ ICurrentDbContext currentContext,
+ IDbContextOptions contextOptions)
{
_database = database;
+ _currentContext = currentContext;
+ _contextOptions = contextOptions;
}
///
@@ -58,7 +65,25 @@ public virtual Task EnsureDeletedAsync(CancellationToken cancellationToken
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual bool EnsureCreated()
- => Database.EnsureDatabaseCreated();
+ {
+ var created = Database.EnsureDatabaseCreated();
+
+ var coreOptionsExtension =
+ _contextOptions.FindExtension()
+ ?? new CoreOptionsExtension();
+
+ var seed = coreOptionsExtension.Seeder;
+ if (seed != null)
+ {
+ seed(_currentContext.Context, created);
+ }
+ else if (coreOptionsExtension.AsyncSeeder != null)
+ {
+ throw new InvalidOperationException(CoreStrings.MissingSeeder);
+ }
+
+ return created;
+ }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -66,8 +91,26 @@ public virtual bool EnsureCreated()
/// 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 EnsureCreatedAsync(CancellationToken cancellationToken = default)
- => Task.FromResult(Database.EnsureDatabaseCreated());
+ public virtual async Task EnsureCreatedAsync(CancellationToken cancellationToken = default)
+ {
+ var created = Database.EnsureDatabaseCreated();
+
+ var coreOptionsExtension =
+ _contextOptions.FindExtension()
+ ?? new CoreOptionsExtension();
+
+ var seedAsync = coreOptionsExtension.AsyncSeeder;
+ if (seedAsync != null)
+ {
+ await seedAsync(_currentContext.Context, created, cancellationToken).ConfigureAwait(false);
+ }
+ else if (coreOptionsExtension.Seeder != null)
+ {
+ throw new InvalidOperationException(CoreStrings.MissingSeeder);
+ }
+
+ return created;
+ }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.InMemory/Storage/Internal/InMemoryStore.cs b/src/EFCore.InMemory/Storage/Internal/InMemoryStore.cs
index 08ca15735f2..b05f0eb85d4 100644
--- a/src/EFCore.InMemory/Storage/Internal/InMemoryStore.cs
+++ b/src/EFCore.InMemory/Storage/Internal/InMemoryStore.cs
@@ -27,9 +27,7 @@ public class InMemoryStore : IInMemoryStore
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public InMemoryStore(IInMemoryTableFactory tableFactory)
- {
- _tableFactory = tableFactory;
- }
+ => _tableFactory = tableFactory;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.InMemory/Storage/Internal/InMemoryTransactionManager.cs b/src/EFCore.InMemory/Storage/Internal/InMemoryTransactionManager.cs
index b09d8dd9499..d07bc94abbb 100644
--- a/src/EFCore.InMemory/Storage/Internal/InMemoryTransactionManager.cs
+++ b/src/EFCore.InMemory/Storage/Internal/InMemoryTransactionManager.cs
@@ -26,9 +26,7 @@ public class InMemoryTransactionManager : IDbContextTransactionManager, ITransac
///
public InMemoryTransactionManager(
IDiagnosticsLogger logger)
- {
- _logger = logger;
- }
+ => _logger = logger;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryIntegerValueGenerator.cs b/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryIntegerValueGenerator.cs
index 46748f7270a..d5c5cbd9a0c 100644
--- a/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryIntegerValueGenerator.cs
+++ b/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryIntegerValueGenerator.cs
@@ -23,9 +23,7 @@ public class InMemoryIntegerValueGenerator : ValueGenerator, IIn
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public InMemoryIntegerValueGenerator(int propertyIndex)
- {
- _propertyIndex = propertyIndex;
- }
+ => _propertyIndex = propertyIndex;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryValueGeneratorSelector.cs b/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryValueGeneratorSelector.cs
index 77483e1ba3b..75cc565b4cb 100644
--- a/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryValueGeneratorSelector.cs
+++ b/src/EFCore.InMemory/ValueGeneration/Internal/InMemoryValueGeneratorSelector.cs
@@ -25,9 +25,7 @@ public InMemoryValueGeneratorSelector(
ValueGeneratorSelectorDependencies dependencies,
IInMemoryDatabase inMemoryDatabase)
: base(dependencies)
- {
- _inMemoryStore = inMemoryDatabase.Store;
- }
+ => _inMemoryStore = inMemoryDatabase.Store;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Proxies/EFCore.Proxies.csproj b/src/EFCore.Proxies/EFCore.Proxies.csproj
index 212bda63217..2e52b366daf 100644
--- a/src/EFCore.Proxies/EFCore.Proxies.csproj
+++ b/src/EFCore.Proxies/EFCore.Proxies.csproj
@@ -39,11 +39,11 @@
-
+
-
+
diff --git a/src/EFCore.Proxies/LazyLoadingProxiesOptionsBuilder.cs b/src/EFCore.Proxies/LazyLoadingProxiesOptionsBuilder.cs
index 4527b8e931b..4ad259e07f9 100644
--- a/src/EFCore.Proxies/LazyLoadingProxiesOptionsBuilder.cs
+++ b/src/EFCore.Proxies/LazyLoadingProxiesOptionsBuilder.cs
@@ -19,9 +19,7 @@ public class LazyLoadingProxiesOptionsBuilder
///
/// The core options builder.
public LazyLoadingProxiesOptionsBuilder(DbContextOptionsBuilder optionsBuilder)
- {
- OptionsBuilder = optionsBuilder;
- }
+ => OptionsBuilder = optionsBuilder;
///
/// Gets the core options builder.
diff --git a/src/EFCore.Proxies/Proxies/Internal/PropertyChangeInterceptorBase.cs b/src/EFCore.Proxies/Proxies/Internal/PropertyChangeInterceptorBase.cs
index 404c7c53d13..393439441f3 100644
--- a/src/EFCore.Proxies/Proxies/Internal/PropertyChangeInterceptorBase.cs
+++ b/src/EFCore.Proxies/Proxies/Internal/PropertyChangeInterceptorBase.cs
@@ -20,9 +20,7 @@ public abstract class PropertyChangeInterceptorBase
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected PropertyChangeInterceptorBase(IEntityType entityType)
- {
- EntityType = entityType;
- }
+ => EntityType = entityType;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs b/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs
index 2637f9e07c8..45308ec2753 100644
--- a/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs
+++ b/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs
@@ -31,9 +31,7 @@ public PropertyChangedInterceptor(
IEntityType entityType,
bool checkEquality)
: base(entityType)
- {
- _checkEquality = checkEquality;
- }
+ => _checkEquality = checkEquality;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs b/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs
index ed1cfe38e51..26ba9d4b04c 100644
--- a/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs
+++ b/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs
@@ -31,9 +31,7 @@ public PropertyChangingInterceptor(
IEntityType entityType,
bool checkEquality)
: base(entityType)
- {
- _checkEquality = checkEquality;
- }
+ => _checkEquality = checkEquality;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Proxies/Proxies/Internal/ProxyBindingInterceptor.cs b/src/EFCore.Proxies/Proxies/Internal/ProxyBindingInterceptor.cs
index ad8acaeceb4..82cbc08870d 100644
--- a/src/EFCore.Proxies/Proxies/Internal/ProxyBindingInterceptor.cs
+++ b/src/EFCore.Proxies/Proxies/Internal/ProxyBindingInterceptor.cs
@@ -26,9 +26,7 @@ private static readonly MethodInfo CreateProxyMethod
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public ProxyBindingInterceptor(IProxyFactory proxyFactory)
- {
- _proxyFactory = proxyFactory;
- }
+ => _proxyFactory = proxyFactory;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Proxies/Proxies/Internal/ProxyChangeTrackingConvention.cs b/src/EFCore.Proxies/Proxies/Internal/ProxyChangeTrackingConvention.cs
index e7cef6fe611..7ba08c57925 100644
--- a/src/EFCore.Proxies/Proxies/Internal/ProxyChangeTrackingConvention.cs
+++ b/src/EFCore.Proxies/Proxies/Internal/ProxyChangeTrackingConvention.cs
@@ -23,9 +23,7 @@ public class ProxyChangeTrackingConvention : IModelInitializedConvention
///
public ProxyChangeTrackingConvention(
ProxiesOptionsExtension? options)
- {
- _options = options;
- }
+ => _options = options;
///
/// Called after a model is finalized.
diff --git a/src/EFCore.Relational/Design/AnnotationCodeGenerator.cs b/src/EFCore.Relational/Design/AnnotationCodeGenerator.cs
index 9f72d0b44c4..189b2ee6609 100644
--- a/src/EFCore.Relational/Design/AnnotationCodeGenerator.cs
+++ b/src/EFCore.Relational/Design/AnnotationCodeGenerator.cs
@@ -45,9 +45,7 @@ public class AnnotationCodeGenerator : IAnnotationCodeGenerator
///
/// Parameter object containing dependencies for this service.
public AnnotationCodeGenerator(AnnotationCodeGeneratorDependencies dependencies)
- {
- Dependencies = dependencies;
- }
+ => Dependencies = dependencies;
///
/// Relational provider-specific dependencies for this service.
@@ -243,6 +241,18 @@ public virtual IReadOnlyList GenerateFluentApiCalls(
#pragma warning restore CS0618
}
+ if (annotations.TryGetValue(RelationalAnnotationNames.ContainerColumnType, out var containerColumnTypeAnnotation)
+ && containerColumnTypeAnnotation is { Value: string containerColumnType }
+ && entityType.IsOwned())
+ {
+ methodCallCodeFragments.Add(
+ new MethodCallCodeFragment(
+ nameof(RelationalOwnedNavigationBuilderExtensions.HasColumnType),
+ containerColumnType));
+
+ annotations.Remove(RelationalAnnotationNames.ContainerColumnType);
+ }
+
methodCallCodeFragments.AddRange(GenerateFluentApiCallsHelper(entityType, annotations, GenerateFluentApi));
return methodCallCodeFragments;
diff --git a/src/EFCore.Relational/Design/AnnotationCodeGeneratorDependencies.cs b/src/EFCore.Relational/Design/AnnotationCodeGeneratorDependencies.cs
index 1c07f78cfdc..d2bed64e354 100644
--- a/src/EFCore.Relational/Design/AnnotationCodeGeneratorDependencies.cs
+++ b/src/EFCore.Relational/Design/AnnotationCodeGeneratorDependencies.cs
@@ -39,9 +39,7 @@ public sealed record AnnotationCodeGeneratorDependencies
[EntityFrameworkInternal]
public AnnotationCodeGeneratorDependencies(
IRelationalTypeMappingSource relationalTypeMappingSource)
- {
- RelationalTypeMappingSource = relationalTypeMappingSource;
- }
+ => RelationalTypeMappingSource = relationalTypeMappingSource;
///
/// The type mapper.
diff --git a/src/EFCore.Relational/Design/Internal/RelationalCSharpRuntimeAnnotationCodeGenerator.cs b/src/EFCore.Relational/Design/Internal/RelationalCSharpRuntimeAnnotationCodeGenerator.cs
index 6379a8b0099..485e89c3968 100644
--- a/src/EFCore.Relational/Design/Internal/RelationalCSharpRuntimeAnnotationCodeGenerator.cs
+++ b/src/EFCore.Relational/Design/Internal/RelationalCSharpRuntimeAnnotationCodeGenerator.cs
@@ -21,9 +21,7 @@ public RelationalCSharpRuntimeAnnotationCodeGenerator(
CSharpRuntimeAnnotationCodeGeneratorDependencies dependencies,
RelationalCSharpRuntimeAnnotationCodeGeneratorDependencies relationalDependencies)
: base(dependencies)
- {
- RelationalDependencies = relationalDependencies;
- }
+ => RelationalDependencies = relationalDependencies;
///
/// Relational provider-specific dependencies for this service.
@@ -40,23 +38,26 @@ public override void Generate(IModel model, CSharpRuntimeAnnotationCodeGenerator
annotations.Remove(RelationalAnnotationNames.RelationalModel);
annotations.Remove(RelationalAnnotationNames.RelationalModelFactory);
- GenerateSimpleAnnotation(RelationalAnnotationNames.RelationalModelFactory, "() => CreateRelationalModel()", parameters);
-
- var methodBuilder = new IndentedStringBuilder();
- var newScope = new BidirectionalDictionary();
- Create(
- model.GetRelationalModel(), parameters with
- {
- MainBuilder = parameters.MethodBuilder,
- MethodBuilder = methodBuilder,
- ScopeObjects = newScope,
- ScopeVariables = newScope.Inverse
- });
-
- var methods = methodBuilder.ToString();
- if (!string.IsNullOrEmpty(methods))
+ if (parameters.ForNativeAot)
{
- parameters.MethodBuilder.AppendLines(methods);
+ GenerateSimpleAnnotation(RelationalAnnotationNames.RelationalModelFactory, "() => CreateRelationalModel()", parameters);
+
+ var methodBuilder = new IndentedStringBuilder();
+ var newScope = new BidirectionalDictionary();
+ Create(
+ model.GetRelationalModel(), parameters with
+ {
+ MainBuilder = parameters.MethodBuilder,
+ MethodBuilder = methodBuilder,
+ ScopeObjects = newScope,
+ ScopeVariables = newScope.Inverse
+ });
+
+ var methods = methodBuilder.ToString();
+ if (!string.IsNullOrEmpty(methods))
+ {
+ parameters.MethodBuilder.AppendLines(methods);
+ }
}
}
else
@@ -69,7 +70,8 @@ public override void Generate(IModel model, CSharpRuntimeAnnotationCodeGenerator
{
parameters.Namespaces.Add(typeof(Dictionary<,>).Namespace!);
parameters.Namespaces.Add(typeof(BindingFlags).Namespace!);
- var functionsVariable = Dependencies.CSharpHelper.Identifier("functions", functions, parameters.ScopeObjects, capitalize: false);
+ var functionsVariable = Dependencies.CSharpHelper.Identifier(
+ "functions", functions, parameters.ScopeObjects, capitalize: false);
parameters.MainBuilder
.Append("var ").Append(functionsVariable).AppendLine(" = new Dictionary();");
@@ -86,7 +88,8 @@ public override void Generate(IModel model, CSharpRuntimeAnnotationCodeGenerator
out IReadOnlyDictionary<(string, string?), ISequence> sequences))
{
parameters.Namespaces.Add(typeof(Dictionary<,>).Namespace!);
- var sequencesVariable = Dependencies.CSharpHelper.Identifier("sequences", sequences, parameters.ScopeObjects, capitalize: false);
+ var sequencesVariable = Dependencies.CSharpHelper.Identifier(
+ "sequences", sequences, parameters.ScopeObjects, capitalize: false);
var mainBuilder = parameters.MainBuilder;
mainBuilder.Append("var ").Append(sequencesVariable).Append(" = new Dictionary<(string, string");
@@ -190,7 +193,8 @@ void CreateMappings(
var defaultMappings = typeBase.GetDefaultMappings();
if (defaultMappings.Any())
{
- var tableMappingsVariable = code.Identifier("defaultTableMappings", defaultMappings, parameters.ScopeObjects, capitalize: false);
+ var tableMappingsVariable = code.Identifier(
+ "defaultTableMappings", defaultMappings, parameters.ScopeObjects, capitalize: false);
mainBuilder
.AppendLine()
.AppendLine($"var {tableMappingsVariable} = new List>();")
@@ -235,7 +239,8 @@ void CreateMappings(
var sqlQueryMappings = typeBase.GetSqlQueryMappings();
if (sqlQueryMappings.Any())
{
- var sqlQueryMappingsVariable = code.Identifier("sqlQueryMappings", sqlQueryMappings, parameters.ScopeObjects, capitalize: false);
+ var sqlQueryMappingsVariable = code.Identifier(
+ "sqlQueryMappings", sqlQueryMappings, parameters.ScopeObjects, capitalize: false);
mainBuilder
.AppendLine()
.AppendLine($"var {sqlQueryMappingsVariable} = new List();")
@@ -250,7 +255,8 @@ void CreateMappings(
var functionMappings = typeBase.GetFunctionMappings();
if (functionMappings.Any())
{
- var functionMappingsVariable = code.Identifier("functionMappings", functionMappings, parameters.ScopeObjects, capitalize: false);
+ var functionMappingsVariable = code.Identifier(
+ "functionMappings", functionMappings, parameters.ScopeObjects, capitalize: false);
mainBuilder
.AppendLine()
.AppendLine($"var {functionMappingsVariable} = new List();")
@@ -258,14 +264,15 @@ void CreateMappings(
.AppendLine($"{code.Literal(RelationalAnnotationNames.FunctionMappings)}, {functionMappingsVariable});");
foreach (var mapping in functionMappings)
{
- Create(mapping, functionMappingsVariable, parameters);
+ Create(mapping, functionMappingsVariable, parameters);
}
}
var deleteStoredProcedureMappings = typeBase.GetDeleteStoredProcedureMappings();
if (deleteStoredProcedureMappings.Any())
{
- var deleteSprocMappingsVariable = code.Identifier("deleteSprocMappings", deleteStoredProcedureMappings, parameters.ScopeObjects, capitalize: false);
+ var deleteSprocMappingsVariable = code.Identifier(
+ "deleteSprocMappings", deleteStoredProcedureMappings, parameters.ScopeObjects, capitalize: false);
mainBuilder
.AppendLine()
.AppendLine($"var {deleteSprocMappingsVariable} = new List();")
@@ -285,7 +292,8 @@ void CreateMappings(
var insertStoredProcedureMappings = typeBase.GetInsertStoredProcedureMappings();
if (typeBase.GetInsertStoredProcedureMappings().Any())
{
- var insertSprocMappingsVariable = code.Identifier("insertSprocMappings", insertStoredProcedureMappings, parameters.ScopeObjects, capitalize: false);
+ var insertSprocMappingsVariable = code.Identifier(
+ "insertSprocMappings", insertStoredProcedureMappings, parameters.ScopeObjects, capitalize: false);
mainBuilder
.AppendLine()
.AppendLine($"var {insertSprocMappingsVariable} = new List();")
@@ -305,7 +313,8 @@ void CreateMappings(
var updateStoredProcedureMappings = typeBase.GetUpdateStoredProcedureMappings();
if (updateStoredProcedureMappings.Any())
{
- var updateSprocMappingsVariable = code.Identifier("updateSprocMappings", updateStoredProcedureMappings, parameters.ScopeObjects, capitalize: false);
+ var updateSprocMappingsVariable = code.Identifier(
+ "updateSprocMappings", updateStoredProcedureMappings, parameters.ScopeObjects, capitalize: false);
mainBuilder
.AppendLine()
.AppendLine($"var {updateSprocMappingsVariable} = new List();")
@@ -532,7 +541,8 @@ private string GetOrCreate(
foreach (var parameter in function.Parameters)
{
- var parameterVariable = code.Identifier(parameter.Name + "FunctionParameter", parameter, parameters.ScopeObjects, capitalize: false);
+ var parameterVariable = code.Identifier(
+ parameter.Name + "FunctionParameter", parameter, parameters.ScopeObjects, capitalize: false);
mainBuilder.AppendLine($"var {parameterVariable} = {functionVariable}.FindParameter({code.Literal(parameter.Name)})!;");
CreateAnnotations(
@@ -598,7 +608,8 @@ private string GetOrCreate(
}
var code = Dependencies.CSharpHelper;
- storedProcedureVariable = code.Identifier(storeStoredProcedure.Name + "StoreSproc", storeStoredProcedure, parameters.ScopeObjects, capitalize: false);
+ storedProcedureVariable = code.Identifier(
+ storeStoredProcedure.Name + "StoreSproc", storeStoredProcedure, parameters.ScopeObjects, capitalize: false);
var mainBuilder = parameters.MainBuilder;
mainBuilder
.Append($"var {storedProcedureVariable} = new StoreStoredProcedure(")
@@ -943,6 +954,7 @@ private void Create(
mainBuilder
.Append($"new CompositeRowKeyValueFactory({uniqueConstraintVariable})");
}
+
mainBuilder.AppendLine(");");
CreateAnnotations(
@@ -1009,6 +1021,7 @@ private void Create(
mainBuilder
.Append($"new CompositeRowIndexValueFactory({indexVariable})");
}
+
mainBuilder.AppendLine(");");
CreateAnnotations(
@@ -1094,7 +1107,8 @@ private void Create(
{
if (!parameters.ScopeVariables.TryGetValue(mappedForeignKey, out var foreignKeyVariable))
{
- foreignKeyVariable = code.Identifier(foreignKeyConstraintVariable + "Fk", mappedForeignKey, parameters.ScopeObjects, capitalize: false);
+ foreignKeyVariable = code.Identifier(
+ foreignKeyConstraintVariable + "Fk", mappedForeignKey, parameters.ScopeObjects, capitalize: false);
mainBuilder
.AppendLine($"var {foreignKeyVariable} = RelationalModel.GetForeignKey(this,").IncrementIndent()
@@ -1303,7 +1317,8 @@ private void Create(
var sqlQuery = sqlQueryMapping.SqlQuery;
var sqlQueryVariable = GetOrCreate(sqlQuery, parameters);
- var sqlQueryMappingVariable = code.Identifier(sqlQuery.Name + "SqlQueryMapping", sqlQueryMapping, parameters.ScopeObjects, capitalize: false);
+ var sqlQueryMappingVariable = code.Identifier(
+ sqlQuery.Name + "SqlQueryMapping", sqlQueryMapping, parameters.ScopeObjects, capitalize: false);
GenerateAddMapping(
sqlQueryMapping,
@@ -1356,7 +1371,8 @@ private void Create(
var storeFunction = functionMapping.StoreFunction;
var functionVariable = GetOrCreate(storeFunction, parameters);
var dbFunctionVariable = metadataVariables[functionMapping.DbFunction];
- var functionMappingVariable = code.Identifier(storeFunction.Name + "FunctionMapping", functionMapping, parameters.ScopeObjects, capitalize: false);
+ var functionMappingVariable = code.Identifier(
+ storeFunction.Name + "FunctionMapping", functionMapping, parameters.ScopeObjects, capitalize: false);
GenerateAddMapping(
functionMapping,
@@ -1422,11 +1438,13 @@ private void Create(
if (!metadataVariables.TryGetValue(sprocMapping.StoredProcedure, out var sprocVariable))
{
var sprocSnippet = CreateFindSnippet(sprocMapping.StoredProcedure, metadataVariables);
- sprocVariable = code.Identifier(storeSproc.Name + sprocMappingType[0] + "Sproc", sprocMapping.StoredProcedure, parameters.ScopeObjects, capitalize: false);
+ sprocVariable = code.Identifier(
+ storeSproc.Name + sprocMappingType[0] + "Sproc", sprocMapping.StoredProcedure, parameters.ScopeObjects, capitalize: false);
mainBuilder.AppendLine($"var {sprocVariable} = {sprocSnippet};");
}
- var sprocMappingVariable = code.Identifier(storeSproc.Name + "SprocMapping", sprocMapping, parameters.ScopeObjects, capitalize: false);
+ var sprocMappingVariable = code.Identifier(
+ storeSproc.Name + "SprocMapping", sprocMapping, parameters.ScopeObjects, capitalize: false);
var tableMappingVariable = sprocMapping.TableMapping != null ? metadataVariables[sprocMapping.TableMapping] : null;
GenerateAddMapping(
@@ -1626,7 +1644,7 @@ private void Create(
Create(parameter, parameters);
}
- if (function.TypeMapping != null)
+ if (function.TypeMapping != null && parameters.ForNativeAot)
{
mainBuilder.Append(functionVariable).Append(".TypeMapping = ");
Create(function.TypeMapping, parameters with { TargetName = functionVariable });
@@ -1667,9 +1685,12 @@ private void Create(IDbFunctionParameter parameter, CSharpRuntimeAnnotationCodeG
.Append(code.Literal(parameter.PropagatesNullability)).AppendLine(",")
.Append(code.Literal(parameter.StoreType)).AppendLine(");").DecrementIndent();
- mainBuilder.Append(parameterVariable).Append(".TypeMapping = ");
- Create(parameter.TypeMapping!, parameters with { TargetName = parameterVariable });
- mainBuilder.AppendLine(";");
+ if (parameters.ForNativeAot)
+ {
+ mainBuilder.Append(parameterVariable).Append(".TypeMapping = ");
+ Create(parameter.TypeMapping!, parameters with { TargetName = parameterVariable });
+ mainBuilder.AppendLine(";");
+ }
CreateAnnotations(
parameter,
@@ -1798,7 +1819,8 @@ public override void Generate(IEntityType entityType, CSharpRuntimeAnnotationCod
{
AddNamespace(typeof(StoreObjectDictionary), parameters.Namespaces);
AddNamespace(typeof(StoreObjectIdentifier), parameters.Namespaces);
- var fragmentsVariable = Dependencies.CSharpHelper.Identifier("fragments", fragments, parameters.ScopeObjects, capitalize: false);
+ var fragmentsVariable = Dependencies.CSharpHelper.Identifier(
+ "fragments", fragments, parameters.ScopeObjects, capitalize: false);
parameters.MainBuilder
.Append("var ").Append(fragmentsVariable)
.AppendLine(" = new StoreObjectDictionary();");
@@ -1815,7 +1837,8 @@ public override void Generate(IEntityType entityType, CSharpRuntimeAnnotationCod
RelationalAnnotationNames.InsertStoredProcedure,
out StoredProcedure insertStoredProcedure))
{
- var sprocVariable = Dependencies.CSharpHelper.Identifier("insertSproc", insertStoredProcedure, parameters.ScopeObjects, capitalize: false);
+ var sprocVariable = Dependencies.CSharpHelper.Identifier(
+ "insertSproc", insertStoredProcedure, parameters.ScopeObjects, capitalize: false);
Create(insertStoredProcedure, sprocVariable, parameters);
@@ -1827,7 +1850,8 @@ public override void Generate(IEntityType entityType, CSharpRuntimeAnnotationCod
RelationalAnnotationNames.DeleteStoredProcedure,
out StoredProcedure deleteStoredProcedure))
{
- var sprocVariable = Dependencies.CSharpHelper.Identifier("deleteSproc", deleteStoredProcedure, parameters.ScopeObjects, capitalize: false);
+ var sprocVariable = Dependencies.CSharpHelper.Identifier(
+ "deleteSproc", deleteStoredProcedure, parameters.ScopeObjects, capitalize: false);
Create(deleteStoredProcedure, sprocVariable, parameters);
@@ -1839,7 +1863,8 @@ public override void Generate(IEntityType entityType, CSharpRuntimeAnnotationCod
RelationalAnnotationNames.UpdateStoredProcedure,
out StoredProcedure updateStoredProcedure))
{
- var sprocVariable = Dependencies.CSharpHelper.Identifier("updateSproc", updateStoredProcedure, parameters.ScopeObjects, capitalize: false);
+ var sprocVariable = Dependencies.CSharpHelper.Identifier(
+ "updateSproc", updateStoredProcedure, parameters.ScopeObjects, capitalize: false);
Create(updateStoredProcedure, sprocVariable, parameters);
@@ -1964,7 +1989,8 @@ private void Create(IStoredProcedureParameter parameter, CSharpRuntimeAnnotation
{
var code = Dependencies.CSharpHelper;
var mainBuilder = parameters.MainBuilder;
- var parameterVariable = code.Identifier(parameter.PropertyName ?? parameter.Name, parameter, parameters.ScopeObjects, capitalize: false);
+ var parameterVariable = code.Identifier(
+ parameter.PropertyName ?? parameter.Name, parameter, parameters.ScopeObjects, capitalize: false);
mainBuilder
.Append("var ").Append(parameterVariable).Append(" = ")
@@ -2055,7 +2081,8 @@ public override void Generate(IProperty property, CSharpRuntimeAnnotationCodeGen
{
AddNamespace(typeof(StoreObjectDictionary), parameters.Namespaces);
AddNamespace(typeof(StoreObjectIdentifier), parameters.Namespaces);
- var overridesVariable = Dependencies.CSharpHelper.Identifier("overrides", tableOverrides, parameters.ScopeObjects, capitalize: false);
+ var overridesVariable = Dependencies.CSharpHelper.Identifier(
+ "overrides", tableOverrides, parameters.ScopeObjects, capitalize: false);
parameters.MainBuilder.AppendLine()
.Append("var ").Append(overridesVariable)
.AppendLine(" = new StoreObjectDictionary();");
diff --git a/src/EFCore.Relational/Diagnostics/CommandEndEventData.cs b/src/EFCore.Relational/Diagnostics/CommandEndEventData.cs
index e28e57d0bf4..52ab9d5c36c 100644
--- a/src/EFCore.Relational/Diagnostics/CommandEndEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/CommandEndEventData.cs
@@ -55,9 +55,7 @@ public CommandEndEventData(
logParameterValues,
startTime,
commandSource)
- {
- Duration = duration;
- }
+ => Duration = duration;
///
/// The duration this event.
diff --git a/src/EFCore.Relational/Diagnostics/CommandErrorEventData.cs b/src/EFCore.Relational/Diagnostics/CommandErrorEventData.cs
index 09eda7aa5f5..9e9f6007b18 100644
--- a/src/EFCore.Relational/Diagnostics/CommandErrorEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/CommandErrorEventData.cs
@@ -57,9 +57,7 @@ public CommandErrorEventData(
startTime,
duration,
commandSource)
- {
- Exception = exception;
- }
+ => Exception = exception;
///
/// The exception that was thrown when execution failed.
diff --git a/src/EFCore.Relational/Diagnostics/CommandExecutedEventData.cs b/src/EFCore.Relational/Diagnostics/CommandExecutedEventData.cs
index 9be04d83a03..78fb02b4e5c 100644
--- a/src/EFCore.Relational/Diagnostics/CommandExecutedEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/CommandExecutedEventData.cs
@@ -57,9 +57,7 @@ public CommandExecutedEventData(
startTime,
duration,
commandSource)
- {
- Result = result;
- }
+ => Result = result;
///
/// The result of executing the command.
diff --git a/src/EFCore.Relational/Diagnostics/ConnectionEndEventData.cs b/src/EFCore.Relational/Diagnostics/ConnectionEndEventData.cs
index 28e59fb9e8f..12bb94caea6 100644
--- a/src/EFCore.Relational/Diagnostics/ConnectionEndEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/ConnectionEndEventData.cs
@@ -33,9 +33,7 @@ public ConnectionEndEventData(
DateTimeOffset startTime,
TimeSpan duration)
: base(eventDefinition, messageGenerator, connection, context, connectionId, async, startTime)
- {
- Duration = duration;
- }
+ => Duration = duration;
///
/// The duration this event.
diff --git a/src/EFCore.Relational/Diagnostics/ConnectionErrorEventData.cs b/src/EFCore.Relational/Diagnostics/ConnectionErrorEventData.cs
index ea2940ec594..af42f0a5c2d 100644
--- a/src/EFCore.Relational/Diagnostics/ConnectionErrorEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/ConnectionErrorEventData.cs
@@ -34,9 +34,7 @@ public ConnectionErrorEventData(
DateTimeOffset startTime,
TimeSpan duration)
: base(eventDefinition, messageGenerator, connection, context, connectionId, async, startTime, duration)
- {
- Exception = exception;
- }
+ => Exception = exception;
///
/// The exception that was thrown when the connection failed.
diff --git a/src/EFCore.Relational/Diagnostics/DataReaderClosingEventData.cs b/src/EFCore.Relational/Diagnostics/DataReaderClosingEventData.cs
index fee023e23bf..db4d6a1bec0 100644
--- a/src/EFCore.Relational/Diagnostics/DataReaderClosingEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/DataReaderClosingEventData.cs
@@ -39,9 +39,7 @@ public DataReaderClosingEventData(
DateTimeOffset startTime)
: base(
eventDefinition, messageGenerator, command, dataReader, context, commandId, connectionId, recordsAffected, readCount, startTime)
- {
- IsAsync = async;
- }
+ => IsAsync = async;
///
/// Indicates whether or not the operation is being executed asynchronously.
diff --git a/src/EFCore.Relational/Diagnostics/DataReaderDisposingEventData.cs b/src/EFCore.Relational/Diagnostics/DataReaderDisposingEventData.cs
index c61a67c3f95..1cf566d3b69 100644
--- a/src/EFCore.Relational/Diagnostics/DataReaderDisposingEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/DataReaderDisposingEventData.cs
@@ -42,9 +42,7 @@ public DataReaderDisposingEventData(
TimeSpan duration)
: base(
eventDefinition, messageGenerator, command, dataReader, context, commandId, connectionId, recordsAffected, readCount, startTime)
- {
- Duration = duration;
- }
+ => Duration = duration;
///
/// The duration from the time the data reader is created until it is disposed. This corresponds to the time reading
diff --git a/src/EFCore.Relational/Diagnostics/MigrationAssemblyEventData.cs b/src/EFCore.Relational/Diagnostics/MigrationAssemblyEventData.cs
index 8025edc237b..8b4ec39bb2e 100644
--- a/src/EFCore.Relational/Diagnostics/MigrationAssemblyEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/MigrationAssemblyEventData.cs
@@ -25,9 +25,7 @@ public MigrationAssemblyEventData(
IMigrator migrator,
IMigrationsAssembly migrationsAssembly)
: base(eventDefinition, messageGenerator, migrator)
- {
- MigrationsAssembly = migrationsAssembly;
- }
+ => MigrationsAssembly = migrationsAssembly;
///
/// The in use.
diff --git a/src/EFCore.Relational/Diagnostics/MigrationColumnOperationEventData.cs b/src/EFCore.Relational/Diagnostics/MigrationColumnOperationEventData.cs
index fd9fbde0d27..49ad7266901 100644
--- a/src/EFCore.Relational/Diagnostics/MigrationColumnOperationEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/MigrationColumnOperationEventData.cs
@@ -19,9 +19,7 @@ public MigrationColumnOperationEventData(
Func messageGenerator,
ColumnOperation columnOperation)
: base(eventDefinition, messageGenerator)
- {
- ColumnOperation = columnOperation;
- }
+ => ColumnOperation = columnOperation;
///
/// Gets the column operation.
diff --git a/src/EFCore.Relational/Diagnostics/MigrationEventData.cs b/src/EFCore.Relational/Diagnostics/MigrationEventData.cs
index f25799ae924..ffa6e4a71ed 100644
--- a/src/EFCore.Relational/Diagnostics/MigrationEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/MigrationEventData.cs
@@ -29,9 +29,7 @@ public MigrationEventData(
IMigrator migrator,
Migration migration)
: base(eventDefinition, messageGenerator, migrator)
- {
- Migration = migration;
- }
+ => Migration = migration;
///
/// The being processed.
diff --git a/src/EFCore.Relational/Diagnostics/MigrationTypeEventData.cs b/src/EFCore.Relational/Diagnostics/MigrationTypeEventData.cs
index 6466b0d4ca4..071e1084ea0 100644
--- a/src/EFCore.Relational/Diagnostics/MigrationTypeEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/MigrationTypeEventData.cs
@@ -23,9 +23,7 @@ public MigrationTypeEventData(
Func messageGenerator,
TypeInfo migrationType)
: base(eventDefinition, messageGenerator)
- {
- MigrationType = migrationType;
- }
+ => MigrationType = migrationType;
///
/// The migration type.
diff --git a/src/EFCore.Relational/Diagnostics/MigratorEventData.cs b/src/EFCore.Relational/Diagnostics/MigratorEventData.cs
index 02ca69e1906..cc0315bc28b 100644
--- a/src/EFCore.Relational/Diagnostics/MigratorEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/MigratorEventData.cs
@@ -25,9 +25,7 @@ public MigratorEventData(
Func messageGenerator,
IMigrator migrator)
: base(eventDefinition, messageGenerator)
- {
- Migrator = migrator;
- }
+ => Migrator = migrator;
///
/// The in use.
diff --git a/src/EFCore.Relational/Diagnostics/MinBatchSizeEventData.cs b/src/EFCore.Relational/Diagnostics/MinBatchSizeEventData.cs
index e14cb0891e6..4d10b7d816a 100644
--- a/src/EFCore.Relational/Diagnostics/MinBatchSizeEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/MinBatchSizeEventData.cs
@@ -27,9 +27,7 @@ public MinBatchSizeEventData(
int commandCount,
int minBatchSize)
: base(eventDefinition, messageGenerator, entries, commandCount)
- {
- MinBatchSize = minBatchSize;
- }
+ => MinBatchSize = minBatchSize;
///
/// The minimum batch size.
diff --git a/src/EFCore.Relational/Diagnostics/RelationalEventId.cs b/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
index f487c54ff62..c05c73c46a3 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
@@ -79,6 +79,8 @@ private enum Id
ColumnOrderIgnoredWarning,
PendingModelChangesWarning,
NonTransactionalMigrationOperationWarning,
+ AcquiringMigrationLock,
+ MigrationsUserTransactionWarning,
// Query events
QueryClientEvaluationWarning = CoreEventId.RelationalBaseId + 500,
@@ -747,7 +749,34 @@ private static EventId MakeMigrationsId(Id id)
/// This event uses the payload when used with a .
///
///
- public static readonly EventId NonTransactionalMigrationOperationWarning = MakeMigrationsId(Id.NonTransactionalMigrationOperationWarning);
+ public static readonly EventId NonTransactionalMigrationOperationWarning =
+ MakeMigrationsId(Id.NonTransactionalMigrationOperationWarning);
+
+ ///
+ /// A migration lock is being acquired.
+ ///
+ ///
+ ///
+ /// This event is in the category.
+ ///
+ ///
+ /// This event uses the payload when used with a .
+ ///
+ ///
+ public static readonly EventId AcquiringMigrationLock = MakeMigrationsId(Id.AcquiringMigrationLock);
+
+ ///
+ /// A migration lock is being acquired.
+ ///
+ ///
+ ///
+ /// This event is in the category.
+ ///
+ ///
+ /// This event uses the payload when used with a .
+ ///
+ ///
+ public static readonly EventId MigrationsUserTransactionWarning = MakeMigrationsId(Id.MigrationsUserTransactionWarning);
private static readonly string _queryPrefix = DbLoggerCategory.Query.Name + ".";
diff --git a/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs b/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
index d704bcf1f1b..177b7e90bba 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
@@ -2365,6 +2365,7 @@ public static void NonTransactionalMigrationOperationWarning(
{
commandText = commandText.Substring(0, 100) + "...";
}
+
definition.Log(diagnostics, commandText, migration.GetType().ShortDisplayName());
}
@@ -2390,9 +2391,70 @@ private static string NonTransactionalMigrationOperationWarning(EventDefinitionB
{
commandText = commandText.Substring(0, 100) + "...";
}
+
return d.GenerateMessage(commandText, p.Migration.GetType().ShortDisplayName());
}
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ public static void AcquiringMigrationLock(
+ this IDiagnosticsLogger diagnostics)
+ {
+ var definition = RelationalResources.LogAcquiringMigrationLock(diagnostics);
+
+ if (diagnostics.ShouldLog(definition))
+ {
+ definition.Log(diagnostics);
+ }
+
+ if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = new EventData(
+ definition,
+ AcquiringMigrationLock);
+
+ diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
+ }
+ }
+
+ private static string AcquiringMigrationLock(EventDefinitionBase definition, EventData payload)
+ {
+ var d = (EventDefinition)definition;
+ return d.GenerateMessage();
+ }
+
+ ///
+ /// Logs for the event.
+ ///
+ /// The diagnostics logger to use.
+ public static void MigrationsUserTransactionWarning(
+ this IDiagnosticsLogger diagnostics)
+ {
+ var definition = RelationalResources.LogMigrationsUserTransaction(diagnostics);
+
+ if (diagnostics.ShouldLog(definition))
+ {
+ definition.Log(diagnostics);
+ }
+
+ if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = new EventData(
+ definition,
+ MigrationsUserTransactionWarning);
+
+ diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
+ }
+ }
+
+ private static string MigrationsUserTransactionWarning(EventDefinitionBase definition, EventData payload)
+ {
+ var d = (EventDefinition)definition;
+ return d.GenerateMessage();
+ }
+
///
/// Logs for the event.
///
diff --git a/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs b/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
index 51e0e3b1aee..762172c8944 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
@@ -358,6 +358,24 @@ public abstract class RelationalLoggingDefinitions : LoggingDefinitions
[EntityFrameworkInternal]
public EventDefinitionBase? LogNoMigrationsFound;
+ ///
+ /// 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? LogAcquiringMigrationLock;
+
+ ///
+ /// 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? LogMigrationsUserTransactionWarning;
+
///
/// 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/Diagnostics/SequenceEventData.cs b/src/EFCore.Relational/Diagnostics/SequenceEventData.cs
index b1b4a02bf96..8d7f263813c 100644
--- a/src/EFCore.Relational/Diagnostics/SequenceEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/SequenceEventData.cs
@@ -23,9 +23,7 @@ public SequenceEventData(
Func messageGenerator,
IReadOnlySequence sequence)
: base(eventDefinition, messageGenerator)
- {
- Sequence = sequence;
- }
+ => Sequence = sequence;
///
/// The sequence.
diff --git a/src/EFCore.Relational/Diagnostics/TransactionEndEventData.cs b/src/EFCore.Relational/Diagnostics/TransactionEndEventData.cs
index 8e60c400b52..9e2f3e5c4dc 100644
--- a/src/EFCore.Relational/Diagnostics/TransactionEndEventData.cs
+++ b/src/EFCore.Relational/Diagnostics/TransactionEndEventData.cs
@@ -35,9 +35,7 @@ public TransactionEndEventData(
DateTimeOffset startTime,
TimeSpan duration)
: base(eventDefinition, messageGenerator, transaction, context, transactionId, connectionId, async, startTime)
- {
- Duration = duration;
- }
+ => Duration = duration;
///
/// The duration of this event.
diff --git a/src/EFCore.Relational/EFCore.Relational.csproj b/src/EFCore.Relational/EFCore.Relational.csproj
index bac04d23a8f..2f0010093e7 100644
--- a/src/EFCore.Relational/EFCore.Relational.csproj
+++ b/src/EFCore.Relational/EFCore.Relational.csproj
@@ -45,12 +45,12 @@
-
+
-
+
diff --git a/src/EFCore.Relational/Extensions/Internal/RelationalModelExtensions.cs b/src/EFCore.Relational/Extensions/Internal/RelationalModelExtensions.cs
index 1c8d7ac540f..c43995b0023 100644
--- a/src/EFCore.Relational/Extensions/Internal/RelationalModelExtensions.cs
+++ b/src/EFCore.Relational/Extensions/Internal/RelationalModelExtensions.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// ReSharper disable once CheckNamespace
+
namespace Microsoft.EntityFrameworkCore.Internal;
///
@@ -18,5 +19,6 @@ public static class RelationalModelExtensions
/// 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 void EnsureRelationalModel(this IModel model) => model.GetRelationalModel();
+ public static void EnsureRelationalModel(this IModel model)
+ => model.GetRelationalModel();
}
diff --git a/src/EFCore.Relational/Extensions/RelationalDatabaseFacadeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalDatabaseFacadeExtensions.cs
index e355e7a7c23..5d8e3bd558f 100644
--- a/src/EFCore.Relational/Extensions/RelationalDatabaseFacadeExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalDatabaseFacadeExtensions.cs
@@ -123,13 +123,6 @@ public static void Migrate(this DatabaseFacade databaseFacade)
///
/// The target migration to migrate the database to, or to migrate to the latest.
///
- ///
- /// The optional seed method to run after migrating the database. It will be invoked even if no migrations were applied.
- ///
- ///
- /// The maximum amount of time that the migration lock should be held. Unless a catastrophic failure occurs, the
- /// lock is released when the migration operation completes.
- ///
///
///
/// Note that this API is mutually exclusive with . EnsureCreated does not use migrations
@@ -145,10 +138,8 @@ public static void Migrate(this DatabaseFacade databaseFacade)
+ " Use a migration bundle or an alternate way of executing migration operations.")]
public static void Migrate(
this DatabaseFacade databaseFacade,
- Action? seed,
- string? targetMigration = null,
- TimeSpan? lockTimeout = null)
- => databaseFacade.GetRelationalService().Migrate(seed, targetMigration, lockTimeout);
+ string? targetMigration)
+ => databaseFacade.GetRelationalService().Migrate(targetMigration);
///
/// Asynchronously applies any pending migrations for the context to the database. Will create the database
@@ -184,13 +175,6 @@ public static Task MigrateAsync(
///
/// The target migration to migrate the database to, or to migrate to the latest.
///
- ///
- /// The optional seed method to run after migrating the database. It will be invoked even if no migrations were applied.
- ///
- ///
- /// The maximum amount of time that the migration lock should be held. Unless a catastrophic failure occurs, the
- /// lock is released when the migration operation completes.
- ///
/// A to observe while waiting for the task to complete.
///
///
@@ -209,11 +193,9 @@ public static Task MigrateAsync(
+ " Use a migration bundle or an alternate way of executing migration operations.")]
public static Task MigrateAsync(
this DatabaseFacade databaseFacade,
- Func? seed,
- string? targetMigration = null,
- TimeSpan? lockTimeout = null,
+ string? targetMigration,
CancellationToken cancellationToken = default)
- => databaseFacade.GetRelationalService().MigrateAsync(seed, targetMigration, lockTimeout, cancellationToken);
+ => databaseFacade.GetRelationalService().MigrateAsync(targetMigration, cancellationToken);
///
/// Executes the given SQL against the database and returns the number of rows affected.
diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
index ff816843742..8cee65a5187 100644
--- a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
@@ -1606,9 +1606,50 @@ public static void SetContainerColumnName(this IMutableEntityType entityType, st
/// The entity type to get the container column name for.
/// The container column name to which the entity type is mapped.
public static string? GetContainerColumnName(this IReadOnlyEntityType entityType)
- => entityType.FindAnnotation(RelationalAnnotationNames.ContainerColumnName)?.Value is string columnName
- ? columnName
- : (entityType.FindOwnership()?.PrincipalEntityType.GetContainerColumnName());
+ {
+ var containerColumnName = entityType.FindAnnotation(RelationalAnnotationNames.ContainerColumnName);
+ return containerColumnName == null
+ ? entityType.FindOwnership()?.PrincipalEntityType.GetContainerColumnName()
+ : (string?)containerColumnName.Value;
+ }
+
+ ///
+ /// Sets the column type to use for the container column to which the entity type is mapped.
+ ///
+ /// The entity type.
+ /// The database column type.
+ public static void SetContainerColumnType(this IMutableEntityType entityType, string? columnType)
+ => entityType.SetOrRemoveAnnotation(RelationalAnnotationNames.ContainerColumnType, columnType);
+
+ ///
+ /// Sets the column type to use for the container column to which the entity type is mapped.
+ ///
+ /// The entity type.
+ /// The database column type.
+ /// Indicates whether the configuration was specified using a data annotation.
+ /// The configured value.
+ public static string? SetContainerColumnType(
+ this IConventionEntityType entityType,
+ string? columnType,
+ bool fromDataAnnotation = false)
+ => (string?)entityType.SetAnnotation(RelationalAnnotationNames.ContainerColumnType, columnType, fromDataAnnotation)?.Value;
+
+ ///
+ /// Gets the for the container column type.
+ ///
+ /// The entity type.
+ /// The .
+ public static ConfigurationSource? GetContainerColumnTypeConfigurationSource(this IConventionEntityType entityType)
+ => entityType.FindAnnotation(RelationalAnnotationNames.ContainerColumnType)
+ ?.GetConfigurationSource();
+
+ ///
+ /// Gets the column type to use for the container column to which the entity type is mapped.
+ ///
+ /// The entity type.
+ /// The database column type.
+ public static string? GetContainerColumnType(this IReadOnlyEntityType entityType)
+ => entityType.FindAnnotation(RelationalAnnotationNames.ContainerColumnType)?.Value as string;
///
/// Sets the type mapping for the container column to which the entity type is mapped.
@@ -1683,8 +1724,14 @@ public static void SetContainerColumnTypeMapping(this IMutableEntityType entityT
/// is returned for entities that are not mapped to a JSON column.
///
public static string? GetJsonPropertyName(this IReadOnlyEntityType entityType)
- => (string?)entityType.FindAnnotation(RelationalAnnotationNames.JsonPropertyName)?.Value
- ?? (!entityType.IsMappedToJson() ? null : entityType.FindOwnership()!.GetNavigation(pointsToPrincipal: false)!.Name);
+ {
+ var propertyName = entityType.FindAnnotation(RelationalAnnotationNames.JsonPropertyName);
+ return propertyName == null
+ ? (entityType.IsMappedToJson()
+ ? entityType.FindOwnership()!.GetNavigation(pointsToPrincipal: false)!.Name
+ : null)
+ : (string?)propertyName.Value;
+ }
///
/// Sets the value of JSON property name used for the given entity mapped to a JSON column.
diff --git a/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs
index f4dec1f5808..f184bc57728 100644
--- a/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
using System.Text;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
@@ -139,8 +138,8 @@ public static IEnumerable GetMappedConstraints(this IFore
{
foreignKey.DeclaringEntityType.Model.EnsureRelationalModel();
return (IEnumerable?)foreignKey.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.ForeignKeyMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.ForeignKeyMappings)
+ ?? Enumerable.Empty();
}
///
diff --git a/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs b/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs
index e7e318635d3..3ffcceb1ed4 100644
--- a/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs
@@ -3,7 +3,6 @@
using System.Text;
using Microsoft.EntityFrameworkCore.Internal;
-using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
// ReSharper disable once CheckNamespace
@@ -170,8 +169,8 @@ public static IEnumerable GetMappedTableIndexes(this IIndex index)
{
index.DeclaringEntityType.Model.EnsureRelationalModel();
return (IEnumerable?)index.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.TableIndexMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.TableIndexMappings)
+ ?? Enumerable.Empty();
}
///
diff --git a/src/EFCore.Relational/Extensions/RelationalKeyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalKeyExtensions.cs
index 406dfa63553..74a44e2738b 100644
--- a/src/EFCore.Relational/Extensions/RelationalKeyExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalKeyExtensions.cs
@@ -3,7 +3,6 @@
using System.Text;
using Microsoft.EntityFrameworkCore.Internal;
-using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
// ReSharper disable once CheckNamespace
@@ -111,8 +110,8 @@ public static IEnumerable GetMappedConstraints(this IKey key)
{
key.DeclaringEntityType.Model.EnsureRelationalModel();
return (IEnumerable?)key.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.UniqueConstraintMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.UniqueConstraintMappings)
+ ?? Enumerable.Empty();
}
///
diff --git a/src/EFCore.Relational/Extensions/RelationalModelExtensions.cs b/src/EFCore.Relational/Extensions/RelationalModelExtensions.cs
index 0e106901ddb..d36f9b026d7 100644
--- a/src/EFCore.Relational/Extensions/RelationalModelExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalModelExtensions.cs
@@ -70,7 +70,7 @@ public static IRelationalModel GetRelationalModel(this IModel model)
if (relationalModel == null)
{
var relationalModelFactory = (Func?)model.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.RelationalModelFactory)
+ RelationalAnnotationNames.RelationalModelFactory)
?? throw new InvalidOperationException(CoreStrings.ModelNotFinalized(nameof(GetRelationalModel)));
lock (relationalModelFactory)
{
diff --git a/src/EFCore.Relational/Extensions/RelationalOwnedNavigationBuilderExtensions.cs b/src/EFCore.Relational/Extensions/RelationalOwnedNavigationBuilderExtensions.cs
index 0f0329748a5..e40b561e023 100644
--- a/src/EFCore.Relational/Extensions/RelationalOwnedNavigationBuilderExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalOwnedNavigationBuilderExtensions.cs
@@ -23,12 +23,7 @@ public static class RelationalOwnedNavigationBuilderExtensions
/// The builder for the owned navigation being configured.
/// The same builder instance so that multiple calls can be chained.
public static OwnedNavigationBuilder ToJson(this OwnedNavigationBuilder builder)
- {
- var navigationName = builder.Metadata.GetNavigation(pointsToPrincipal: false)!.Name;
- builder.ToJson(navigationName);
-
- return builder;
- }
+ => builder.ToJson(builder.Metadata.GetNavigation(pointsToPrincipal: false)!.Name);
///
/// Configures a relationship where this entity type and the entities that it owns are mapped to a JSON column in the database.
@@ -45,12 +40,7 @@ public static OwnedNavigationBuilder ToJson builder)
where TOwnerEntity : class
where TDependentEntity : class
- {
- var navigationName = builder.Metadata.GetNavigation(pointsToPrincipal: false)!.Name;
- builder.ToJson(navigationName);
-
- return builder;
- }
+ => (OwnedNavigationBuilder)((OwnedNavigationBuilder)builder).ToJson();
///
/// Configures a relationship where this entity type and the entities that it owns are mapped to a JSON column in the database.
@@ -68,11 +58,7 @@ public static OwnedNavigationBuilder ToJson (OwnedNavigationBuilder)((OwnedNavigationBuilder)builder).ToJson(jsonColumnName);
///
/// Configures a relationship where this entity type and the entities that it owns are mapped to a JSON column in the database.
@@ -94,6 +80,40 @@ public static OwnedNavigationBuilder ToJson(
return builder;
}
+ ///
+ /// Set the relational database column type to be used to store the document represented by this owned entity.
+ ///
+ ///
+ /// This method should only be specified for the outer-most owned entity in the given ownership structure and
+ /// only when mapping the column to a database document type.
+ ///
+ /// The builder for the owned navigation being configured.
+ /// The database type for the column, or to use the database default.
+ /// The same builder instance so that multiple calls can be chained.
+ public static OwnedNavigationBuilder HasColumnType(
+ this OwnedNavigationBuilder builder,
+ string? columnType)
+ where TOwnerEntity : class
+ where TDependentEntity : class
+ => (OwnedNavigationBuilder)((OwnedNavigationBuilder)builder).HasColumnType(columnType);
+
+ ///
+ /// Set the relational database column type to be used to store the document represented by this owned entity.
+ ///
+ ///
+ /// This method should only be specified for the outer-most owned entity in the given ownership structure and
+ /// only when mapping the column to a database document type.
+ ///
+ /// The builder for the owned navigation being configured.
+ /// The database type for the column, or to use the database default.
+ /// The same builder instance so that multiple calls can be chained.
+ public static OwnedNavigationBuilder HasColumnType(this OwnedNavigationBuilder builder, string? columnType)
+ {
+ builder.OwnedEntityType.SetContainerColumnType(columnType);
+
+ return builder;
+ }
+
///
/// Configures the navigation of an entity mapped to a JSON column, mapping the navigation to a specific JSON property,
/// rather than using the navigation name.
diff --git a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
index db1bbdd1f89..ac188c89e0a 100644
--- a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
@@ -521,8 +521,8 @@ public static IEnumerable GetDefaultColumnMappings(this IPro
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable?)property.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.DefaultColumnMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.DefaultColumnMappings)
+ ?? Enumerable.Empty();
}
///
@@ -534,8 +534,8 @@ public static IEnumerable GetTableColumnMappings(this IProperty
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable?)property.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.TableColumnMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.TableColumnMappings)
+ ?? Enumerable.Empty();
}
///
@@ -547,8 +547,8 @@ public static IEnumerable GetViewColumnMappings(this IProper
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable?)property.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.ViewColumnMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.ViewColumnMappings)
+ ?? Enumerable.Empty();
}
///
@@ -560,8 +560,8 @@ public static IEnumerable GetSqlQueryColumnMappings(this
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable?)property.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.SqlQueryColumnMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.SqlQueryColumnMappings)
+ ?? Enumerable.Empty();
}
///
@@ -573,8 +573,8 @@ public static IEnumerable GetFunctionColumnMappings(this
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable?)property.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.FunctionColumnMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.FunctionColumnMappings)
+ ?? Enumerable.Empty();
}
///
@@ -586,8 +586,8 @@ public static IEnumerable GetInsertStoredPr
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable?)property.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.InsertStoredProcedureResultColumnMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.InsertStoredProcedureResultColumnMappings)
+ ?? Enumerable.Empty();
}
///
@@ -599,8 +599,8 @@ public static IEnumerable GetInsertStoredProce
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable?)property.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.InsertStoredProcedureParameterMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.InsertStoredProcedureParameterMappings)
+ ?? Enumerable.Empty();
}
///
@@ -612,8 +612,8 @@ public static IEnumerable GetDeleteStoredProce
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable?)property.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.DeleteStoredProcedureParameterMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.DeleteStoredProcedureParameterMappings)
+ ?? Enumerable.Empty();
}
///
@@ -625,8 +625,8 @@ public static IEnumerable GetUpdateStoredPr
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable?)property.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.UpdateStoredProcedureResultColumnMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.UpdateStoredProcedureResultColumnMappings)
+ ?? Enumerable.Empty();
}
///
@@ -638,8 +638,8 @@ public static IEnumerable GetUpdateStoredProce
{
property.DeclaringType.Model.EnsureRelationalModel();
return (IEnumerable?)property.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.UpdateStoredProcedureParameterMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.UpdateStoredProcedureParameterMappings)
+ ?? Enumerable.Empty();
}
///
diff --git a/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs b/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs
index 08e551a6ebc..930af7d1a75 100644
--- a/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalTypeBaseExtensions.cs
@@ -43,8 +43,8 @@ public static IEnumerable GetDefaultMappings(this ITypeBase t
{
typeBase.Model.EnsureRelationalModel();
return (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.DefaultMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.DefaultMappings)
+ ?? Enumerable.Empty();
}
///
@@ -56,8 +56,8 @@ public static IEnumerable GetTableMappings(this ITypeBase typeBas
{
typeBase.Model.EnsureRelationalModel();
return (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.TableMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.TableMappings)
+ ?? Enumerable.Empty();
}
#endregion Table mapping
@@ -89,8 +89,8 @@ public static IEnumerable GetViewMappings(this ITypeBase typeBase)
{
typeBase.Model.EnsureRelationalModel();
return (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.ViewMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.ViewMappings)
+ ?? Enumerable.Empty();
}
#endregion View mapping
@@ -114,8 +114,8 @@ public static IEnumerable GetSqlQueryMappings(this ITypeBase t
{
typeBase.Model.EnsureRelationalModel();
return (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.SqlQueryMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.SqlQueryMappings)
+ ?? Enumerable.Empty();
}
#endregion SQL query mapping
@@ -139,8 +139,8 @@ public static IEnumerable GetFunctionMappings(this ITypeBase t
{
typeBase.Model.EnsureRelationalModel();
return (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.FunctionMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.FunctionMappings)
+ ?? Enumerable.Empty();
}
#endregion
@@ -209,7 +209,9 @@ public static IEnumerable GetFunctionMappings(this ITypeBase t
public static IEnumerable GetInsertStoredProcedureMappings(this ITypeBase typeBase)
{
typeBase.Model.EnsureRelationalModel();
- return (IEnumerable?)typeBase.FindRuntimeAnnotationValue(RelationalAnnotationNames.InsertStoredProcedureMappings) ?? Enumerable.Empty();
+ return (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.InsertStoredProcedureMappings)
+ ?? Enumerable.Empty();
}
///
@@ -220,7 +222,9 @@ public static IEnumerable GetInsertStoredProcedureMappi
public static IEnumerable GetDeleteStoredProcedureMappings(this ITypeBase typeBase)
{
typeBase.Model.EnsureRelationalModel();
- return (IEnumerable?)typeBase.FindRuntimeAnnotationValue(RelationalAnnotationNames.DeleteStoredProcedureMappings) ?? Enumerable.Empty();
+ return (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.DeleteStoredProcedureMappings)
+ ?? Enumerable.Empty();
}
///
@@ -231,7 +235,9 @@ public static IEnumerable GetDeleteStoredProcedureMappi
public static IEnumerable GetUpdateStoredProcedureMappings(this ITypeBase typeBase)
{
typeBase.Model.EnsureRelationalModel();
- return (IEnumerable?)typeBase.FindRuntimeAnnotationValue(RelationalAnnotationNames.UpdateStoredProcedureMappings) ?? Enumerable.Empty();
+ return (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
+ RelationalAnnotationNames.UpdateStoredProcedureMappings)
+ ?? Enumerable.Empty();
}
#endregion
@@ -368,6 +374,16 @@ public static bool IsMappedToJson(this IReadOnlyTypeBase typeBase)
? entityType.GetContainerColumnName()
: ((IReadOnlyComplexType)typeBase).GetContainerColumnName();
+ ///
+ /// Gets the column type to use for the container column to which the type is mapped.
+ ///
+ /// The type.
+ /// The database column type.
+ public static string? GetContainerColumnType(this IReadOnlyTypeBase typeBase)
+ => typeBase is IReadOnlyEntityType entityType
+ ? entityType.GetContainerColumnType()
+ : null;
+
///
/// Gets the value of JSON property name used for the given entity mapped to a JSON column.
///
diff --git a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs
index 2a526d3dfc2..d0ee126a44c 100644
--- a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs
+++ b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs
@@ -96,8 +96,7 @@ public static readonly IDictionary RelationalServi
typeof(IAggregateMethodCallTranslatorPlugin),
new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true)
},
- { typeof(IMemberTranslatorPlugin), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) },
- { typeof(IMigratorPlugin), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) }
+ { typeof(IMemberTranslatorPlugin), new ServiceCharacteristics(ServiceLifetime.Scoped, multipleRegistrations: true) }
};
///
diff --git a/src/EFCore.Relational/Infrastructure/RelationalDbContextOptionsBuilder.cs b/src/EFCore.Relational/Infrastructure/RelationalDbContextOptionsBuilder.cs
index ee0e59414f7..6f8bd323463 100644
--- a/src/EFCore.Relational/Infrastructure/RelationalDbContextOptionsBuilder.cs
+++ b/src/EFCore.Relational/Infrastructure/RelationalDbContextOptionsBuilder.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel;
+using Microsoft.EntityFrameworkCore.Internal;
namespace Microsoft.EntityFrameworkCore.Infrastructure;
@@ -26,9 +27,7 @@ public abstract class RelationalDbContextOptionsBuilder :
///
/// The core options builder.
protected RelationalDbContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder)
- {
- OptionsBuilder = optionsBuilder;
- }
+ => OptionsBuilder = optionsBuilder;
///
/// Gets the core options builder.
@@ -102,7 +101,7 @@ public virtual TBuilder MigrationsAssembly(string? assemblyName)
///
/// See Database migrations for more information and examples.
///
- /// The where the migrations are located.
+ /// The where the migrations are located.
/// The same builder instance so that multiple calls can be chained.
public virtual TBuilder MigrationsAssembly(Assembly assembly)
=> WithOption(e => (TExtension)e.WithMigrationsAssembly(assembly));
@@ -159,6 +158,54 @@ public virtual TBuilder ExecutionStrategy(
=> WithOption(
e => (TExtension)e.WithExecutionStrategyFactory(Check.NotNull(getExecutionStrategy, nameof(getExecutionStrategy))));
+ ///
+ /// Configures the context to translate parameterized collections to inline constants.
+ ///
+ ///
+ ///
+ /// When a LINQ query contains a parameterized collection, by default EF Core parameterizes the entire collection as a single
+ /// SQL parameter, if possible. For example, on SQL Server, the LINQ query Where(b => ids.Contains(b.Id) is translated to
+ /// WHERE [b].[Id] IN (SELECT [i].[value] FROM OPENJSON(@__ids_0) ...). While this helps with query plan caching, it can
+ /// produce worse query plans for certain query types.
+ ///
+ ///
+ /// instructs EF to translate the collection to a set of constants:
+ /// WHERE [b].[Id] IN (1, 2, 3). This can produce better query plans for certain query types, but can also lead to query
+ /// plan bloat.
+ ///
+ ///
+ /// Note that it's possible to cause EF to translate a specific collection in a specific query to constants by wrapping the
+ /// parameterized collection in : Where(b => EF.Constant(ids).Contains(b.Id). This overrides
+ /// the default.
+ ///
+ ///
+ public virtual TBuilder TranslateParameterizedCollectionsToConstants()
+ => WithOption(e => (TExtension)e.WithParameterizedCollectionTranslationMode(ParameterizedCollectionTranslationMode.Constantize));
+
+ ///
+ /// Configures the context to translate parameterized collections to inline constants.
+ ///
+ ///
+ ///
+ /// When a LINQ query contains a parameterized collection, by default EF Core parameterizes the entire collection as a single
+ /// SQL parameter, if possible. For example, on SQL Server, the LINQ query Where(b => ids.Contains(b.Id) is translated to
+ /// WHERE [b].[Id] IN (SELECT [i].[value] FROM OPENJSON(@__ids_0) ...). While this helps with query plan caching, it can
+ /// produce worse query plans for certain query types.
+ ///
+ ///
+ /// instructs EF to translate the collection to a set of constants:
+ /// WHERE [b].[Id] IN (1, 2, 3). This can produce better query plans for certain query types, but can also lead to query
+ /// plan bloat.
+ ///
+ ///
+ /// Note that it's possible to cause EF to translate a specific collection in a specific query to constants by wrapping the
+ /// parameterized collection in : Where(b => EF.Constant(ids).Contains(b.Id). This overrides
+ /// the default.
+ ///
+ ///
+ public virtual TBuilder TranslateParameterizedCollectionsToParameters()
+ => WithOption(e => (TExtension)e.WithParameterizedCollectionTranslationMode(ParameterizedCollectionTranslationMode.Parameterize));
+
///
/// Sets an option by cloning the extension used to store the settings. This ensures the builder
/// does not modify options that are already in use elsewhere.
diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelRuntimeInitializer.cs b/src/EFCore.Relational/Infrastructure/RelationalModelRuntimeInitializer.cs
index c60b4d3fa9a..26f333099b0 100644
--- a/src/EFCore.Relational/Infrastructure/RelationalModelRuntimeInitializer.cs
+++ b/src/EFCore.Relational/Infrastructure/RelationalModelRuntimeInitializer.cs
@@ -36,9 +36,7 @@ public RelationalModelRuntimeInitializer(
ModelRuntimeInitializerDependencies dependencies,
RelationalModelRuntimeInitializerDependencies relationalDependencies)
: base(dependencies)
- {
- RelationalDependencies = relationalDependencies;
- }
+ => RelationalDependencies = relationalDependencies;
///
/// Relational provider-specific dependencies for this service.
@@ -61,11 +59,12 @@ protected override void InitializeModel(IModel model, bool designTime, bool prev
model.SetRuntimeAnnotation(RelationalAnnotationNames.ModelDependencies, RelationalDependencies.RelationalModelDependencies);
}
else if (model.FindRuntimeAnnotation(RelationalAnnotationNames.RelationalModel) == null
- && model.FindRuntimeAnnotation(RelationalAnnotationNames.RelationalModelFactory) == null)
+ && model.FindRuntimeAnnotation(RelationalAnnotationNames.RelationalModelFactory) == null)
{
var annotationProvider = RelationalDependencies.RelationalAnnotationProvider;
var typeMappingSource = (IRelationalTypeMappingSource)Dependencies.ModelDependencies.TypeMappingSource;
- model.SetRuntimeAnnotation(RelationalAnnotationNames.RelationalModelFactory,
+ model.SetRuntimeAnnotation(
+ RelationalAnnotationNames.RelationalModelFactory,
() => RelationalModel.Create(
model,
annotationProvider,
diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
index 4c5f8b5db56..69ed1618137 100644
--- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
+++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
@@ -32,9 +32,7 @@ public RelationalModelValidator(
ModelValidatorDependencies dependencies,
RelationalModelValidatorDependencies relationalDependencies)
: base(dependencies)
- {
- RelationalDependencies = relationalDependencies;
- }
+ => RelationalDependencies = relationalDependencies;
///
/// Relational provider-specific dependencies for this service.
@@ -2017,9 +2015,10 @@ private static void ValidateNonTphMapping(IEntityType rootEntityType, StoreObjec
var unmappedOwnedType = entityType.GetReferencingForeignKeys()
.Where(fk => fk.IsOwnership)
.Select(fk => fk.DeclaringEntityType)
- .FirstOrDefault(owned => StoreObjectIdentifier.Create(owned, storeObjectType) == null
- && ((IConventionEntityType)owned).GetStoreObjectConfigurationSource(storeObjectType) == null
- && !owned.IsMappedToJson());
+ .FirstOrDefault(
+ owned => StoreObjectIdentifier.Create(owned, storeObjectType) == null
+ && ((IConventionEntityType)owned).GetStoreObjectConfigurationSource(storeObjectType) == null
+ && !owned.IsMappedToJson());
if (unmappedOwnedType != null
&& entityType.GetDerivedTypes().Any(derived => StoreObjectIdentifier.Create(derived, storeObjectType) != null))
{
@@ -2521,8 +2520,7 @@ protected virtual void ValidateIndexProperties(
}
}
-
- ///
+ ///
protected override void ValidateData(IModel model, IDiagnosticsLogger logger)
{
foreach (var entityType in model.GetEntityTypes())
@@ -2532,11 +2530,13 @@ protected override void ValidateData(IModel model, IDiagnosticsLogger x.ForeignKey.IsOwnership && x.TargetEntityType.IsMappedToJson()))
+ foreach (var navigation in entityType.GetNavigations()
+ .Where(x => x.ForeignKey.IsOwnership && x.TargetEntityType.IsMappedToJson()))
{
if (entityType.GetSeedData().Any(x => x.TryGetValue(navigation.Name, out var _)))
{
- throw new InvalidOperationException(RelationalStrings.HasDataNotSupportedForEntitiesMappedToJson(entityType.DisplayName()));
+ throw new InvalidOperationException(
+ RelationalStrings.HasDataNotSupportedForEntitiesMappedToJson(entityType.DisplayName()));
}
}
}
@@ -2592,6 +2592,23 @@ protected virtual void ValidateJsonEntities(
IModel model,
IDiagnosticsLogger logger)
{
+ foreach (var entityType in model.GetEntityTypes())
+ {
+ if (entityType[RelationalAnnotationNames.ContainerColumnType] != null)
+ {
+ if (entityType.FindOwnership()?.PrincipalEntityType.IsOwned() == true)
+ {
+ throw new InvalidOperationException(RelationalStrings.ContainerTypeOnNestedOwnedEntityType(entityType.DisplayName()));
+ }
+
+ if (!entityType.IsOwned()
+ || entityType.GetContainerColumnName() == null)
+ {
+ throw new InvalidOperationException(RelationalStrings.ContainerTypeOnNonContainer(entityType.DisplayName()));
+ }
+ }
+ }
+
var tables = BuildSharedTableEntityMap(model.GetEntityTypes());
foreach (var (table, mappedTypes) in tables)
{
@@ -2771,23 +2788,24 @@ protected virtual void ValidateJsonEntityKey(
{
if (primaryKeyProperty.GetJsonPropertyName() != null)
{
- // issue #28594
+ // Issue #28594
throw new InvalidOperationException(
RelationalStrings.JsonEntityWithExplicitlyConfiguredJsonPropertyNameOnKey(
primaryKeyProperty.Name, jsonEntityType.DisplayName()));
}
- }
- if (!ownership.IsUnique)
- {
- // for collection entities, make sure that ordinal key is not explicitly defined
- var ordinalKeyProperty = primaryKeyProperties.Last();
- if (!ordinalKeyProperty.IsOrdinalKeyProperty())
+ if (!ownership.IsUnique)
{
- // issue #28594
- throw new InvalidOperationException(
- RelationalStrings.JsonEntityWithExplicitlyConfiguredOrdinalKey(
- jsonEntityType.DisplayName()));
+ // For collection entities, no key properties other than the generated ones are allowed because they
+ // will not be persisted.
+ if (!primaryKeyProperty.IsOrdinalKeyProperty()
+ && !primaryKeyProperty.IsForeignKey())
+ {
+ // issue #28594
+ throw new InvalidOperationException(
+ RelationalStrings.JsonEntityWithExplicitlyConfiguredKey(
+ jsonEntityType.DisplayName(), primaryKeyProperty.Name));
+ }
}
}
@@ -2815,8 +2833,13 @@ protected virtual void ValidateJsonEntityProperties(
IEntityType jsonEntityType)
{
var jsonPropertyNames = new List();
- foreach (var property in jsonEntityType.GetDeclaredProperties().Where(p => !string.IsNullOrEmpty(p.GetJsonPropertyName())))
+ foreach (var property in jsonEntityType.GetDeclaredProperties())
{
+ if (string.IsNullOrEmpty(property.GetJsonPropertyName()))
+ {
+ continue;
+ }
+
if (property.TryGetDefaultValue(out var _))
{
throw new InvalidOperationException(
@@ -2839,6 +2862,12 @@ protected virtual void ValidateJsonEntityProperties(
foreach (var navigation in jsonEntityType.GetDeclaredNavigations())
{
+ if (!navigation.TargetEntityType.IsMappedToJson()
+ || navigation.IsOnDependent)
+ {
+ continue;
+ }
+
var jsonPropertyName = navigation.TargetEntityType.GetJsonPropertyName()!;
if (!jsonPropertyNames.Contains(jsonPropertyName))
{
diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidatorDependencies.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidatorDependencies.cs
index a388cdb7c16..785c584acfd 100644
--- a/src/EFCore.Relational/Infrastructure/RelationalModelValidatorDependencies.cs
+++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidatorDependencies.cs
@@ -47,9 +47,7 @@ public sealed record RelationalModelValidatorDependencies
[EntityFrameworkInternal]
public RelationalModelValidatorDependencies(
IRelationalTypeMappingSource typeMappingSource)
- {
- TypeMappingSource = typeMappingSource;
- }
+ => TypeMappingSource = typeMappingSource;
///
/// The type mapper.
diff --git a/src/EFCore.Relational/Infrastructure/RelationalOptionsExtension.cs b/src/EFCore.Relational/Infrastructure/RelationalOptionsExtension.cs
index f8adafd96d9..ce2742e46a6 100644
--- a/src/EFCore.Relational/Infrastructure/RelationalOptionsExtension.cs
+++ b/src/EFCore.Relational/Infrastructure/RelationalOptionsExtension.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Text;
+using Microsoft.EntityFrameworkCore.Internal;
namespace Microsoft.EntityFrameworkCore.Infrastructure;
@@ -33,10 +34,10 @@ public abstract class RelationalOptionsExtension : IDbContextOptionsExtension
private QuerySplittingBehavior? _querySplittingBehavior;
private string? _migrationsAssembly;
private Assembly? _migrationsAssemblyObject;
-
private string? _migrationsHistoryTableName;
private string? _migrationsHistoryTableSchema;
private Func? _executionStrategyFactory;
+ private ParameterizedCollectionTranslationMode? _parameterizedCollectionTranslationMode;
///
/// Creates a new set of options with everything set to default values.
@@ -63,6 +64,7 @@ protected RelationalOptionsExtension(RelationalOptionsExtension copyFrom)
_migrationsHistoryTableName = copyFrom._migrationsHistoryTableName;
_migrationsHistoryTableSchema = copyFrom._migrationsHistoryTableSchema;
_executionStrategyFactory = copyFrom._executionStrategyFactory;
+ _parameterizedCollectionTranslationMode = copyFrom._parameterizedCollectionTranslationMode;
}
///
@@ -381,6 +383,27 @@ public virtual RelationalOptionsExtension WithExecutionStrategyFactory(
return clone;
}
+ ///
+ /// Configured translation mode for parameterized collections.
+ ///
+ public virtual ParameterizedCollectionTranslationMode? ParameterizedCollectionTranslationMode
+ => _parameterizedCollectionTranslationMode;
+
+ ///
+ /// Creates a new instance with all options the same as for this instance, but with the given option changed.
+ /// It is unusual to call this method directly. Instead use .
+ ///
+ /// The option to change.
+ public virtual RelationalOptionsExtension WithParameterizedCollectionTranslationMode(
+ ParameterizedCollectionTranslationMode parameterizedCollectionTranslationMode)
+ {
+ var clone = Clone();
+
+ clone._parameterizedCollectionTranslationMode = parameterizedCollectionTranslationMode;
+
+ return clone;
+ }
+
///
/// Finds an existing registered on the given options
/// or throws if none has been registered. This is typically used to find some relational
@@ -540,6 +563,12 @@ public override string LogFragment
builder.Append(Extension._migrationsHistoryTableName ?? HistoryRepository.DefaultTableName).Append(' ');
}
+ if (Extension._parameterizedCollectionTranslationMode != null)
+ {
+ builder.Append("ParameterizedCollectionTranslationMode=").Append(Extension._parameterizedCollectionTranslationMode)
+ .Append(' ');
+ }
+
_logFragment = builder.ToString();
}
diff --git a/src/EFCore.Relational/Internal/ParameterizedCollectionTranslationMode.cs b/src/EFCore.Relational/Internal/ParameterizedCollectionTranslationMode.cs
new file mode 100644
index 00000000000..cfb59de6f08
--- /dev/null
+++ b/src/EFCore.Relational/Internal/ParameterizedCollectionTranslationMode.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.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 enum ParameterizedCollectionTranslationMode
+{
+ ///
+ /// 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.
+ ///
+ Constantize = 0,
+
+ ///
+ /// 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.
+ ///
+ Parameterize,
+}
diff --git a/src/EFCore.Relational/Internal/SemanticVersionComparer.cs b/src/EFCore.Relational/Internal/SemanticVersionComparer.cs
index f1f61e298c9..2f92a0fdd60 100644
--- a/src/EFCore.Relational/Internal/SemanticVersionComparer.cs
+++ b/src/EFCore.Relational/Internal/SemanticVersionComparer.cs
@@ -1,9 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
-using System.Collections.Generic;
-
namespace Microsoft.EntityFrameworkCore.Internal;
///
diff --git a/src/EFCore.Relational/Metadata/Builders/CheckConstraintBuilder.cs b/src/EFCore.Relational/Metadata/Builders/CheckConstraintBuilder.cs
index fd9c2fccebc..62ef2431ff0 100644
--- a/src/EFCore.Relational/Metadata/Builders/CheckConstraintBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/CheckConstraintBuilder.cs
@@ -22,9 +22,7 @@ public class CheckConstraintBuilder : IInfrastructure
[EntityFrameworkInternal]
public CheckConstraintBuilder(IMutableCheckConstraint checkConstraint)
- {
- Builder = ((CheckConstraint)checkConstraint).Builder;
- }
+ => Builder = ((CheckConstraint)checkConstraint).Builder;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Builders/DbFunctionBuilderBase.cs b/src/EFCore.Relational/Metadata/Builders/DbFunctionBuilderBase.cs
index ffaa6126514..6d947a239a4 100644
--- a/src/EFCore.Relational/Metadata/Builders/DbFunctionBuilderBase.cs
+++ b/src/EFCore.Relational/Metadata/Builders/DbFunctionBuilderBase.cs
@@ -22,9 +22,7 @@ public abstract class DbFunctionBuilderBase : IInfrastructure
[EntityFrameworkInternal]
protected DbFunctionBuilderBase(IMutableDbFunction function)
- {
- Builder = ((DbFunction)function).Builder;
- }
+ => Builder = ((DbFunction)function).Builder;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Builders/DbFunctionParameterBuilder.cs b/src/EFCore.Relational/Metadata/Builders/DbFunctionParameterBuilder.cs
index cbe2eb2e24a..6383dedf8ac 100644
--- a/src/EFCore.Relational/Metadata/Builders/DbFunctionParameterBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/DbFunctionParameterBuilder.cs
@@ -23,9 +23,7 @@ public class DbFunctionParameterBuilder : IInfrastructure
[EntityFrameworkInternal]
public DbFunctionParameterBuilder(IMutableDbFunctionParameter parameter)
- {
- Builder = ((DbFunctionParameter)parameter).Builder;
- }
+ => Builder = ((DbFunctionParameter)parameter).Builder;
private InternalDbFunctionParameterBuilder Builder { [DebuggerStepThrough] get; }
diff --git a/src/EFCore.Relational/Metadata/Builders/OwnedNavigationTableValuedFunctionBuilder.cs b/src/EFCore.Relational/Metadata/Builders/OwnedNavigationTableValuedFunctionBuilder.cs
index d4b1d1ca368..6f31fbca6e5 100644
--- a/src/EFCore.Relational/Metadata/Builders/OwnedNavigationTableValuedFunctionBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/OwnedNavigationTableValuedFunctionBuilder.cs
@@ -19,9 +19,7 @@ public OwnedNavigationTableValuedFunctionBuilder(
IMutableDbFunction function,
OwnedNavigationBuilder ownedNavigationBuilder)
: base(function)
- {
- OwnedNavigationBuilder = ownedNavigationBuilder;
- }
+ => OwnedNavigationBuilder = ownedNavigationBuilder;
private OwnedNavigationBuilder OwnedNavigationBuilder { get; }
diff --git a/src/EFCore.Relational/Metadata/Builders/SequenceBuilder.cs b/src/EFCore.Relational/Metadata/Builders/SequenceBuilder.cs
index 2a0d2e0646c..7fc797f03c3 100644
--- a/src/EFCore.Relational/Metadata/Builders/SequenceBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/SequenceBuilder.cs
@@ -19,9 +19,7 @@ public class SequenceBuilder : IInfrastructure
///
/// The to configure.
public SequenceBuilder(IMutableSequence sequence)
- {
- Builder = ((Sequence)sequence).Builder;
- }
+ => Builder = ((Sequence)sequence).Builder;
private InternalSequenceBuilder Builder { [DebuggerStepThrough] get; }
diff --git a/src/EFCore.Relational/Metadata/Builders/TableValuedFunctionBuilder.cs b/src/EFCore.Relational/Metadata/Builders/TableValuedFunctionBuilder.cs
index 6f7533d4e18..f5f9cc3d4f6 100644
--- a/src/EFCore.Relational/Metadata/Builders/TableValuedFunctionBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/TableValuedFunctionBuilder.cs
@@ -17,9 +17,7 @@ public class TableValuedFunctionBuilder : DbFunctionBuilderBase, IInfrastructure
[EntityFrameworkInternal]
public TableValuedFunctionBuilder(IMutableDbFunction function, EntityTypeBuilder entityTypeBuilder)
: base(function)
- {
- EntityTypeBuilder = entityTypeBuilder;
- }
+ => EntityTypeBuilder = entityTypeBuilder;
private EntityTypeBuilder EntityTypeBuilder { get; }
diff --git a/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs b/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs
index a25f84861ec..b8e49bb22c2 100644
--- a/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs
@@ -41,9 +41,7 @@ protected RelationalConventionSetBuilder(
ProviderConventionSetBuilderDependencies dependencies,
RelationalConventionSetBuilderDependencies relationalDependencies)
: base(dependencies)
- {
- RelationalDependencies = relationalDependencies;
- }
+ => RelationalDependencies = relationalDependencies;
///
/// Relational provider-specific dependencies for this service.
@@ -81,9 +79,15 @@ public override ConventionSet CreateConventionSet()
conventionSet.Replace(
new RelationalValueGenerationConvention(Dependencies, RelationalDependencies));
+
+ conventionSet.Replace(
+ new RelationalKeyDiscoveryConvention(Dependencies, RelationalDependencies));
+
conventionSet.Replace(
new RelationalQueryFilterRewritingConvention(Dependencies, RelationalDependencies));
- conventionSet.Replace(new RelationalRuntimeModelConvention(Dependencies, RelationalDependencies));
+
+ conventionSet.Replace(
+ new RelationalRuntimeModelConvention(Dependencies, RelationalDependencies));
return conventionSet;
}
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalColumnAttributeConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalColumnAttributeConvention.cs
index 8669ae16b76..186a701cd2b 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalColumnAttributeConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalColumnAttributeConvention.cs
@@ -22,9 +22,7 @@ public RelationalColumnAttributeConvention(
ProviderConventionSetBuilderDependencies dependencies,
RelationalConventionSetBuilderDependencies relationalDependencies)
: base(dependencies)
- {
- RelationalDependencies = relationalDependencies;
- }
+ => RelationalDependencies = relationalDependencies;
///
/// Relational provider-specific dependencies for this service.
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalColumnCommentAttributeConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalColumnCommentAttributeConvention.cs
index 4b4277cdc19..e24c122e8f6 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalColumnCommentAttributeConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalColumnCommentAttributeConvention.cs
@@ -20,9 +20,7 @@ public RelationalColumnCommentAttributeConvention(
ProviderConventionSetBuilderDependencies dependencies,
RelationalConventionSetBuilderDependencies relationalDependencies)
: base(dependencies)
- {
- RelationalDependencies = relationalDependencies;
- }
+ => RelationalDependencies = relationalDependencies;
///
/// Relational provider-specific dependencies for this service.
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalKeyDiscoveryConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalKeyDiscoveryConvention.cs
new file mode 100644
index 00000000000..1d2a1e2d96c
--- /dev/null
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalKeyDiscoveryConvention.cs
@@ -0,0 +1,158 @@
+// 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.Conventions;
+
+///
+/// A relational-specific convention inheriting from .
+///
+///
+///
+/// See Model building conventions for more information and examples.
+///
+///
+public class RelationalKeyDiscoveryConvention : KeyDiscoveryConvention, IEntityTypeAnnotationChangedConvention
+{
+ ///
+ /// 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 const string SynthesizedOrdinalPropertyName = "__synthesizedOrdinal";
+
+ ///
+ /// Creates a new instance of .
+ ///
+ /// Parameter object containing dependencies for this convention.
+ /// Parameter object containing relational dependencies for this convention.
+ public RelationalKeyDiscoveryConvention(
+ ProviderConventionSetBuilderDependencies dependencies,
+ RelationalConventionSetBuilderDependencies relationalDependencies)
+ : base(dependencies)
+ {
+ RelationalDependencies = relationalDependencies;
+ }
+
+ ///
+ /// Relational provider-specific dependencies for this service.
+ ///
+ protected virtual RelationalConventionSetBuilderDependencies RelationalDependencies { get; }
+
+ ///
+ protected override List? DiscoverKeyProperties(IConventionEntityType entityType)
+ {
+ var ownership = entityType.FindOwnership();
+ if (ownership?.DeclaringEntityType != entityType)
+ {
+ ownership = null;
+ }
+
+ // Don't discover key properties for owned collection types mapped to JSON so that we can persist properties
+ // called `Id` without attempting to persist key values.
+ if (ownership?.IsUnique == false
+ && entityType.GetContainerColumnName() is not null)
+ {
+ return [];
+ }
+
+ return base.DiscoverKeyProperties(entityType);
+ }
+
+ ///
+ protected override void ProcessKeyProperties(
+ IList keyProperties,
+ IConventionEntityType entityType)
+ {
+ var isMappedToJson = entityType.GetContainerColumnName() is not null;
+ var synthesizedProperty = keyProperties.FirstOrDefault(p => p.Name == SynthesizedOrdinalPropertyName);
+ var ownershipForeignKey = entityType.FindOwnership();
+ if (ownershipForeignKey?.IsUnique == false
+ && isMappedToJson)
+ {
+ // This is an owned collection, so it has a composite key consisting of FK properties pointing to the owner PK,
+ // any additional key properties defined by the application, and then the synthesized property.
+ // Add these in the correct order--this is somewhat inefficient, but we are limited because we have to manipulate the
+ // existing collection.
+ var existingKeyProperties = keyProperties.ToList();
+ keyProperties.Clear();
+
+ // Add the FK properties to form the first part of the composite key.
+ foreach (var conventionProperty in ownershipForeignKey.Properties)
+ {
+ keyProperties.Add(conventionProperty);
+ }
+
+ // Generate the synthesized key property if it doesn't exist.
+ if (synthesizedProperty == null)
+ {
+ var builder = entityType.Builder.CreateUniqueProperty(typeof(int), SynthesizedOrdinalPropertyName, required: true);
+ builder = builder?.ValueGenerated(ValueGenerated.OnAdd) ?? builder;
+ synthesizedProperty = builder!.Metadata;
+ }
+
+ // Add non-duplicate, non-ownership, non-synthesized properties.
+ foreach (var keyProperty in existingKeyProperties)
+ {
+ if (keyProperty != synthesizedProperty
+ && !keyProperties.Contains(keyProperty))
+ {
+ keyProperties.Add(keyProperty);
+ }
+ }
+
+ // Finally, the synthesized property always goes at the end.
+ keyProperties.Add(synthesizedProperty);
+ }
+ else
+ {
+ // Not an owned collection or not mapped to JSON.
+ if (synthesizedProperty is not null)
+ {
+ // This was an owned collection, but now is not, so remove the synthesized property.
+ keyProperties.Remove(synthesizedProperty);
+ }
+
+ base.ProcessKeyProperties(keyProperties, entityType);
+ }
+ }
+
+ ///
+ public override void ProcessPropertyAdded(
+ IConventionPropertyBuilder propertyBuilder,
+ IConventionContext context)
+ {
+ if (propertyBuilder.Metadata.Name != SynthesizedOrdinalPropertyName)
+ {
+ base.ProcessPropertyAdded(propertyBuilder, context);
+ }
+ }
+
+ ///
+ public virtual void ProcessEntityTypeAnnotationChanged(
+ IConventionEntityTypeBuilder entityTypeBuilder,
+ string name,
+ IConventionAnnotation? annotation,
+ IConventionAnnotation? oldAnnotation,
+ IConventionContext context)
+ {
+ if (name == RelationalAnnotationNames.ContainerColumnName)
+ {
+ Configure(this, entityTypeBuilder);
+ }
+
+ static void Configure(RelationalKeyDiscoveryConvention me, IConventionEntityTypeBuilder builder)
+ {
+ me.TryConfigurePrimaryKey(builder);
+
+ foreach (var ownershipFk in builder.Metadata.GetReferencingForeignKeys())
+ {
+ if (ownershipFk.IsOwnership)
+ {
+ Configure(me, ownershipFk.DeclaringEntityType.Builder);
+ }
+ }
+ }
+ }
+}
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalNavigationJsonPropertyNameAttributeConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalNavigationJsonPropertyNameAttributeConvention.cs
index 17f55836472..71de2ea23fe 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalNavigationJsonPropertyNameAttributeConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalNavigationJsonPropertyNameAttributeConvention.cs
@@ -25,9 +25,7 @@ public RelationalNavigationJsonPropertyNameAttributeConvention(
ProviderConventionSetBuilderDependencies dependencies,
RelationalConventionSetBuilderDependencies relationalDependencies)
: base(dependencies)
- {
- RelationalDependencies = relationalDependencies;
- }
+ => RelationalDependencies = relationalDependencies;
///
/// Relational provider-specific dependencies for this service.
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalPropertyJsonPropertyNameAttributeConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalPropertyJsonPropertyNameAttributeConvention.cs
index 33f2b1ab240..3c5150113bb 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalPropertyJsonPropertyNameAttributeConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalPropertyJsonPropertyNameAttributeConvention.cs
@@ -22,9 +22,7 @@ public RelationalPropertyJsonPropertyNameAttributeConvention(
ProviderConventionSetBuilderDependencies dependencies,
RelationalConventionSetBuilderDependencies relationalDependencies)
: base(dependencies)
- {
- RelationalDependencies = relationalDependencies;
- }
+ => RelationalDependencies = relationalDependencies;
///
/// Relational provider-specific dependencies for this service.
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalRuntimeModelConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalRuntimeModelConvention.cs
index e61eb9cf64f..20aeb33cd38 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalRuntimeModelConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalRuntimeModelConvention.cs
@@ -23,9 +23,7 @@ public RelationalRuntimeModelConvention(
ProviderConventionSetBuilderDependencies dependencies,
RelationalConventionSetBuilderDependencies relationalDependencies)
: base(dependencies)
- {
- RelationalDependencies = relationalDependencies;
- }
+ => RelationalDependencies = relationalDependencies;
///
/// Relational provider-specific dependencies for this service.
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalTableAttributeConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalTableAttributeConvention.cs
index 3226db5a8dd..6dd75c621ad 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalTableAttributeConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalTableAttributeConvention.cs
@@ -22,9 +22,7 @@ public RelationalTableAttributeConvention(
ProviderConventionSetBuilderDependencies dependencies,
RelationalConventionSetBuilderDependencies relationalDependencies)
: base(dependencies)
- {
- RelationalDependencies = relationalDependencies;
- }
+ => RelationalDependencies = relationalDependencies;
///
/// Relational provider-specific dependencies for this service.
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalTableCommentAttributeConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalTableCommentAttributeConvention.cs
index ba202eb015e..e6ac6c4237d 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalTableCommentAttributeConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalTableCommentAttributeConvention.cs
@@ -20,9 +20,7 @@ public RelationalTableCommentAttributeConvention(
ProviderConventionSetBuilderDependencies dependencies,
RelationalConventionSetBuilderDependencies relationalDependencies)
: base(dependencies)
- {
- RelationalDependencies = relationalDependencies;
- }
+ => RelationalDependencies = relationalDependencies;
///
/// Relational provider-specific dependencies for this service.
diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalValueGenerationConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalValueGenerationConvention.cs
index 20029a66117..087d1feec47 100644
--- a/src/EFCore.Relational/Metadata/Conventions/RelationalValueGenerationConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/RelationalValueGenerationConvention.cs
@@ -28,9 +28,7 @@ public RelationalValueGenerationConvention(
ProviderConventionSetBuilderDependencies dependencies,
RelationalConventionSetBuilderDependencies relationalDependencies)
: base(dependencies)
- {
- RelationalDependencies = relationalDependencies;
- }
+ => RelationalDependencies = relationalDependencies;
///
/// Relational provider-specific dependencies for this service.
diff --git a/src/EFCore.Relational/Metadata/Conventions/SequenceUniquificationConvention.cs b/src/EFCore.Relational/Metadata/Conventions/SequenceUniquificationConvention.cs
index d901df57937..9afdd36ab39 100644
--- a/src/EFCore.Relational/Metadata/Conventions/SequenceUniquificationConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/SequenceUniquificationConvention.cs
@@ -44,7 +44,7 @@ public virtual void ProcessModelFinalizing(
IConventionContext context)
{
var model = modelBuilder.Metadata;
- var modelSequences =
+ var modelSequences =
(IReadOnlyDictionary<(string Name, string? Schema), ISequence>?)model[RelationalAnnotationNames.Sequences];
if (modelSequences != null)
{
diff --git a/src/EFCore.Relational/Metadata/ITable.cs b/src/EFCore.Relational/Metadata/ITable.cs
index bfcaa5b0c51..6b933ec9691 100644
--- a/src/EFCore.Relational/Metadata/ITable.cs
+++ b/src/EFCore.Relational/Metadata/ITable.cs
@@ -134,8 +134,8 @@ string ITableBase.ToDebugString(MetadataDebugStringOptions options, int indent)
}
if ((options & MetadataDebugStringOptions.SingleLine) == 0
- && designTime
- && Comment != null)
+ && designTime
+ && Comment != null)
{
builder
.AppendLine()
diff --git a/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs b/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs
index e40951abb20..08ade8a541b 100644
--- a/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs
+++ b/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs
@@ -122,11 +122,7 @@ public static IEnumerable GetCheckConstraints(IReadOnl
public static IReadOnlyCheckConstraint? FindDeclaredCheckConstraint(IReadOnlyEntityType entityType, string name)
{
var dataDictionary = GetConstraintsDictionary(entityType);
- return dataDictionary == null
- ? null
- : dataDictionary.TryGetValue(name, out var checkConstraint)
- ? checkConstraint
- : null;
+ return dataDictionary?.GetValueOrDefault(name);
}
///
diff --git a/src/EFCore.Relational/Metadata/Internal/ColumnNameComparer.cs b/src/EFCore.Relational/Metadata/Internal/ColumnNameComparer.cs
index 28ce0569a97..3880cfcf8b2 100644
--- a/src/EFCore.Relational/Metadata/Internal/ColumnNameComparer.cs
+++ b/src/EFCore.Relational/Metadata/Internal/ColumnNameComparer.cs
@@ -20,9 +20,7 @@ public sealed class ColumnNameComparer : IComparer
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public ColumnNameComparer(Table table)
- {
- _table = table;
- }
+ => _table = table;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Internal/DbFunction.cs b/src/EFCore.Relational/Metadata/Internal/DbFunction.cs
index 4096165f137..f93df12aea9 100644
--- a/src/EFCore.Relational/Metadata/Internal/DbFunction.cs
+++ b/src/EFCore.Relational/Metadata/Internal/DbFunction.cs
@@ -656,7 +656,8 @@ public virtual IReadOnlyList Parameters
/// 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 IStoreFunction? StoreFunction => _storeFunction;
+ public virtual IStoreFunction? StoreFunction
+ => _storeFunction;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Internal/EntityTypeMappingFragment.cs b/src/EFCore.Relational/Metadata/Internal/EntityTypeMappingFragment.cs
index d37388ba95c..eed19d44965 100644
--- a/src/EFCore.Relational/Metadata/Internal/EntityTypeMappingFragment.cs
+++ b/src/EFCore.Relational/Metadata/Internal/EntityTypeMappingFragment.cs
@@ -47,8 +47,10 @@ public EntityTypeMappingFragment(
public virtual InternalEntityTypeMappingFragmentBuilder Builder
{
[DebuggerStepThrough]
- get => _builder ?? throw new InvalidOperationException(CoreStrings.ObjectRemovedFromModel(
- StoreObject.DisplayName()));
+ get => _builder
+ ?? throw new InvalidOperationException(
+ CoreStrings.ObjectRemovedFromModel(
+ StoreObject.DisplayName()));
}
///
diff --git a/src/EFCore.Relational/Metadata/Internal/FunctionMapping.cs b/src/EFCore.Relational/Metadata/Internal/FunctionMapping.cs
index 2bf82c263f2..d6282e8f9c5 100644
--- a/src/EFCore.Relational/Metadata/Internal/FunctionMapping.cs
+++ b/src/EFCore.Relational/Metadata/Internal/FunctionMapping.cs
@@ -19,9 +19,7 @@ public class FunctionMapping : TableMappingBase, IFunctio
///
public FunctionMapping(IEntityType entityType, StoreFunction storeFunction, IDbFunction dbFunction, bool? includesDerivedTypes)
: base(entityType, storeFunction, includesDerivedTypes)
- {
- DbFunction = dbFunction;
- }
+ => DbFunction = dbFunction;
///
public virtual bool IsDefaultFunctionMapping { get; set; }
diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
index 637be020b0e..ae7dd11e42c 100644
--- a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
+++ b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
@@ -23,9 +23,7 @@ public class RelationalModel : Annotatable, IRelationalModel
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public RelationalModel(IModel model)
- {
- Model = model;
- }
+ => Model = model;
///
public virtual IModel Model { get; }
@@ -286,12 +284,14 @@ private static void AddDefaultMappings(
includesDerivedTypes: entityType.GetDirectlyDerivedTypes().Any()
? !isTpc && mappedType == entityType
: null);
+
var containerColumnName = mappedType.GetContainerColumnName();
+ var containerColumnType = mappedType.GetContainerColumnType();
if (!string.IsNullOrEmpty(containerColumnName))
{
CreateContainerColumn(
- defaultTable, containerColumnName, mappedType, relationalTypeMappingSource,
- static (c, t, m) => new JsonColumnBase(c, m.StoreType, t, m));
+ defaultTable, containerColumnName, containerColumnType, mappedType, relationalTypeMappingSource,
+ static (colName, colType, table, mapping) => new JsonColumnBase(colName, colType ?? mapping.StoreType, table, mapping));
}
else
{
@@ -366,6 +366,7 @@ private static void CreateDefaultColumnMapping(
tableMappings = new List>();
complexType.AddRuntimeAnnotation(RelationalAnnotationNames.DefaultMappings, tableMappings);
}
+
tableMappings.Add(tableMapping);
defaultTable.ComplexTypeMappings.Add(tableMapping);
@@ -395,8 +396,8 @@ static string GetColumnName(IProperty property)
private static IEnumerable GetTableMappings(ITypeBase typeBase)
=> (IEnumerable?)typeBase.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.TableMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.TableMappings)
+ ?? Enumerable.Empty();
private static void AddTables(
RelationalModel databaseModel,
@@ -417,8 +418,8 @@ private static void AddTables(
var mappingStrategy = entityType.GetMappingStrategy();
var isTpc = mappingStrategy == RelationalAnnotationNames.TpcMappingStrategy;
var includesDerivedTypes = entityType.GetDirectlyDerivedTypes().Any()
- ? !isTpc && mappedType == entityType
- : (bool?)null;
+ ? !isTpc && mappedType == entityType
+ : (bool?)null;
while (mappedType != null)
{
var mappedTableName = mappedType.GetTableName();
@@ -492,11 +493,12 @@ private static void CreateTableMapping(
};
var containerColumnName = mappedType.GetContainerColumnName();
+ var containerColumnType = mappedType.GetContainerColumnType();
if (!string.IsNullOrEmpty(containerColumnName))
{
CreateContainerColumn(
- table, containerColumnName, (IEntityType)mappedType, relationalTypeMappingSource,
- static (c, t, m) => new JsonColumn(c, m.StoreType, (Table)t, m));
+ table, containerColumnName, containerColumnType, (IEntityType)mappedType, relationalTypeMappingSource,
+ static (colName, colType, table, mapping) => new JsonColumn(colName, colType ?? mapping.StoreType, (Table)table, mapping));
}
else
{
@@ -530,7 +532,8 @@ private static void CreateTableMapping(
{
var complexType = complexProperty.ComplexType;
- var complexTableMappings = (List?)complexType.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableMappings);
+ var complexTableMappings =
+ (List?)complexType.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableMappings);
if (complexTableMappings == null)
{
complexTableMappings = [];
@@ -567,9 +570,10 @@ private static void CreateTableMapping(
private static void CreateContainerColumn(
TableBase tableBase,
string containerColumnName,
+ string? containerColumnType,
IEntityType mappedType,
IRelationalTypeMappingSource relationalTypeMappingSource,
- Func> createColumn)
+ Func> createColumn)
where TColumnMappingBase : class, IColumnMappingBase
{
var ownership = mappedType.GetForeignKeys().Single(fk => fk.IsOwnership);
@@ -577,8 +581,8 @@ private static void CreateContainerColumn(
{
Check.DebugAssert(tableBase.FindColumn(containerColumnName) == null, $"Table does not have column '{containerColumnName}'.");
- var jsonColumnTypeMapping = relationalTypeMappingSource.FindMapping(typeof(JsonElement), mappedType.Model)!;
- var jsonColumn = createColumn(containerColumnName, tableBase, jsonColumnTypeMapping);
+ var jsonColumnTypeMapping = relationalTypeMappingSource.FindMapping(typeof(JsonElement), storeTypeName: containerColumnType)!;
+ var jsonColumn = createColumn(containerColumnName, containerColumnType, tableBase, jsonColumnTypeMapping);
tableBase.Columns.Add(containerColumnName, jsonColumn);
jsonColumn.IsNullable = !ownership.IsRequiredDependent || !ownership.IsUnique;
@@ -625,8 +629,8 @@ private static void AddViews(
}
var includesDerivedTypes = entityType.GetDirectlyDerivedTypes().Any()
- ? !isTpc && mappedType == entityType
- : (bool?)null;
+ ? !isTpc && mappedType == entityType
+ : (bool?)null;
foreach (var fragment in mappedType.GetMappingFragments(StoreObjectType.View))
{
CreateViewMapping(
@@ -684,11 +688,13 @@ private static void CreateViewMapping(
};
var containerColumnName = mappedType.GetContainerColumnName();
+ var containerColumnType = mappedType.GetContainerColumnType();
if (!string.IsNullOrEmpty(containerColumnName))
{
CreateContainerColumn(
- view, containerColumnName, mappedType, relationalTypeMappingSource,
- static (c, t, m) => new JsonViewColumn(c, m.StoreType, (View)t, m));
+ view, containerColumnName, containerColumnType, mappedType, relationalTypeMappingSource,
+ static (colName, colType, table, mapping) => new JsonViewColumn(
+ colName, colType ?? mapping.StoreType, (View)table, mapping));
}
else
{
@@ -770,11 +776,9 @@ private static void AddSqlQueries(RelationalModel databaseModel, IEntityType ent
databaseModel.Queries.Add(mappedQuery.Name, sqlQuery);
}
- var queryMapping = new SqlQueryMapping(entityType, sqlQuery,
- includesDerivedTypes: entityType.GetDirectlyDerivedTypes().Any() ? true : null)
- {
- IsDefaultSqlQueryMapping = true
- };
+ var queryMapping = new SqlQueryMapping(
+ entityType, sqlQuery,
+ includesDerivedTypes: entityType.GetDirectlyDerivedTypes().Any() ? true : null) { IsDefaultSqlQueryMapping = true };
foreach (var property in mappedType.GetProperties())
{
@@ -909,11 +913,9 @@ private static FunctionMapping CreateFunctionMapping(
var storeFunction = GetOrCreateStoreFunction(dbFunction, model);
var mappedFunction = StoreObjectIdentifier.DbFunction(dbFunction.Name);
- var functionMapping = new FunctionMapping(entityType, storeFunction, dbFunction,
- includesDerivedTypes: entityType.GetDirectlyDerivedTypes().Any() ? true : null)
- {
- IsDefaultFunctionMapping = @default
- };
+ var functionMapping = new FunctionMapping(
+ entityType, storeFunction, dbFunction,
+ includesDerivedTypes: entityType.GetDirectlyDerivedTypes().Any() ? true : null) { IsDefaultFunctionMapping = @default };
foreach (var property in mappedType.GetProperties())
{
@@ -997,8 +999,8 @@ private static void AddStoredProcedures(
while (mappedType != null)
{
var includesDerivedTypes = entityType.GetDirectlyDerivedTypes().Any()
- ? !isTpc && mappedType == entityType
- : (bool?)null;
+ ? !isTpc && mappedType == entityType
+ : (bool?)null;
var tableMappings = GetTableMappings(entityType).Where(
m => m.Table.Name == mappedType.GetTableName()
@@ -1245,7 +1247,8 @@ static StoreStoredProcedure GetOrCreateStoreStoredProcedure(
var storeStoredProcedure = (StoreStoredProcedure?)storedProcedure.StoreStoredProcedure;
if (storeStoredProcedure == null)
{
- storeStoredProcedure = (StoreStoredProcedure?)databaseModel.FindStoredProcedure(storedProcedure.Name, storedProcedure.Schema);
+ storeStoredProcedure =
+ (StoreStoredProcedure?)databaseModel.FindStoredProcedure(storedProcedure.Name, storedProcedure.Schema);
if (storeStoredProcedure == null)
{
storeStoredProcedure = new StoreStoredProcedure(storedProcedure.Name, storedProcedure.Schema, databaseModel);
@@ -1359,8 +1362,8 @@ static StoreStoredProcedureResultColumn GetOrCreateStoreStoredProcedureResultCol
private static IEnumerable GetTableColumnMappings(IProperty property)
=> (IEnumerable?)property.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.TableColumnMappings)
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.TableColumnMappings)
+ ?? Enumerable.Empty();
private static IColumn? FindColumn(Table table, IProperty property)
=> GetTableColumnMappings(property)
@@ -1457,7 +1460,9 @@ private static void PopulateTableConfiguration(Table table, bool designTime)
if (designTime)
{
- foreach (var checkConstraint in includeInherited ? entityType.GetCheckConstraints() : entityType.GetDeclaredCheckConstraints())
+ foreach (var checkConstraint in includeInherited
+ ? entityType.GetCheckConstraints()
+ : entityType.GetDeclaredCheckConstraints())
{
var name = checkConstraint.GetName(storeObject);
if (name == null)
@@ -2063,7 +2068,7 @@ IEnumerable IRelationalModel.Views
IEnumerable IRelationalModel.Functions
{
[DebuggerStepThrough]
- get => Functions.OrderBy(f => f.Key).Select(t => t.Value);
+ get => Functions.OrderBy(f => f.Key, NamedListComparer.Instance).Select(t => t.Value);
}
IEnumerable IRelationalModel.StoredProcedures
diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs
index bc86bdd78c2..836b55f07d2 100644
--- a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs
+++ b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs
@@ -28,8 +28,5 @@ public static class RelationalPropertyExtensions
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public static bool IsOrdinalKeyProperty(this IReadOnlyProperty property)
- => property.FindContainingPrimaryKey() is { Properties.Count: > 1 }
- && !property.IsForeignKey()
- && property.ClrType == typeof(int)
- && property.GetJsonPropertyName() == null;
+ => property.Name == RelationalKeyDiscoveryConvention.SynthesizedOrdinalPropertyName;
}
diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs
index fea560ea1c9..3b0f0c41cc0 100644
--- a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs
+++ b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs
@@ -73,8 +73,10 @@ public override bool IsReadOnly
public virtual InternalRelationalPropertyOverridesBuilder Builder
{
[DebuggerStepThrough]
- get => _builder ?? throw new InvalidOperationException(CoreStrings.ObjectRemovedFromModel(
- $"{Property.Name} - {StoreObject.DisplayName()}"));
+ get => _builder
+ ?? throw new InvalidOperationException(
+ CoreStrings.ObjectRemovedFromModel(
+ $"{Property.Name} - {StoreObject.DisplayName()}"));
}
///
diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalTypeBaseExtensions.cs b/src/EFCore.Relational/Metadata/Internal/RelationalTypeBaseExtensions.cs
index 037cb568f89..d84babc6554 100644
--- a/src/EFCore.Relational/Metadata/Internal/RelationalTypeBaseExtensions.cs
+++ b/src/EFCore.Relational/Metadata/Internal/RelationalTypeBaseExtensions.cs
@@ -23,8 +23,8 @@ public static IEnumerable GetViewOrTableMappings(this ITypeBa
{
typeBase.Model.EnsureRelationalModel();
return (IEnumerable?)(typeBase.FindRuntimeAnnotationValue(
- RelationalAnnotationNames.ViewMappings)
- ?? typeBase.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableMappings))
- ?? Enumerable.Empty();
+ RelationalAnnotationNames.ViewMappings)
+ ?? typeBase.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableMappings))
+ ?? Enumerable.Empty();
}
}
diff --git a/src/EFCore.Relational/Metadata/Internal/SqlQuery.cs b/src/EFCore.Relational/Metadata/Internal/SqlQuery.cs
index 685655ca267..21346767cba 100644
--- a/src/EFCore.Relational/Metadata/Internal/SqlQuery.cs
+++ b/src/EFCore.Relational/Metadata/Internal/SqlQuery.cs
@@ -19,9 +19,7 @@ public class SqlQuery : TableBase, ISqlQuery
///
public SqlQuery(string name, RelationalModel model, string sql)
: base(name, null, model)
- {
- Sql = sql;
- }
+ => Sql = sql;
///
public virtual string Sql { get; set; }
diff --git a/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedure.cs b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedure.cs
index 4db631dcaa5..6abb728ddba 100644
--- a/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedure.cs
+++ b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedure.cs
@@ -103,9 +103,7 @@ public virtual void AddParameter(IStoreStoredProcedureParameter parameter)
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual IStoreStoredProcedureParameter? FindParameter(string name)
- => _parametersSet.TryGetValue(name, out var parameter)
- ? parameter
- : null;
+ => _parametersSet.GetValueOrDefault(name);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureResultColumn.cs b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureResultColumn.cs
index 29f1f4913c0..16624668886 100644
--- a/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureResultColumn.cs
+++ b/src/EFCore.Relational/Metadata/Internal/StoreStoredProcedureResultColumn.cs
@@ -25,9 +25,7 @@ public StoreStoredProcedureResultColumn(
StoreStoredProcedure storedProcedure,
RelationalTypeMapping? storeTypeMapping = null)
: base(name, type, storedProcedure, storeTypeMapping)
- {
- Position = position;
- }
+ => Position = position;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedure.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedure.cs
index de01c98664b..06f6fc25fe1 100644
--- a/src/EFCore.Relational/Metadata/Internal/StoredProcedure.cs
+++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedure.cs
@@ -505,9 +505,7 @@ public virtual IReadOnlyList Parameters
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual StoredProcedureParameter? FindParameter(string propertyName)
- => _currentValueParameters.TryGetValue(propertyName, out var parameter)
- ? parameter
- : null;
+ => _currentValueParameters.GetValueOrDefault(propertyName);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -538,9 +536,7 @@ public virtual StoredProcedureParameter AddParameter(string propertyName)
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual StoredProcedureParameter? FindOriginalValueParameter(string propertyName)
- => _originalValueParameters.TryGetValue(propertyName, out var parameter)
- ? parameter
- : null;
+ => _originalValueParameters.GetValueOrDefault(propertyName);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -616,9 +612,7 @@ public virtual IReadOnlyList ResultColumns
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual StoredProcedureResultColumn? FindResultColumn(string propertyName)
- => _propertyResultColumns.TryGetValue(propertyName, out var resultColumn)
- ? resultColumn
- : null;
+ => _propertyResultColumns.GetValueOrDefault(propertyName);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureComparer.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureComparer.cs
index edb4e5d6be6..07d03daa7f6 100644
--- a/src/EFCore.Relational/Metadata/Internal/StoredProcedureComparer.cs
+++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureComparer.cs
@@ -47,11 +47,11 @@ public int Compare(IStoredProcedure? x, IStoredProcedure? y)
return 1;
}
- var xId = x.GetStoreIdentifier();
- var yId = y.GetStoreIdentifier();
+ var xStoreId = x.GetStoreIdentifier();
+ var yStoreId = y.GetStoreIdentifier();
var result = 0;
- result = xId.StoreObjectType.CompareTo(yId.StoreObjectType);
+ result = xStoreId.StoreObjectType.CompareTo(yStoreId.StoreObjectType);
if (result != 0)
{
return result;
@@ -63,13 +63,13 @@ public int Compare(IStoredProcedure? x, IStoredProcedure? y)
return result;
}
- result = StringComparer.Ordinal.Compare(xId.Name, yId.Name);
+ result = StringComparer.Ordinal.Compare(xStoreId.Name, yStoreId.Name);
if (result != 0)
{
return result;
}
- result = StringComparer.Ordinal.Compare(xId.Schema, yId.Schema);
+ result = StringComparer.Ordinal.Compare(xStoreId.Schema, yStoreId.Schema);
if (result != 0)
{
return result;
diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameterMapping.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameterMapping.cs
index 3a07090020e..cb17a986501 100644
--- a/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameterMapping.cs
+++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameterMapping.cs
@@ -23,9 +23,7 @@ public StoredProcedureParameterMapping(
StoreStoredProcedureParameter storeParameter,
StoredProcedureMapping storedProcedureMapping)
: base(property, storeParameter, storedProcedureMapping)
- {
- Parameter = parameter;
- }
+ => Parameter = parameter;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumnMapping.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumnMapping.cs
index 51b40790312..367586abc02 100644
--- a/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumnMapping.cs
+++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumnMapping.cs
@@ -23,9 +23,7 @@ public StoredProcedureResultColumnMapping(
StoreStoredProcedureResultColumn storeResultColumn,
StoredProcedureMapping storedProcedureMapping)
: base(property, storeResultColumn, storedProcedureMapping)
- {
- ResultColumn = resultColumn;
- }
+ => ResultColumn = resultColumn;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Internal/Table.cs b/src/EFCore.Relational/Metadata/Internal/Table.cs
index 10d70265c92..9ba15edbe8b 100644
--- a/src/EFCore.Relational/Metadata/Internal/Table.cs
+++ b/src/EFCore.Relational/Metadata/Internal/Table.cs
@@ -21,9 +21,7 @@ public class Table : TableBase, ITable
///
public Table(string name, string? schema, RelationalModel model)
: base(name, schema, model)
- {
- Columns = new SortedDictionary(new ColumnNameComparer(this));
- }
+ => Columns = new SortedDictionary(new ColumnNameComparer(this));
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -109,9 +107,7 @@ public virtual UniqueConstraint? PrimaryKey
public virtual UniqueConstraint? FindUniqueConstraint(string name)
=> PrimaryKey != null && PrimaryKey.Name == name
? PrimaryKey
- : UniqueConstraints.TryGetValue(name, out var constraint)
- ? constraint
- : null;
+ : UniqueConstraints.GetValueOrDefault(name);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Internal/TableBase.cs b/src/EFCore.Relational/Metadata/Internal/TableBase.cs
index 43d36758ed2..d2accdc373d 100644
--- a/src/EFCore.Relational/Metadata/Internal/TableBase.cs
+++ b/src/EFCore.Relational/Metadata/Internal/TableBase.cs
@@ -86,9 +86,7 @@ public override bool IsReadOnly
///
public virtual IColumnBase? FindColumn(string name)
- => Columns.TryGetValue(name, out var column)
- ? column
- : null;
+ => Columns.GetValueOrDefault(name);
///
public virtual IColumnBase? FindColumn(IProperty property)
diff --git a/src/EFCore.Relational/Metadata/RelationalAdHocMapper.cs b/src/EFCore.Relational/Metadata/RelationalAdHocMapper.cs
index e59550c82b0..ade20fc2738 100644
--- a/src/EFCore.Relational/Metadata/RelationalAdHocMapper.cs
+++ b/src/EFCore.Relational/Metadata/RelationalAdHocMapper.cs
@@ -30,9 +30,7 @@ public class RelationalAdHocMapper : AdHocMapper
///
public RelationalAdHocMapper(AdHocMapperDependencies dependencies, RelationalAdHocMapperDependencies relationalDependencies)
: base(dependencies)
- {
- RelationalDependencies = relationalDependencies;
- }
+ => RelationalDependencies = relationalDependencies;
///
/// Relational-specific dependencies for this service.
diff --git a/src/EFCore.Relational/Metadata/RelationalAnnotationNames.cs b/src/EFCore.Relational/Metadata/RelationalAnnotationNames.cs
index 6041736eca8..efbf0a4e205 100644
--- a/src/EFCore.Relational/Metadata/RelationalAnnotationNames.cs
+++ b/src/EFCore.Relational/Metadata/RelationalAnnotationNames.cs
@@ -324,6 +324,11 @@ public static class RelationalAnnotationNames
///
public const string ContainerColumnName = Prefix + "ContainerColumnName";
+ ///
+ /// The column type for the container column to which the object is mapped.
+ ///
+ public const string ContainerColumnType = Prefix + nameof(ContainerColumnType);
+
///
/// The name for the annotation specifying container column type mapping.
///
@@ -368,9 +373,9 @@ public static class RelationalAnnotationNames
Collation,
DefaultSchema,
Name,
- #pragma warning disable CS0618 // Type or member is obsolete
+#pragma warning disable CS0618 // Type or member is obsolete
SequencePrefix,
- #pragma warning restore CS0618 // Type or member is obsolete
+#pragma warning restore CS0618 // Type or member is obsolete
Sequences,
CheckConstraints,
Filter,
@@ -408,9 +413,10 @@ public static class RelationalAnnotationNames
ModelDependencies,
FieldValueGetter,
ContainerColumnName,
- #pragma warning disable CS0618 // Type or member is obsolete
+ ContainerColumnType,
+#pragma warning disable CS0618 // Type or member is obsolete
ContainerColumnTypeMapping,
- #pragma warning restore CS0618 // Type or member is obsolete
+#pragma warning restore CS0618 // Type or member is obsolete
JsonPropertyName,
StoreType
};
diff --git a/src/EFCore.Relational/Metadata/RelationalAnnotationProvider.cs b/src/EFCore.Relational/Metadata/RelationalAnnotationProvider.cs
index 05bd8fa3605..b7e0bea889f 100644
--- a/src/EFCore.Relational/Metadata/RelationalAnnotationProvider.cs
+++ b/src/EFCore.Relational/Metadata/RelationalAnnotationProvider.cs
@@ -25,9 +25,7 @@ public class RelationalAnnotationProvider : IRelationalAnnotationProvider
///
/// Parameter object containing dependencies for this service.
public RelationalAnnotationProvider(RelationalAnnotationProviderDependencies dependencies)
- {
- Dependencies = dependencies;
- }
+ => Dependencies = dependencies;
///
/// Relational provider-specific dependencies for this service.
diff --git a/src/EFCore.Relational/Metadata/RuntimeDbFunction.cs b/src/EFCore.Relational/Metadata/RuntimeDbFunction.cs
index b2e62b25929..0f638a228d3 100644
--- a/src/EFCore.Relational/Metadata/RuntimeDbFunction.cs
+++ b/src/EFCore.Relational/Metadata/RuntimeDbFunction.cs
@@ -145,7 +145,8 @@ public virtual RuntimeDbFunctionParameter AddParameter(
/// 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 IStoreFunction? StoreFunction => _storeFunction;
+ public virtual IStoreFunction? StoreFunction
+ => _storeFunction;
///
/// Returns a string that represents the current object.
diff --git a/src/EFCore.Relational/Metadata/StoreObjectDictionary.cs b/src/EFCore.Relational/Metadata/StoreObjectDictionary.cs
index 84ede007aae..864b596eb55 100644
--- a/src/EFCore.Relational/Metadata/StoreObjectDictionary.cs
+++ b/src/EFCore.Relational/Metadata/StoreObjectDictionary.cs
@@ -14,9 +14,7 @@ public class StoreObjectDictionary : IReadOnlyStoreObjectDictionary
///
public virtual T? Find(in StoreObjectIdentifier storeObject)
- => _dictionary.TryGetValue(storeObject, out var value)
- ? value
- : null;
+ => _dictionary.GetValueOrDefault(storeObject);
///
public virtual IEnumerable GetValues()
diff --git a/src/EFCore.Relational/Migrations/HistoryRepository.cs b/src/EFCore.Relational/Migrations/HistoryRepository.cs
index e3d1a03e75f..bfb565786c0 100644
--- a/src/EFCore.Relational/Migrations/HistoryRepository.cs
+++ b/src/EFCore.Relational/Migrations/HistoryRepository.cs
@@ -47,6 +47,14 @@ protected HistoryRepository(HistoryRepositoryDependencies dependencies)
TableSchema = relationalOptions.MigrationsHistoryTableSchema;
}
+ ///
+ /// 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 LockReleaseBehavior LockReleaseBehavior { get; }
+
///
/// Relational provider-specific dependencies for this service.
///
@@ -120,14 +128,15 @@ protected virtual string ProductVersionColumnName
///
/// if the table already exists, otherwise.
public virtual bool Exists()
- => InterpretExistsResult(
- Dependencies.RawSqlCommandBuilder.Build(ExistsSql).ExecuteScalar(
- new RelationalCommandParameterObject(
- Dependencies.Connection,
- null,
- null,
- Dependencies.CurrentContext.Context,
- Dependencies.CommandLogger, CommandSource.Migrations)));
+ => Dependencies.DatabaseCreator.Exists()
+ && InterpretExistsResult(
+ Dependencies.RawSqlCommandBuilder.Build(ExistsSql).ExecuteScalar(
+ new RelationalCommandParameterObject(
+ Dependencies.Connection,
+ null,
+ null,
+ Dependencies.CurrentContext.Context,
+ Dependencies.CommandLogger, CommandSource.Migrations)));
///
/// Checks whether or not the history table exists.
@@ -139,15 +148,16 @@ public virtual bool Exists()
///
/// If the is canceled.
public virtual async Task ExistsAsync(CancellationToken cancellationToken = default)
- => InterpretExistsResult(
- await Dependencies.RawSqlCommandBuilder.Build(ExistsSql).ExecuteScalarAsync(
- new RelationalCommandParameterObject(
- Dependencies.Connection,
- null,
- null,
- Dependencies.CurrentContext.Context,
- Dependencies.CommandLogger, CommandSource.Migrations),
- cancellationToken).ConfigureAwait(false));
+ => await Dependencies.DatabaseCreator.ExistsAsync(cancellationToken).ConfigureAwait(false)
+ && InterpretExistsResult(
+ await Dependencies.RawSqlCommandBuilder.Build(ExistsSql).ExecuteScalarAsync(
+ new RelationalCommandParameterObject(
+ Dependencies.Connection,
+ null,
+ null,
+ Dependencies.CurrentContext.Context,
+ Dependencies.CommandLogger, CommandSource.Migrations),
+ cancellationToken).ConfigureAwait(false));
///
/// Interprets the result of executing .
@@ -173,13 +183,15 @@ public virtual string GetCreateScript()
/// Creates the history table.
///
public virtual void Create()
- => Dependencies.MigrationCommandExecutor.ExecuteNonQuery(GetCreateCommands(), Dependencies.Connection);
+ => Dependencies.MigrationCommandExecutor.ExecuteNonQuery(
+ GetCreateCommands(), Dependencies.Connection, new MigrationExecutionState(), commitTransaction: true);
///
/// Creates the history table.
///
public virtual Task CreateAsync(CancellationToken cancellationToken = default)
- => Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(GetCreateCommands(), Dependencies.Connection, cancellationToken);
+ => Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(
+ GetCreateCommands(), Dependencies.Connection, new MigrationExecutionState(), commitTransaction: true, cancellationToken: cancellationToken);
///
/// Returns the migration commands that will create the history table.
@@ -194,21 +206,37 @@ protected virtual IReadOnlyList GetCreateCommands()
return commandList;
}
+ bool IHistoryRepository.CreateIfNotExists()
+ => Dependencies.MigrationCommandExecutor.ExecuteNonQuery(
+ GetCreateIfNotExistsCommands(), Dependencies.Connection, new MigrationExecutionState(), commitTransaction: true)
+ != 0;
+
+ async Task IHistoryRepository.CreateIfNotExistsAsync(CancellationToken cancellationToken)
+ => (await Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(
+ GetCreateIfNotExistsCommands(), Dependencies.Connection, new MigrationExecutionState(), commitTransaction: true, cancellationToken: cancellationToken).ConfigureAwait(false))
+ != 0;
+
+ private IReadOnlyList GetCreateIfNotExistsCommands()
+ => Dependencies.MigrationsSqlGenerator.Generate([new SqlOperation
+ {
+ Sql = GetCreateIfNotExistsScript(),
+ SuppressTransaction = true
+ }]);
+
///
/// Gets an exclusive lock on the database.
///
- /// The time to wait for the lock before an exception is thrown.
/// An object that can be disposed to release the lock.
- public abstract IDisposable GetDatabaseLock(TimeSpan timeout);
+ public abstract IMigrationsDatabaseLock AcquireDatabaseLock();
///
/// Gets an exclusive lock on the database.
///
- /// The time to wait for the lock before an exception is thrown.
/// A to observe while waiting for the task to complete.
+ ///
/// An object that can be disposed to release the lock.
/// If the is canceled.
- public abstract Task GetDatabaseLockAsync(TimeSpan timeout, CancellationToken cancellationToken = default);
+ public abstract Task AcquireDatabaseLockAsync(CancellationToken cancellationToken = default);
///
/// Configures the entity type mapped to the history table.
diff --git a/src/EFCore.Relational/Migrations/HistoryRepositoryDependencies.cs b/src/EFCore.Relational/Migrations/HistoryRepositoryDependencies.cs
index b5f40bd8ba2..a8bf28d3f22 100644
--- a/src/EFCore.Relational/Migrations/HistoryRepositoryDependencies.cs
+++ b/src/EFCore.Relational/Migrations/HistoryRepositoryDependencies.cs
@@ -59,7 +59,8 @@ public HistoryRepositoryDependencies(
IRelationalTypeMappingSource typeMappingSource,
ICurrentDbContext currentContext,
IModelRuntimeInitializer modelRuntimeInitializer,
- IRelationalCommandDiagnosticsLogger commandLogger)
+ IRelationalCommandDiagnosticsLogger commandLogger,
+ IDiagnosticsLogger migrationsLogger)
{
DatabaseCreator = databaseCreator;
RawSqlCommandBuilder = rawSqlCommandBuilder;
@@ -75,6 +76,7 @@ public HistoryRepositoryDependencies(
CurrentContext = currentContext;
ModelRuntimeInitializer = modelRuntimeInitializer;
CommandLogger = commandLogger;
+ MigrationsLogger = migrationsLogger;
}
///
@@ -146,4 +148,9 @@ public HistoryRepositoryDependencies(
/// The command logger
///
public IRelationalCommandDiagnosticsLogger CommandLogger { get; init; }
+
+ ///
+ /// The migrations logger
+ ///
+ public IDiagnosticsLogger MigrationsLogger { get; init; }
}
diff --git a/src/EFCore.Relational/Migrations/IHistoryRepository.cs b/src/EFCore.Relational/Migrations/IHistoryRepository.cs
index 4d52db38043..2189e6cea87 100644
--- a/src/EFCore.Relational/Migrations/IHistoryRepository.cs
+++ b/src/EFCore.Relational/Migrations/IHistoryRepository.cs
@@ -50,12 +50,44 @@ public interface IHistoryRepository
///
/// A to observe while waiting for the task to complete.
///
- /// A task that represents the asynchronous operation. The task result contains
- /// if the table already exists, otherwise.
+ /// A task that represents the asynchronous operation.
///
/// If the is canceled.
Task CreateAsync(CancellationToken cancellationToken = default);
+ ///
+ /// Creates the history table if it doesn't exist.
+ ///
+ /// if the table was created, otherwise.
+ bool CreateIfNotExists()
+ {
+ if (!Exists())
+ {
+ Create();
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// Creates the history table.
+ ///
+ /// A to observe while waiting for the task to complete.
+ ///
+ /// A task that represents the asynchronous operation. The task result contains
+ /// if the table was created, otherwise.
+ ///
+ /// If the is canceled.
+ async Task CreateIfNotExistsAsync(CancellationToken cancellationToken = default)
+ {
+ if (!await ExistsAsync(cancellationToken).ConfigureAwait(false))
+ {
+ await CreateAsync(cancellationToken).ConfigureAwait(false);
+ return true;
+ }
+ return false;
+ }
+
///
/// Queries the history table for all migrations that have been applied.
///
@@ -75,20 +107,23 @@ Task> GetAppliedMigrationsAsync(
CancellationToken cancellationToken = default);
///
- /// Gets an exclusive lock on the database.
+ /// The condition under witch the lock is released implicitly.
+ ///
+ LockReleaseBehavior LockReleaseBehavior { get; }
+
+ ///
+ /// Acquires an exclusive lock on the database.
///
- /// The time to wait for the lock before an exception is thrown.
/// An object that can be disposed to release the lock.
- IDisposable GetDatabaseLock(TimeSpan timeout);
+ IMigrationsDatabaseLock AcquireDatabaseLock();
///
- /// Gets an exclusive lock on the database.
+ /// Acquires an exclusive lock on the database asynchronously.
///
- /// The time to wait for the lock before an exception is thrown.
/// A to observe while waiting for the task to complete.
/// An object that can be disposed to release the lock.
/// If the is canceled.
- Task GetDatabaseLockAsync(TimeSpan timeout, CancellationToken cancellationToken = default);
+ Task AcquireDatabaseLockAsync(CancellationToken cancellationToken = default);
///
/// Generates a SQL script that will create the history table.
diff --git a/src/EFCore.Relational/Migrations/IMigrationCommandExecutor.cs b/src/EFCore.Relational/Migrations/IMigrationCommandExecutor.cs
index 7069ba9c045..039bbaa4f15 100644
--- a/src/EFCore.Relational/Migrations/IMigrationCommandExecutor.cs
+++ b/src/EFCore.Relational/Migrations/IMigrationCommandExecutor.cs
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Data;
+
namespace Microsoft.EntityFrameworkCore.Migrations;
///
@@ -28,6 +30,24 @@ void ExecuteNonQuery(
IEnumerable migrationCommands,
IRelationalConnection connection);
+ ///
+ /// Executes the given commands using the given database connection.
+ ///
+ /// The commands to execute.
+ /// The connection to use.
+ /// The state of the current migration execution.
+ ///
+ /// Indicates whether the transaction started by this call should be commited.
+ /// If , the transaction will be made available in .
+ ///
+ /// The isolation level for the transaction.
+ int ExecuteNonQuery(
+ IReadOnlyList migrationCommands,
+ IRelationalConnection connection,
+ MigrationExecutionState executionState,
+ bool commitTransaction,
+ IsolationLevel? isolationLevel = null);
+
///
/// Executes the given commands using the given database connection.
///
@@ -40,4 +60,26 @@ Task ExecuteNonQueryAsync(
IEnumerable migrationCommands,
IRelationalConnection connection,
CancellationToken cancellationToken = default);
+
+ ///
+ /// Executes the given commands using the given database connection.
+ ///
+ /// The commands to execute.
+ /// The connection to use.
+ /// The state of the current migration execution.
+ ///
+ /// Indicates whether the transaction started by this call should be commited.
+ /// If , the transaction will be made available in .
+ ///
+ /// The isolation level for the transaction.
+ /// A to observe while waiting for the task to complete.
+ /// A task that represents the asynchronous operation.
+ /// If the is canceled.
+ Task ExecuteNonQueryAsync(
+ IReadOnlyList migrationCommands,
+ IRelationalConnection connection,
+ MigrationExecutionState executionState,
+ bool commitTransaction,
+ IsolationLevel? isolationLevel = null,
+ CancellationToken cancellationToken = default);
}
diff --git a/src/EFCore.Relational/Migrations/IMigrationsDatabaseLock.cs b/src/EFCore.Relational/Migrations/IMigrationsDatabaseLock.cs
new file mode 100644
index 00000000000..b08ea1b9faa
--- /dev/null
+++ b/src/EFCore.Relational/Migrations/IMigrationsDatabaseLock.cs
@@ -0,0 +1,62 @@
+// 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.Migrations;
+
+///
+/// Represents an exclusive lock on the database that is used to ensure that only one migration application can be run at a time.
+///
+///
+/// Typically only database providers implement this.
+///
+public interface IMigrationsDatabaseLock : IDisposable, IAsyncDisposable
+{
+ ///
+ /// The history repository.
+ ///
+ protected IHistoryRepository HistoryRepository { get; }
+
+ ///
+ /// Acquires an exclusive lock on the database again if the current one was already released.
+ ///
+ /// Indicates whether the connection was reopened.
+ ///
+ /// Indicates whether the transaction was restarted.
+ /// if there's no current transaction.
+ ///
+ /// An object that can be disposed to release the lock.
+ IMigrationsDatabaseLock ReacquireIfNeeded(bool connectionReopened, bool? transactionRestarted)
+ {
+ if ((connectionReopened && HistoryRepository.LockReleaseBehavior == LockReleaseBehavior.Connection)
+ || (transactionRestarted is true && HistoryRepository.LockReleaseBehavior == LockReleaseBehavior.Transaction))
+ {
+ Dispose();
+ return HistoryRepository.AcquireDatabaseLock();
+ }
+
+ return this;
+ }
+
+ ///
+ /// Acquires an exclusive lock on the database again, if the current one was already released.
+ ///
+ /// Indicates whether the connection was reopened.
+ ///
+ /// Indicates whether the transaction was restarted.
+ /// if there's no current transaction.
+ ///
+ /// A to observe while waiting for the task to complete.
+ /// An object that can be disposed to release the lock.
+ async Task ReacquireIfNeededAsync(
+ bool connectionReopened, bool? transactionRestarted, CancellationToken cancellationToken = default)
+ {
+ if ((connectionReopened && HistoryRepository.LockReleaseBehavior == LockReleaseBehavior.Connection)
+ || (transactionRestarted is true && HistoryRepository.LockReleaseBehavior == LockReleaseBehavior.Transaction))
+ {
+ await DisposeAsync().ConfigureAwait(false);
+ return await HistoryRepository.AcquireDatabaseLockAsync(cancellationToken).ConfigureAwait(false);
+ }
+
+ return this;
+ }
+}
diff --git a/src/EFCore.Relational/Migrations/IMigrator.cs b/src/EFCore.Relational/Migrations/IMigrator.cs
index 0b02cbe7df3..b7735b4f0f7 100644
--- a/src/EFCore.Relational/Migrations/IMigrator.cs
+++ b/src/EFCore.Relational/Migrations/IMigrator.cs
@@ -26,37 +26,23 @@ public interface IMigrator
/// Migrates the database to either a specified target migration or up to the latest
/// migration that exists in the .
///
- ///
- /// The optional seed method to run after migrating the database. It will be invoked even if no migrations were applied.
- ///
///
/// The target migration to migrate the database to, or to migrate to the latest.
///
- ///
- /// The maximum amount of time that the migration lock should be held. Unless a catastrophic failure occurs, the
- /// lock is released when the migration operation completes.
- ///
///
/// See Database migrations for more information and examples.
///
[RequiresUnreferencedCode("Migration generation currently isn't compatible with trimming")]
[RequiresDynamicCode("Migrations operations are not supported with NativeAOT")]
- void Migrate(Action? seed = null, string? targetMigration = null, TimeSpan? lockTimeout = null);
+ void Migrate(string? targetMigration = null);
///
/// Migrates the database to either a specified target migration or up to the latest
/// migration that exists in the .
///
- ///
- /// The optional seed method to run after migrating the database. It will be invoked even if no migrations were applied.
- ///
///
/// The target migration to migrate the database to, or to migrate to the latest.
///
- ///
- /// The maximum amount of time that the migration lock should be held. Unless a catastrophic failure occurs, the
- /// lock is released when the migration operation completes.
- ///
/// A to observe while waiting for the task to complete.
/// A task that represents the asynchronous operation
///
@@ -66,9 +52,7 @@ public interface IMigrator
[RequiresUnreferencedCode("Migration generation currently isn't compatible with trimming")]
[RequiresDynamicCode("Migrations operations are not supported with NativeAOT")]
Task MigrateAsync(
- Func? seed = null,
string? targetMigration = null,
- TimeSpan? lockTimeout = null,
CancellationToken cancellationToken = default);
///
diff --git a/src/EFCore.Relational/Migrations/IMigratorData.cs b/src/EFCore.Relational/Migrations/IMigratorData.cs
deleted file mode 100644
index 60531a1c699..00000000000
--- a/src/EFCore.Relational/Migrations/IMigratorData.cs
+++ /dev/null
@@ -1,29 +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.Migrations;
-
-///
-/// A class that holds the results from the last migrations application.
-///
-///
-/// See Database migrations for more information and examples.
-///
-public interface IMigratorData
-{
- ///
- /// The migrations that were applied to the database.
- ///
- public IReadOnlyList AppliedMigrations { get; }
-
- ///
- /// The migrations that were reverted from the database.
- ///
- public IReadOnlyList RevertedMigrations { get; }
-
- ///
- /// The target migration.
- /// if all migrations were reverted or no target migration was specified.
- ///
- public Migration? TargetMigration { get; }
-}
diff --git a/src/EFCore.Relational/Migrations/IMigratorPlugin.cs b/src/EFCore.Relational/Migrations/IMigratorPlugin.cs
deleted file mode 100644
index ae06c4d968e..00000000000
--- a/src/EFCore.Relational/Migrations/IMigratorPlugin.cs
+++ /dev/null
@@ -1,73 +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.Migrations;
-
-///
-///
-/// A service on the EF internal service provider that allows providers or extensions to execute logic
-/// after is called.
-///
-///
-/// This type is typically used by providers or extensions. It is generally not used in application code.
-///
-///
-///
-/// The service lifetime is . This means a single instance
-/// is used by many instances. The implementation must be thread-safe.
-/// This service cannot depend on services registered as .
-///
-public interface IMigratorPlugin
-{
- ///
- /// Called by before applying the migrations.
- ///
- /// The that is being migrated.
- /// The that contains the result of the migrations application.
- ///
- /// See Database migrations for more information and examples.
- ///
- void Migrating(DbContext context, IMigratorData data);
-
- ///
- /// Called by before applying the migrations.
- ///
- /// The that is being migrated.
- /// The that contains the result of the migrations application.
- ///
- /// See Database migrations for more information and examples.
- ///
- /// A to observe while waiting for the task to complete.
- /// A task that represents the asynchronous operation
- /// If the is canceled.
- Task MigratingAsync(
- DbContext context,
- IMigratorData data,
- CancellationToken cancellationToken = default);
-
- ///
- /// Called by after applying the migrations, but before the seeding action.
- ///
- /// The that is being migrated.
- /// The that contains the result of the migrations application.
- ///
- /// See Database migrations for more information and examples.
- ///
- void Migrated(DbContext context, IMigratorData data);
-
- ///
- /// Called by after applying the migrations, but before the seeding action.
- ///
- /// The that is being migrated.
- /// The that contains the result of the migrations application.
- ///
- /// See Database migrations for more information and examples.
- ///
- /// A to observe while waiting for the task to complete.
- /// A task that represents the asynchronous operation
- /// If the is canceled.
- Task MigratedAsync(
- DbContext context,
- IMigratorData data,
- CancellationToken cancellationToken = default);
-}
diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationCommandExecutor.cs b/src/EFCore.Relational/Migrations/Internal/MigrationCommandExecutor.cs
index e545d4c13b5..68c6ba4a886 100644
--- a/src/EFCore.Relational/Migrations/Internal/MigrationCommandExecutor.cs
+++ b/src/EFCore.Relational/Migrations/Internal/MigrationCommandExecutor.cs
@@ -11,20 +11,25 @@ namespace Microsoft.EntityFrameworkCore.Migrations.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 class MigrationCommandExecutor : IMigrationCommandExecutor
+///
+/// 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 class MigrationCommandExecutor(IExecutionStrategy executionStrategy) : IMigrationCommandExecutor
{
- private readonly IExecutionStrategy _executionStrategy;
-
///
/// 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 MigrationCommandExecutor(IExecutionStrategy executionStrategy)
- {
- _executionStrategy = executionStrategy;
- }
+ public virtual void ExecuteNonQuery(
+ IEnumerable migrationCommands,
+ IRelationalConnection connection)
+ => ExecuteNonQuery(
+ migrationCommands.ToList(), connection, new MigrationExecutionState(), commitTransaction: true);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -32,75 +37,111 @@ public MigrationCommandExecutor(IExecutionStrategy executionStrategy)
/// 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 ExecuteNonQuery(
- IEnumerable migrationCommands,
- IRelationalConnection connection)
+ public virtual int ExecuteNonQuery(
+ IReadOnlyList migrationCommands,
+ IRelationalConnection connection,
+ MigrationExecutionState executionState,
+ bool commitTransaction,
+ System.Data.IsolationLevel? isolationLevel = null)
{
- var userTransaction = connection.CurrentTransaction;
- if (userTransaction is not null
- && (migrationCommands.Any(x => x.TransactionSuppressed) || _executionStrategy.RetriesOnFailure))
+ var inUserTransaction = connection.CurrentTransaction is not null && executionState.Transaction == null;
+ if (inUserTransaction
+ && (migrationCommands.Any(x => x.TransactionSuppressed) || executionStrategy.RetriesOnFailure))
{
throw new NotSupportedException(RelationalStrings.TransactionSuppressedMigrationInUserTransaction);
}
- using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled))
- {
- var parameters = new ExecuteParameters(migrationCommands.ToList(), connection);
- if (userTransaction is null)
- {
- _executionStrategy.Execute(parameters, static (_, p) => Execute(p, beginTransaction: true), verifySucceeded: null);
- }
- else
- {
- Execute(parameters, beginTransaction: false);
- }
- }
+ using var transactionScope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled);
+
+ return executionStrategy.Execute(
+ (migrationCommands, connection, inUserTransaction, executionState, commitTransaction, isolationLevel),
+ static (_, s) => Execute(
+ s.migrationCommands,
+ s.connection,
+ s.executionState,
+ beginTransaction: !s.inUserTransaction,
+ commitTransaction: !s.inUserTransaction && s.commitTransaction,
+ s.isolationLevel),
+ verifySucceeded: null);
}
- private static bool Execute(ExecuteParameters parameters, bool beginTransaction)
+ private static int Execute(
+ IReadOnlyList migrationCommands,
+ IRelationalConnection connection,
+ MigrationExecutionState executionState,
+ bool beginTransaction,
+ bool commitTransaction,
+ System.Data.IsolationLevel? isolationLevel)
{
- var migrationCommands = parameters.MigrationCommands;
- var connection = parameters.Connection;
- IDbContextTransaction? transaction = null;
- connection.Open();
+ var result = 0;
+ var connectionOpened = connection.Open();
+ Check.DebugAssert(!connectionOpened || executionState.Transaction == null,
+ "executionState.Transaction should be null");
+
try
{
- for (var i = parameters.CurrentCommandIndex; i < migrationCommands.Count; i++)
+ for (var i = executionState.LastCommittedCommandIndex; i < migrationCommands.Count; i++)
{
var command = migrationCommands[i];
- if (transaction == null
+ if (executionState.Transaction == null
&& !command.TransactionSuppressed
&& beginTransaction)
{
- transaction = connection.BeginTransaction();
+ executionState.Transaction = isolationLevel == null
+ ? connection.BeginTransaction()
+ : connection.BeginTransaction(isolationLevel.Value);
+ if (executionState.DatabaseLock != null)
+ {
+ executionState.DatabaseLock = executionState.DatabaseLock.ReacquireIfNeeded(
+ connectionOpened, transactionRestarted: true);
+ connectionOpened = false;
+ }
}
- if (transaction != null
+ if (executionState.Transaction != null
&& command.TransactionSuppressed)
{
- transaction.Commit();
- transaction.Dispose();
- transaction = null;
- parameters.CurrentCommandIndex = i;
+ executionState.Transaction.Commit();
+ executionState.Transaction.Dispose();
+ executionState.Transaction = null;
+ executionState.LastCommittedCommandIndex = i;
+ executionState.AnyOperationPerformed = true;
+
+ if (executionState.DatabaseLock != null)
+ {
+ executionState.DatabaseLock = executionState.DatabaseLock.ReacquireIfNeeded(
+ connectionOpened, transactionRestarted: null);
+ connectionOpened = false;
+ }
}
- command.ExecuteNonQuery(connection);
+ result = command.ExecuteNonQuery(connection);
- if (transaction == null)
+ if (executionState.Transaction == null)
{
- parameters.CurrentCommandIndex = i + 1;
+ executionState.LastCommittedCommandIndex = i + 1;
+ executionState.AnyOperationPerformed = true;
}
}
- transaction?.Commit();
+ if (commitTransaction
+ && executionState.Transaction != null)
+ {
+ executionState.Transaction.Commit();
+ executionState.Transaction.Dispose();
+ executionState.Transaction = null;
+ }
}
- finally
+ catch
{
- transaction?.Dispose();
+ executionState.Transaction?.Dispose();
+ executionState.Transaction = null;
connection.Close();
+ throw;
}
- return true;
+ connection.Close();
+ return result;
}
///
@@ -109,101 +150,136 @@ private static bool Execute(ExecuteParameters parameters, bool beginTransaction)
/// 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 async Task ExecuteNonQueryAsync(
+ public virtual Task ExecuteNonQueryAsync(
IEnumerable migrationCommands,
IRelationalConnection connection,
CancellationToken cancellationToken = default)
+ => ExecuteNonQueryAsync(
+ migrationCommands.ToList(), connection, new MigrationExecutionState(), commitTransaction: true, System.Data.IsolationLevel.Unspecified, cancellationToken);
+
+ ///
+ /// 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 async Task ExecuteNonQueryAsync(
+ IReadOnlyList migrationCommands,
+ IRelationalConnection connection,
+ MigrationExecutionState executionState,
+ bool commitTransaction,
+ System.Data.IsolationLevel? isolationLevel = null,
+ CancellationToken cancellationToken = default)
{
- var userTransaction = connection.CurrentTransaction;
- if (userTransaction is not null
- && (migrationCommands.Any(x => x.TransactionSuppressed) || _executionStrategy.RetriesOnFailure))
+ var inUserTransaction = connection.CurrentTransaction is not null && executionState.Transaction == null;
+ if (inUserTransaction
+ && (migrationCommands.Any(x => x.TransactionSuppressed) || executionStrategy.RetriesOnFailure))
{
throw new NotSupportedException(RelationalStrings.TransactionSuppressedMigrationInUserTransaction);
}
- var transactionScope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled);
- try
- {
- var parameters = new ExecuteParameters(migrationCommands.ToList(), connection);
- if (userTransaction is null)
- {
- await _executionStrategy.ExecuteAsync(
- parameters,
- static (_, p, ct) => ExecuteAsync(p, beginTransaction: true, ct),
- verifySucceeded: null,
- cancellationToken).ConfigureAwait(false);
- }
- else
- {
- await ExecuteAsync(parameters, beginTransaction: false, cancellationToken).ConfigureAwait(false);
- }
+ using var transactionScope = new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled);
- }
- finally
- {
- await transactionScope.DisposeAsyncIfAvailable().ConfigureAwait(false);
- }
+ return await executionStrategy.ExecuteAsync(
+ (migrationCommands, connection, inUserTransaction, executionState, commitTransaction, isolationLevel),
+ static (_, s, ct) => ExecuteAsync(
+ s.migrationCommands,
+ s.connection,
+ s.executionState,
+ beginTransaction: !s.inUserTransaction,
+ commitTransaction: !s.inUserTransaction && s.commitTransaction,
+ s.isolationLevel,
+ ct),
+ verifySucceeded: null,
+ cancellationToken).ConfigureAwait(false);
}
- private static async Task ExecuteAsync(ExecuteParameters parameters, bool beginTransaction, CancellationToken cancellationToken)
+ private static async Task ExecuteAsync(
+ IReadOnlyList migrationCommands,
+ IRelationalConnection connection,
+ MigrationExecutionState executionState,
+ bool beginTransaction,
+ bool commitTransaction,
+ System.Data.IsolationLevel? isolationLevel,
+ CancellationToken cancellationToken)
{
- var migrationCommands = parameters.MigrationCommands;
- var connection = parameters.Connection;
- IDbContextTransaction? transaction = null;
- await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
+ var result = 0;
+ var connectionOpened = await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
+ Check.DebugAssert(!connectionOpened || executionState.Transaction == null,
+ "executionState.Transaction should be null");
+
try
{
- for (var i = parameters.CurrentCommandIndex; i < migrationCommands.Count; i++)
+ for (var i = executionState.LastCommittedCommandIndex; i < migrationCommands.Count; i++)
{
+ var lockReacquired = false;
var command = migrationCommands[i];
- if (transaction == null
+ if (executionState.Transaction == null
&& !command.TransactionSuppressed
&& beginTransaction)
{
- transaction = await connection.BeginTransactionAsync(cancellationToken)
+ executionState.Transaction = await (isolationLevel == null
+ ? connection.BeginTransactionAsync(cancellationToken)
+ : connection.BeginTransactionAsync(isolationLevel.Value, cancellationToken))
.ConfigureAwait(false);
+
+ if (executionState.DatabaseLock != null)
+ {
+ executionState.DatabaseLock = await executionState.DatabaseLock.ReacquireIfNeededAsync(
+ connectionOpened, transactionRestarted: true, cancellationToken)
+ .ConfigureAwait(false);
+ lockReacquired = true;
+ }
}
- if (transaction != null
+ if (executionState.Transaction != null
&& command.TransactionSuppressed)
{
- await transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
- await transaction.DisposeAsync().ConfigureAwait(false);
- transaction = null;
- parameters.CurrentCommandIndex = i;
+ await executionState.Transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
+ await executionState.Transaction.DisposeAsync().ConfigureAwait(false);
+ executionState.Transaction = null;
+ executionState.LastCommittedCommandIndex = i;
+ executionState.AnyOperationPerformed = true;
+
+ if (executionState.DatabaseLock != null
+ && !lockReacquired)
+ {
+ executionState.DatabaseLock = await executionState.DatabaseLock.ReacquireIfNeededAsync(
+ connectionOpened, transactionRestarted: null, cancellationToken)
+ .ConfigureAwait(false);
+ }
}
- await command.ExecuteNonQueryAsync(connection, cancellationToken: cancellationToken)
+ result = await command.ExecuteNonQueryAsync(connection, cancellationToken: cancellationToken)
.ConfigureAwait(false);
- if (transaction == null)
+ if (executionState.Transaction == null)
{
- parameters.CurrentCommandIndex = i + 1;
+ executionState.LastCommittedCommandIndex = i + 1;
+ executionState.AnyOperationPerformed = true;
}
}
- if (transaction != null)
+ if (commitTransaction
+ && executionState.Transaction != null)
{
- await transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
+ await executionState.Transaction.CommitAsync(cancellationToken).ConfigureAwait(false);
+ await executionState.Transaction.DisposeAsync().ConfigureAwait(false);
+ executionState.Transaction = null;
}
}
- finally
+ catch
{
- if (transaction != null)
+ if (executionState.Transaction != null)
{
- await transaction.DisposeAsync().ConfigureAwait(false);
+ await executionState.Transaction.DisposeAsync().ConfigureAwait(false);
+ executionState.Transaction = null;
}
-
await connection.CloseAsync().ConfigureAwait(false);
+ throw;
}
- return true;
- }
-
- private sealed class ExecuteParameters(List migrationCommands, IRelationalConnection connection)
- {
- public int CurrentCommandIndex;
- public List