diff --git a/.config/guardian/.gdnbaselines b/.config/guardian/.gdnbaselines
new file mode 100644
index 00000000000..f8d9ac1b5c3
--- /dev/null
+++ b/.config/guardian/.gdnbaselines
@@ -0,0 +1,33 @@
+{
+ "hydrated": false,
+ "properties": {
+ "helpUri": "https://eng.ms/docs/microsoft-security/security/azure-security/cloudai-security-fundamentals-engineering/security-integration/guardian-wiki/microsoft-guardian/general/baselines",
+ "hydrationStatus": "This file does not contain identifying data. It is safe to check into your repo. To hydrate this file with identifying data, run `guardian hydrate --help` and follow the guidance."
+ },
+ "version": "1.0.0",
+ "baselines": {
+ "default": {
+ "name": "default",
+ "createdDate": "2024-03-26 04:31:45Z",
+ "lastUpdatedDate": "2024-03-26 04:31:45Z"
+ }
+ },
+ "results": {
+ "669098c2981b719b462da19de85b79975f42c84324e2a97495a071d3be667770": {
+ "signature": "669098c2981b719b462da19de85b79975f42c84324e2a97495a071d3be667770",
+ "alternativeSignatures": [],
+ "memberOf": [
+ "default"
+ ],
+ "createdDate": "2024-03-26 04:31:45Z"
+ },
+ "07b47302c50eb284f09501218f012eb0ea54edf3d8ae1a3737f1bd5083da699a": {
+ "signature": "07b47302c50eb284f09501218f012eb0ea54edf3d8ae1a3737f1bd5083da699a",
+ "alternativeSignatures": [],
+ "memberOf": [
+ "default"
+ ],
+ "createdDate": "2024-03-26 04:31:45Z"
+ }
+ }
+}
\ No newline at end of file
diff --git a/.config/tsaoptions.json b/.config/tsaoptions.json
index a0aae9cb757..f4ade5f9fe7 100644
--- a/.config/tsaoptions.json
+++ b/.config/tsaoptions.json
@@ -4,7 +4,7 @@
"instanceUrl": "https://devdiv.visualstudio.com/",
"iterationPath": "DevDiv",
"notificationAliases": [
- "aspnetcore-build@microsoft.com"
+ "efdatateam@microsoft.com"
],
"projectName": "DEVDIV",
"repositoryName": "efcore",
diff --git a/Directory.Build.targets b/Directory.Build.targets
index 58605e9ceb0..4aec99516ab 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -2,18 +2,16 @@
+ PACKAGE.md
+
+
+
+
-
-
-
-
-
+
@@ -19,7 +19,7 @@
-
+
diff --git a/azure-pipelines-public.yml b/azure-pipelines-public.yml
new file mode 100644
index 00000000000..aa704ddcde6
--- /dev/null
+++ b/azure-pipelines-public.yml
@@ -0,0 +1,316 @@
+schedules:
+- cron: 0 9 * * 1
+ displayName: "Run CodeQL3000 weekly, Monday at 2:00 AM PDT"
+ branches:
+ include:
+ - release/2.1
+ - release/6.0
+ - release/7.0
+ - main
+ always: true
+
+parameters:
+ # Parameter below is ignored in public builds.
+ #
+ # Choose whether to run the CodeQL3000 tasks.
+ # Manual builds align w/ official builds unless this parameter is true.
+ - name: runCodeQL3000
+ default: false
+ displayName: Run CodeQL3000 tasks
+ type: boolean
+
+variables:
+ - name: _BuildConfig
+ value: Release
+ - name: _TeamName
+ value: AspNetCore
+ - name: DOTNET_SKIP_FIRST_TIME_EXPERIENCE
+ value: true
+ - name: _PublishUsingPipelines
+ value: true
+ - name: _CosmosConnectionUrl
+ value: https://localhost:8081
+ - name: _CosmosToken
+ value: C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==
+ - ${{ if or(startswith(variables['Build.SourceBranch'], 'refs/heads/release/'), startswith(variables['Build.SourceBranch'], 'refs/heads/internal/release/'), eq(variables['Build.Reason'], 'Manual')) }}:
+ - name: PostBuildSign
+ value: false
+ - ${{ else }}:
+ - name: PostBuildSign
+ value: true
+ - ${{ if ne(variables['System.TeamProject'], 'public') }}:
+ - group: DotNet-HelixApi-Access
+ - group: DotNetBuilds storage account read tokens
+ - group: AzureDevOps-Artifact-Feeds-Pats
+ - name: _InternalRuntimeDownloadArgs
+ value: /p:DotNetRuntimeSourceFeed=https://dotnetbuilds.blob.core.windows.net/internal
+ /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64)
+ - ${{ if eq(variables['System.TeamProject'], 'public') }}:
+ - name: _InternalRuntimeDownloadArgs
+ value: ''
+ - name: LC_ALL
+ value: 'en_US.UTF-8'
+ - name: LANG
+ value: 'en_US.UTF-8'
+ - name: LANGUAGE
+ value: 'en_US.UTF-8'
+ - name: runCodeQL3000
+ value: ${{ and(ne(variables['System.TeamProject'], 'public'), or(eq(variables['Build.Reason'], 'Schedule'), and(eq(variables['Build.Reason'], 'Manual'), eq(parameters.runCodeQL3000, 'true')))) }}
+ - template: /eng/common/templates/variables/pool-providers.yml
+
+trigger:
+ batch: true
+ branches:
+ include:
+ - main
+ - release/*
+ - feature/*
+ - internal/release/*
+
+pr: ['*']
+
+stages:
+- stage: build
+ displayName: Build
+ jobs:
+ - template: eng/common/templates/jobs/jobs.yml
+ parameters:
+ enableMicrobuild: ${{ ne(variables.runCodeQL3000, 'true') }}
+ enablePublishBuildArtifacts: true
+ enablePublishBuildAssets: ${{ ne(variables.runCodeQL3000, 'true') }}
+ enablePublishUsingPipelines: ${{ variables._PublishUsingPipelines }}
+ publishAssetsImmediately: true
+ enableSourceIndex: ${{ and(ne(variables['System.TeamProject'], 'public'), eq(variables['Build.SourceBranch'], 'refs/heads/main')) }}
+ enableTelemetry: true
+ helixRepo: dotnet/efcore
+ jobs:
+ - job: Windows
+ enablePublishTestResults: ${{ ne(variables.runCodeQL3000, 'true') }}
+ pool:
+ ${{ if eq(variables['System.TeamProject'], 'public') }}:
+ name: $(DncEngPublicBuildPool)
+ demands: ImageOverride -equals 1es-windows-2019-open
+ ${{ if ne(variables['System.TeamProject'], 'public') }}:
+ name: $(DncEngInternalBuildPool)
+ demands: ImageOverride -equals 1es-windows-2019
+ ${{ if eq(variables.runCodeQL3000, 'true') }}:
+ # Component governance and SBOM creation are not needed here. Disable what Arcade would inject.
+ disableComponentGovernance: true
+ enableSbom: false
+ # CodeQL3000 extends build duration.
+ timeoutInMinutes: 180
+ ${{ else }}:
+ timeoutInMinutes: 90
+ variables:
+ - _InternalBuildArgs: ''
+ # Rely on task Arcade injects, not auto-injected build step.
+ - skipComponentGovernanceDetection: true
+ - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
+ - _SignType: real
+ - _InternalBuildArgs: /p:DotNetSignType=$(_SignType) /p:TeamName=$(_TeamName) /p:DotNetPublishUsingPipelines=$(_PublishUsingPipelines) /p:OfficialBuildId=$(BUILD.BUILDNUMBER)
+ - ${{ if eq(variables.runCodeQL3000, 'true') }}:
+ - _AdditionalBuildArgs: /p:Test=false /p:Sign=false /p:Pack=false /p:Publish=false /p:UseSharedCompilation=false
+ # Security analysis is included in normal runs. Disable its auto-injection.
+ - skipNugetSecurityAnalysis: true
+ # Do not let CodeQL3000 Extension gate scan frequency.
+ - Codeql.Cadence: 0
+ # Enable CodeQL3000 unconditionally so it may be run on any branch.
+ - Codeql.Enabled: true
+ # Ignore test and infrastructure code.
+ - Codeql.SourceRoot: src
+ # CodeQL3000 needs this plumbed along as a variable to enable TSA.
+ - Codeql.TSAEnabled: ${{ eq(variables['Build.Reason'], 'Schedule') }}
+ # Default expects tsaoptions.json under SourceRoot.
+ - Codeql.TSAOptionsPath: '$(Build.SourcesDirectory)/.config/tsaoptions.json'
+ - ${{ else }}:
+ - _AdditionalBuildArgs: ''
+ steps:
+ - task: NuGetCommand@2
+ displayName: 'Clear NuGet caches'
+ condition: succeeded()
+ inputs:
+ command: custom
+ arguments: 'locals all -clear'
+ - script: "echo ##vso[build.addbuildtag]daily-build"
+ condition: and(notin(variables['Build.Reason'], 'PullRequest'), ne(variables['IsFinalBuild'], 'true'))
+ displayName: 'Set CI tags'
+ - script: "echo ##vso[build.addbuildtag]release-candidate"
+ condition: and(notin(variables['Build.Reason'], 'PullRequest'), eq(variables['IsFinalBuild'], 'true'))
+ displayName: 'Set CI tags'
+ - powershell: SqlLocalDB start
+ displayName: Start LocalDB
+ - ${{ if ne(variables['System.TeamProject'], 'public') }}:
+ - task: PowerShell@2
+ displayName: Setup Private Feeds Credentials
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1
+ arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token
+ env:
+ Token: $(dn-bot-dnceng-artifact-feeds-rw)
+ - ${{ if eq(variables.runCodeQL3000, 'true') }}:
+ - task: CodeQL3000Init@0
+ displayName: CodeQL Initialize
+ - script: "echo ##vso[build.addbuildtag]CodeQL3000"
+ displayName: 'Set CI CodeQL3000 tag'
+ condition: ne(variables.CODEQL_DIST,'')
+ - script: eng\common\cibuild.cmd -configuration $(_BuildConfig) -prepareMachine $(_InternalBuildArgs)
+ $(_InternalRuntimeDownloadArgs) $(_AdditionalBuildArgs)
+ env:
+ Test__Cosmos__DefaultConnection: $(_CosmosConnectionUrl)
+ name: Build
+ - ${{ if eq(variables.runCodeQL3000, 'true') }}:
+ - task: CodeQL3000Finalize@0
+ displayName: CodeQL Finalize
+ - ${{ else }}:
+ - task: PublishBuildArtifacts@1
+ displayName: Upload TestResults
+ condition: always()
+ continueOnError: true
+ inputs:
+ pathtoPublish: artifacts/TestResults/$(_BuildConfig)/
+ artifactName: $(Agent.Os)_$(Agent.JobName) TestResults
+ artifactType: Container
+ parallel: true
+
+ - ${{ if ne(variables.runCodeQL3000, 'true') }}:
+ - job: macOS
+ enablePublishTestResults: true
+ pool:
+ vmImage: macOS-11
+ variables:
+ # Rely on task Arcade injects, not auto-injected build step.
+ - skipComponentGovernanceDetection: true
+ steps:
+ - ${{ if ne(variables['System.TeamProject'], 'public') }}:
+ - task: Bash@3
+ displayName: Setup Private Feeds Credentials
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh
+ arguments: $(Build.SourcesDirectory)/NuGet.config $Token
+ env:
+ Token: $(dn-bot-dnceng-artifact-feeds-rw)
+ - script: eng/common/cibuild.sh --configuration $(_BuildConfig) --prepareMachine $(_InternalRuntimeDownloadArgs)
+ env:
+ Test__Cosmos__DefaultConnection: $(_CosmosConnectionUrl)
+ COMPlus_EnableWriteXorExecute: 0 # Work-around for https://github.com/dotnet/runtime/issues/70758
+ name: Build
+ - task: PublishBuildArtifacts@1
+ displayName: Upload TestResults
+ condition: always()
+ continueOnError: true
+ inputs:
+ pathtoPublish: artifacts/TestResults/$(_BuildConfig)/
+ artifactName: $(Agent.Os)_$(Agent.JobName) TestResults
+ artifactType: Container
+ parallel: true
+
+ - job: Linux
+ timeoutInMinutes: 120
+ enablePublishTestResults: true
+ pool:
+ ${{ if or(ne(variables['System.TeamProject'], 'internal'), in(variables['Build.Reason'], 'Manual', 'PullRequest', 'Schedule')) }}:
+ vmImage: ubuntu-22.04
+ ${{ if and(eq(variables['System.TeamProject'], 'internal'), notin(variables['Build.Reason'], 'Manual', 'PullRequest', 'Schedule')) }}:
+ name: $(DncEngInternalBuildPool)
+ demands: ImageOverride -equals Build.Ubuntu.2204.Amd64
+ variables:
+ - _runCounter: $[counter(variables['Build.Reason'], 0)]
+ # Rely on task Arcade injects, not auto-injected build step.
+ - skipComponentGovernanceDetection: true
+ - ${{ if and(eq(variables['System.TeamProject'], 'internal'), notin(variables['Build.Reason'], 'PullRequest', 'Schedule')) }}:
+ - _CosmosConnectionUrl: 'true'
+ steps:
+ - bash: |
+ echo "##vso[task.setvariable variable=_CosmosConnectionUrl]https://ef-nightly-test.documents.azure.com:443/"
+ echo "##vso[task.setvariable variable=_CosmosToken]$(ef-nightly-cosmos-key)"
+ displayName: Prepare to run Cosmos tests on ef-nightly-test
+ condition: and(eq(variables['_CosmosConnectionUrl'], 'true'), or(endsWith(variables['_runCounter'], '0'), endsWith(variables['_runCounter'], '2'), endsWith(variables['_runCounter'], '4'), endsWith(variables['_runCounter'], '6'), endsWith(variables['_runCounter'], '8')))
+ - bash: |
+ echo "##vso[task.setvariable variable=_CosmosConnectionUrl]https://ef-pr-test.documents.azure.com:443/"
+ echo "##vso[task.setvariable variable=_CosmosToken]$(ef-pr-cosmos-test)"
+ displayName: Prepare to run Cosmos tests on ef-pr-test
+ condition: and(eq(variables['_CosmosConnectionUrl'], 'true'), or(endsWith(variables['_runCounter'], '1'), endsWith(variables['_runCounter'], '3'), endsWith(variables['_runCounter'], '5'), endsWith(variables['_runCounter'], '7'), endsWith(variables['_runCounter'], '9')))
+ - ${{ if ne(variables['System.TeamProject'], 'public') }}:
+ - task: Bash@3
+ displayName: Setup Private Feeds Credentials
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh
+ arguments: $(Build.SourcesDirectory)/NuGet.config $Token
+ env:
+ Token: $(dn-bot-dnceng-artifact-feeds-rw)
+ - script: eng/common/cibuild.sh --configuration $(_BuildConfig) --prepareMachine $(_InternalRuntimeDownloadArgs)
+ env:
+ Test__Cosmos__DefaultConnection: $(_CosmosConnectionUrl)
+ Test__Cosmos__AuthToken: $(_CosmosToken)
+ name: Build
+ - task: PublishBuildArtifacts@1
+ displayName: Upload TestResults
+ condition: always()
+ continueOnError: true
+ inputs:
+ pathtoPublish: artifacts/TestResults/$(_BuildConfig)/
+ artifactName: $(Agent.Os)_$(Agent.JobName) TestResults
+ artifactType: Container
+ parallel: true
+
+ - job: Helix
+ timeoutInMinutes: 180
+ pool:
+ ${{ if eq(variables['System.TeamProject'], 'public') }}:
+ name: $(DncEngPublicBuildPool)
+ demands: ImageOverride -equals 1es-windows-2019-open
+ ${{ if ne(variables['System.TeamProject'], 'public') }}:
+ name: $(DncEngInternalBuildPool)
+ demands: ImageOverride -equals 1es-windows-2019
+ variables:
+ # Rely on task Arcade injects, not auto-injected build step.
+ - skipComponentGovernanceDetection: true
+ - name: _HelixBuildConfig
+ value: $(_BuildConfig)
+ - ${{ if eq(variables['System.TeamProject'], 'public') }}:
+ - name: HelixTargetQueues
+ value: OSX.1100.Amd64.Open;(Ubuntu.2004.Amd64.SqlServer)Ubuntu.2004.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-20.04-helix-sqlserver-amd64
+ - name: _HelixAccessToken
+ value: '' # Needed for public queues
+ - ${{ if ne(variables['System.TeamProject'], 'public') }}:
+ - name: HelixTargetQueues
+ value: OSX.1100.Amd64;(Ubuntu.2004.Amd64.SqlServer)Ubuntu.2004.Amd64@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-20.04-helix-sqlserver-amd64
+ - name: _HelixAccessToken
+ value: $(HelixApiAccessToken) # Needed for internal queues
+ steps:
+ - task: NuGetCommand@2
+ displayName: 'Clear NuGet caches'
+ condition: succeeded()
+ inputs:
+ command: custom
+ arguments: 'locals all -clear'
+ - ${{ if ne(variables['System.TeamProject'], 'public') }}:
+ - task: PowerShell@2
+ displayName: Setup Private Feeds Credentials
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1
+ arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token
+ env:
+ Token: $(dn-bot-dnceng-artifact-feeds-rw)
+ - script: restore.cmd -ci /p:configuration=$(_BuildConfig) $(_InternalRuntimeDownloadArgs)
+ displayName: Restore packages
+ - script: .dotnet\dotnet build eng\helix.proj /restore /t:Test /p:configuration=$(_BuildConfig) /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog $(_InternalRuntimeDownloadArgs)
+ displayName: Send job to helix
+ env:
+ HelixAccessToken: $(_HelixAccessToken)
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken) # We need to set this env var to publish helix results to Azure Dev Ops
+ MSSQL_SA_PASSWORD: "Password12!"
+ COMPlus_EnableWriteXorExecute: 0 # Work-around for https://github.com/dotnet/runtime/issues/70758
+ DotNetBuildsInternalReadSasToken: $(dotnetbuilds-internal-container-read-token)
+
+- ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), ne(variables.runCodeQL3000, 'true')) }}:
+ - template: eng\common\templates\post-build\post-build.yml
+ parameters:
+ publishingInfraVersion: 3
+ # Symbol validation isn't being very reliable lately. This should be enabled back
+ # once this issue is resolved: https://github.com/dotnet/arcade/issues/2871
+ enableSymbolValidation: false
+ enableSigningValidation: false
+ enableNugetValidation: false
+ enableSourceLinkValidation: false
+ publishAssetsImmediately: true
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index aa704ddcde6..e169cc05bcf 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -1,24 +1,3 @@
-schedules:
-- cron: 0 9 * * 1
- displayName: "Run CodeQL3000 weekly, Monday at 2:00 AM PDT"
- branches:
- include:
- - release/2.1
- - release/6.0
- - release/7.0
- - main
- always: true
-
-parameters:
- # Parameter below is ignored in public builds.
- #
- # Choose whether to run the CodeQL3000 tasks.
- # Manual builds align w/ official builds unless this parameter is true.
- - name: runCodeQL3000
- default: false
- displayName: Run CodeQL3000 tasks
- type: boolean
-
variables:
- name: _BuildConfig
value: Release
@@ -38,26 +17,17 @@ variables:
- ${{ else }}:
- name: PostBuildSign
value: true
- - ${{ if ne(variables['System.TeamProject'], 'public') }}:
- - group: DotNet-HelixApi-Access
- - group: DotNetBuilds storage account read tokens
- - group: AzureDevOps-Artifact-Feeds-Pats
- - name: _InternalRuntimeDownloadArgs
- value: /p:DotNetRuntimeSourceFeed=https://dotnetbuilds.blob.core.windows.net/internal
- /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64)
- - ${{ if eq(variables['System.TeamProject'], 'public') }}:
- - name: _InternalRuntimeDownloadArgs
- value: ''
+ - group: DotNet-HelixApi-Access
+ - group: DotNetBuilds storage account read tokens
+ - group: AzureDevOps-Artifact-Feeds-Pats
+ - name: _InternalRuntimeDownloadArgs
+ value: /p:DotNetRuntimeSourceFeed=https://dotnetbuilds.blob.core.windows.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64)
- name: LC_ALL
value: 'en_US.UTF-8'
- name: LANG
value: 'en_US.UTF-8'
- name: LANGUAGE
value: 'en_US.UTF-8'
- - name: runCodeQL3000
- value: ${{ and(ne(variables['System.TeamProject'], 'public'), or(eq(variables['Build.Reason'], 'Schedule'), and(eq(variables['Build.Reason'], 'Manual'), eq(parameters.runCodeQL3000, 'true')))) }}
- - template: /eng/common/templates/variables/pool-providers.yml
-
trigger:
batch: true
branches:
@@ -66,64 +36,65 @@ trigger:
- release/*
- feature/*
- internal/release/*
-
pr: ['*']
-
-stages:
-- stage: build
- displayName: Build
- jobs:
- - template: eng/common/templates/jobs/jobs.yml
- parameters:
- enableMicrobuild: ${{ ne(variables.runCodeQL3000, 'true') }}
- enablePublishBuildArtifacts: true
- enablePublishBuildAssets: ${{ ne(variables.runCodeQL3000, 'true') }}
- enablePublishUsingPipelines: ${{ variables._PublishUsingPipelines }}
- publishAssetsImmediately: true
- enableSourceIndex: ${{ and(ne(variables['System.TeamProject'], 'public'), eq(variables['Build.SourceBranch'], 'refs/heads/main')) }}
- enableTelemetry: true
- helixRepo: dotnet/efcore
- jobs:
+resources:
+ repositories:
+ - repository: 1ESPipelineTemplates
+ type: git
+ name: 1ESPipelineTemplates/1ESPipelineTemplates
+ ref: refs/tags/release
+extends:
+ template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates
+ parameters:
+ featureFlags:
+ autoBaseline: false
+ sdl:
+ sourceAnalysisPool:
+ name: NetCore1ESPool-Svc-Internal
+ image: 1es-windows-2022
+ os: windows
+ baseline:
+ baselineFile: $(Build.SourcesDirectory)\.config\guardian\.gdnbaselines
+ binskim:
+ scanOutputDirectoryOnly: true
+ customBuildTags:
+ - ES365AIMigrationTooling
+ stages:
+ - stage: build
+ displayName: Build
+ jobs:
+ - template: /eng/common/templates-official/jobs/jobs.yml@self
+ parameters:
+ enableMicrobuild: true
+ enablePublishBuildArtifacts: true
+ enablePublishBuildAssets: true
+ enablePublishTestResults: true
+ enablePublishUsingPipelines: ${{ variables._PublishUsingPipelines }}
+ publishAssetsImmediately: true
+ enableSourceIndex: ${{ eq(variables['Build.SourceBranch'], 'refs/heads/main') }}
+ enableTelemetry: true
+ helixRepo: dotnet/efcore
+ jobs:
- job: Windows
- enablePublishTestResults: ${{ ne(variables.runCodeQL3000, 'true') }}
pool:
- ${{ if eq(variables['System.TeamProject'], 'public') }}:
- name: $(DncEngPublicBuildPool)
- demands: ImageOverride -equals 1es-windows-2019-open
- ${{ if ne(variables['System.TeamProject'], 'public') }}:
- name: $(DncEngInternalBuildPool)
- demands: ImageOverride -equals 1es-windows-2019
- ${{ if eq(variables.runCodeQL3000, 'true') }}:
- # Component governance and SBOM creation are not needed here. Disable what Arcade would inject.
- disableComponentGovernance: true
- enableSbom: false
- # CodeQL3000 extends build duration.
- timeoutInMinutes: 180
- ${{ else }}:
- timeoutInMinutes: 90
+ name: NetCore1ESPool-Svc-Internal
+ demands: ImageOverride -equals 1es-windows-2022
+ os: windows
+ timeoutInMinutes: 180
variables:
+ - _AdditionalBuildArgs: ''
- _InternalBuildArgs: ''
# Rely on task Arcade injects, not auto-injected build step.
- skipComponentGovernanceDetection: true
- - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
+ - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}:
- _SignType: real
- _InternalBuildArgs: /p:DotNetSignType=$(_SignType) /p:TeamName=$(_TeamName) /p:DotNetPublishUsingPipelines=$(_PublishUsingPipelines) /p:OfficialBuildId=$(BUILD.BUILDNUMBER)
- - ${{ if eq(variables.runCodeQL3000, 'true') }}:
- - _AdditionalBuildArgs: /p:Test=false /p:Sign=false /p:Pack=false /p:Publish=false /p:UseSharedCompilation=false
- # Security analysis is included in normal runs. Disable its auto-injection.
- - skipNugetSecurityAnalysis: true
- # Do not let CodeQL3000 Extension gate scan frequency.
- - Codeql.Cadence: 0
- # Enable CodeQL3000 unconditionally so it may be run on any branch.
- - Codeql.Enabled: true
- # Ignore test and infrastructure code.
- - Codeql.SourceRoot: src
- # CodeQL3000 needs this plumbed along as a variable to enable TSA.
- - Codeql.TSAEnabled: ${{ eq(variables['Build.Reason'], 'Schedule') }}
- # Default expects tsaoptions.json under SourceRoot.
- - Codeql.TSAOptionsPath: '$(Build.SourcesDirectory)/.config/tsaoptions.json'
- - ${{ else }}:
- - _AdditionalBuildArgs: ''
+ # Ignore test and infrastructure code.
+ - Codeql.SourceRoot: src
+ # CodeQL3000 needs this plumbed along as a variable to enable TSA.
+ - Codeql.TSAEnabled: true
+ # Default expects tsaoptions.json under SourceRoot.
+ - Codeql.TSAOptionsPath: '$(Build.SourcesDirectory)/.config/tsaoptions.json'
steps:
- task: NuGetCommand@2
displayName: 'Clear NuGet caches'
@@ -139,178 +110,146 @@ stages:
displayName: 'Set CI tags'
- powershell: SqlLocalDB start
displayName: Start LocalDB
- - ${{ if ne(variables['System.TeamProject'], 'public') }}:
- - task: PowerShell@2
- displayName: Setup Private Feeds Credentials
- inputs:
- filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1
- arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token
- env:
- Token: $(dn-bot-dnceng-artifact-feeds-rw)
- - ${{ if eq(variables.runCodeQL3000, 'true') }}:
- - task: CodeQL3000Init@0
- displayName: CodeQL Initialize
- - script: "echo ##vso[build.addbuildtag]CodeQL3000"
- displayName: 'Set CI CodeQL3000 tag'
- condition: ne(variables.CODEQL_DIST,'')
- - script: eng\common\cibuild.cmd -configuration $(_BuildConfig) -prepareMachine $(_InternalBuildArgs)
- $(_InternalRuntimeDownloadArgs) $(_AdditionalBuildArgs)
+ - task: PowerShell@2
+ displayName: Setup Private Feeds Credentials
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1
+ arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token
+ env:
+ Token: $(dn-bot-dnceng-artifact-feeds-rw)
+ - script: eng\common\cibuild.cmd -configuration $(_BuildConfig) -prepareMachine $(_InternalBuildArgs) $(_InternalRuntimeDownloadArgs) $(_AdditionalBuildArgs)
+ env:
+ Test__Cosmos__DefaultConnection: $(_CosmosConnectionUrl)
+ name: Build
+ templateContext:
+ outputs:
+ - output: pipelineArtifact
+ displayName: Upload TestResults
+ condition: always()
+ path: artifacts/TestResults/$(_BuildConfig)/
+ artifact: $(Agent.Os)_$(Agent.JobName) TestResults
+ - job: macOS
+ pool:
+ name: Azure Pipelines
+ image: macOS-11
+ os: macOS
+ variables:
+ # Rely on task Arcade injects, not auto-injected build step.
+ - skipComponentGovernanceDetection: true
+ steps:
+ - task: Bash@3
+ displayName: Setup Private Feeds Credentials
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh
+ arguments: $(Build.SourcesDirectory)/NuGet.config $Token
+ env:
+ Token: $(dn-bot-dnceng-artifact-feeds-rw)
+ - script: eng/common/cibuild.sh --configuration $(_BuildConfig) --prepareMachine $(_InternalRuntimeDownloadArgs)
+ env:
+ Test__Cosmos__DefaultConnection: $(_CosmosConnectionUrl)
+ # Work-around for https://github.com/dotnet/runtime/issues/70758
+ COMPlus_EnableWriteXorExecute: 0
+ name: Build
+ templateContext:
+ outputs:
+ - output: pipelineArtifact
+ displayName: Upload TestResults
+ condition: always()
+ path: artifacts/TestResults/$(_BuildConfig)/
+ artifact: $(Agent.Os)_$(Agent.JobName) TestResults
+ - job: Linux
+ timeoutInMinutes: 120
+ pool:
+ name: NetCore1ESPool-Svc-Internal
+ demands: ImageOverride -equals 1es-ubuntu-2204
+ os: linux
+ variables:
+ - _runCounter: $[counter(variables['Build.Reason'], 0)]
+ # Rely on task Arcade injects, not auto-injected build step.
+ - skipComponentGovernanceDetection: true
+ - ${{ if notin(variables['Build.Reason'], 'PullRequest', 'Schedule') }}:
+ - _CosmosConnectionUrl: 'true'
+ steps:
+ - bash: |
+ echo "##vso[task.setvariable variable=_CosmosConnectionUrl]https://ef-nightly-test.documents.azure.com:443/"
+ echo "##vso[task.setvariable variable=_CosmosToken]$(ef-nightly-cosmos-key)"
+ displayName: Prepare to run Cosmos tests on ef-nightly-test
+ condition: and(eq(variables['_CosmosConnectionUrl'], 'true'), or(endsWith(variables['_runCounter'], '0'), endsWith(variables['_runCounter'], '2'), endsWith(variables['_runCounter'], '4'), endsWith(variables['_runCounter'], '6'), endsWith(variables['_runCounter'], '8')))
+ - bash: |
+ echo "##vso[task.setvariable variable=_CosmosConnectionUrl]https://ef-pr-test.documents.azure.com:443/"
+ echo "##vso[task.setvariable variable=_CosmosToken]$(ef-pr-cosmos-test)"
+ displayName: Prepare to run Cosmos tests on ef-pr-test
+ condition: and(eq(variables['_CosmosConnectionUrl'], 'true'), or(endsWith(variables['_runCounter'], '1'), endsWith(variables['_runCounter'], '3'), endsWith(variables['_runCounter'], '5'), endsWith(variables['_runCounter'], '7'), endsWith(variables['_runCounter'], '9')))
+ - task: Bash@3
+ displayName: Setup Private Feeds Credentials
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh
+ arguments: $(Build.SourcesDirectory)/NuGet.config $Token
+ env:
+ Token: $(dn-bot-dnceng-artifact-feeds-rw)
+ - script: eng/common/cibuild.sh --configuration $(_BuildConfig) --prepareMachine $(_InternalRuntimeDownloadArgs)
env:
Test__Cosmos__DefaultConnection: $(_CosmosConnectionUrl)
+ Test__Cosmos__AuthToken: $(_CosmosToken)
name: Build
- - ${{ if eq(variables.runCodeQL3000, 'true') }}:
- - task: CodeQL3000Finalize@0
- displayName: CodeQL Finalize
- - ${{ else }}:
- - task: PublishBuildArtifacts@1
- displayName: Upload TestResults
- condition: always()
- continueOnError: true
- inputs:
- pathtoPublish: artifacts/TestResults/$(_BuildConfig)/
- artifactName: $(Agent.Os)_$(Agent.JobName) TestResults
- artifactType: Container
- parallel: true
-
- - ${{ if ne(variables.runCodeQL3000, 'true') }}:
- - job: macOS
- enablePublishTestResults: true
- pool:
- vmImage: macOS-11
- variables:
- # Rely on task Arcade injects, not auto-injected build step.
- - skipComponentGovernanceDetection: true
- steps:
- - ${{ if ne(variables['System.TeamProject'], 'public') }}:
- - task: Bash@3
- displayName: Setup Private Feeds Credentials
- inputs:
- filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh
- arguments: $(Build.SourcesDirectory)/NuGet.config $Token
- env:
- Token: $(dn-bot-dnceng-artifact-feeds-rw)
- - script: eng/common/cibuild.sh --configuration $(_BuildConfig) --prepareMachine $(_InternalRuntimeDownloadArgs)
- env:
- Test__Cosmos__DefaultConnection: $(_CosmosConnectionUrl)
- COMPlus_EnableWriteXorExecute: 0 # Work-around for https://github.com/dotnet/runtime/issues/70758
- name: Build
- - task: PublishBuildArtifacts@1
- displayName: Upload TestResults
- condition: always()
- continueOnError: true
- inputs:
- pathtoPublish: artifacts/TestResults/$(_BuildConfig)/
- artifactName: $(Agent.Os)_$(Agent.JobName) TestResults
- artifactType: Container
- parallel: true
-
- - job: Linux
- timeoutInMinutes: 120
- enablePublishTestResults: true
- pool:
- ${{ if or(ne(variables['System.TeamProject'], 'internal'), in(variables['Build.Reason'], 'Manual', 'PullRequest', 'Schedule')) }}:
- vmImage: ubuntu-22.04
- ${{ if and(eq(variables['System.TeamProject'], 'internal'), notin(variables['Build.Reason'], 'Manual', 'PullRequest', 'Schedule')) }}:
- name: $(DncEngInternalBuildPool)
- demands: ImageOverride -equals Build.Ubuntu.2204.Amd64
- variables:
- - _runCounter: $[counter(variables['Build.Reason'], 0)]
- # Rely on task Arcade injects, not auto-injected build step.
- - skipComponentGovernanceDetection: true
- - ${{ if and(eq(variables['System.TeamProject'], 'internal'), notin(variables['Build.Reason'], 'PullRequest', 'Schedule')) }}:
- - _CosmosConnectionUrl: 'true'
- steps:
- - bash: |
- echo "##vso[task.setvariable variable=_CosmosConnectionUrl]https://ef-nightly-test.documents.azure.com:443/"
- echo "##vso[task.setvariable variable=_CosmosToken]$(ef-nightly-cosmos-key)"
- displayName: Prepare to run Cosmos tests on ef-nightly-test
- condition: and(eq(variables['_CosmosConnectionUrl'], 'true'), or(endsWith(variables['_runCounter'], '0'), endsWith(variables['_runCounter'], '2'), endsWith(variables['_runCounter'], '4'), endsWith(variables['_runCounter'], '6'), endsWith(variables['_runCounter'], '8')))
- - bash: |
- echo "##vso[task.setvariable variable=_CosmosConnectionUrl]https://ef-pr-test.documents.azure.com:443/"
- echo "##vso[task.setvariable variable=_CosmosToken]$(ef-pr-cosmos-test)"
- displayName: Prepare to run Cosmos tests on ef-pr-test
- condition: and(eq(variables['_CosmosConnectionUrl'], 'true'), or(endsWith(variables['_runCounter'], '1'), endsWith(variables['_runCounter'], '3'), endsWith(variables['_runCounter'], '5'), endsWith(variables['_runCounter'], '7'), endsWith(variables['_runCounter'], '9')))
- - ${{ if ne(variables['System.TeamProject'], 'public') }}:
- - task: Bash@3
- displayName: Setup Private Feeds Credentials
- inputs:
- filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh
- arguments: $(Build.SourcesDirectory)/NuGet.config $Token
- env:
- Token: $(dn-bot-dnceng-artifact-feeds-rw)
- - script: eng/common/cibuild.sh --configuration $(_BuildConfig) --prepareMachine $(_InternalRuntimeDownloadArgs)
- env:
- Test__Cosmos__DefaultConnection: $(_CosmosConnectionUrl)
- Test__Cosmos__AuthToken: $(_CosmosToken)
- name: Build
- - task: PublishBuildArtifacts@1
- displayName: Upload TestResults
- condition: always()
- continueOnError: true
- inputs:
- pathtoPublish: artifacts/TestResults/$(_BuildConfig)/
- artifactName: $(Agent.Os)_$(Agent.JobName) TestResults
- artifactType: Container
- parallel: true
-
- - job: Helix
- timeoutInMinutes: 180
- pool:
- ${{ if eq(variables['System.TeamProject'], 'public') }}:
- name: $(DncEngPublicBuildPool)
- demands: ImageOverride -equals 1es-windows-2019-open
- ${{ if ne(variables['System.TeamProject'], 'public') }}:
- name: $(DncEngInternalBuildPool)
- demands: ImageOverride -equals 1es-windows-2019
- variables:
- # Rely on task Arcade injects, not auto-injected build step.
- - skipComponentGovernanceDetection: true
- - name: _HelixBuildConfig
- value: $(_BuildConfig)
- - ${{ if eq(variables['System.TeamProject'], 'public') }}:
- - name: HelixTargetQueues
- value: OSX.1100.Amd64.Open;(Ubuntu.2004.Amd64.SqlServer)Ubuntu.2004.Amd64.Open@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-20.04-helix-sqlserver-amd64
- - name: _HelixAccessToken
- value: '' # Needed for public queues
- - ${{ if ne(variables['System.TeamProject'], 'public') }}:
- - name: HelixTargetQueues
- value: OSX.1100.Amd64;(Ubuntu.2004.Amd64.SqlServer)Ubuntu.2004.Amd64@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-20.04-helix-sqlserver-amd64
- - name: _HelixAccessToken
- value: $(HelixApiAccessToken) # Needed for internal queues
- steps:
- - task: NuGetCommand@2
- displayName: 'Clear NuGet caches'
- condition: succeeded()
- inputs:
- command: custom
- arguments: 'locals all -clear'
- - ${{ if ne(variables['System.TeamProject'], 'public') }}:
- - task: PowerShell@2
- displayName: Setup Private Feeds Credentials
- inputs:
- filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1
- arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token
- env:
- Token: $(dn-bot-dnceng-artifact-feeds-rw)
- - script: restore.cmd -ci /p:configuration=$(_BuildConfig) $(_InternalRuntimeDownloadArgs)
- displayName: Restore packages
- - script: .dotnet\dotnet build eng\helix.proj /restore /t:Test /p:configuration=$(_BuildConfig) /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog $(_InternalRuntimeDownloadArgs)
- displayName: Send job to helix
- env:
- HelixAccessToken: $(_HelixAccessToken)
- SYSTEM_ACCESSTOKEN: $(System.AccessToken) # We need to set this env var to publish helix results to Azure Dev Ops
- MSSQL_SA_PASSWORD: "Password12!"
- COMPlus_EnableWriteXorExecute: 0 # Work-around for https://github.com/dotnet/runtime/issues/70758
- DotNetBuildsInternalReadSasToken: $(dotnetbuilds-internal-container-read-token)
-
-- ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), ne(variables.runCodeQL3000, 'true')) }}:
- - template: eng\common\templates\post-build\post-build.yml
- parameters:
- publishingInfraVersion: 3
- # Symbol validation isn't being very reliable lately. This should be enabled back
- # once this issue is resolved: https://github.com/dotnet/arcade/issues/2871
- enableSymbolValidation: false
- enableSigningValidation: false
- enableNugetValidation: false
- enableSourceLinkValidation: false
- publishAssetsImmediately: true
+ templateContext:
+ outputs:
+ - output: pipelineArtifact
+ displayName: Upload TestResults
+ condition: always()
+ path: artifacts/TestResults/$(_BuildConfig)/
+ artifact: $(Agent.Os)_$(Agent.JobName) TestResults
+ - job: Helix
+ timeoutInMinutes: 180
+ pool:
+ name: NetCore1ESPool-Svc-Internal
+ demands: ImageOverride -equals 1es-windows-2022
+ os: windows
+ variables:
+ # Rely on task Arcade injects, not auto-injected build step.
+ - skipComponentGovernanceDetection: true
+ - name: _HelixBuildConfig
+ value: $(_BuildConfig)
+ - name: HelixTargetQueues
+ value: OSX.1100.Amd64;(Ubuntu.2004.Amd64.SqlServer)Ubuntu.2004.Amd64@mcr.microsoft.com/dotnet-buildtools/prereqs:ubuntu-20.04-helix-sqlserver-amd64
+ - name: _HelixAccessToken
+ # Needed for internal queues
+ value: $(HelixApiAccessToken)
+ steps:
+ - task: NuGetCommand@2
+ displayName: 'Clear NuGet caches'
+ condition: succeeded()
+ inputs:
+ command: custom
+ arguments: 'locals all -clear'
+ - task: PowerShell@2
+ displayName: Setup Private Feeds Credentials
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1
+ arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token
+ env:
+ Token: $(dn-bot-dnceng-artifact-feeds-rw)
+ - script: restore.cmd -ci /p:configuration=$(_BuildConfig) $(_InternalRuntimeDownloadArgs)
+ displayName: Restore packages
+ - script: .dotnet\dotnet build eng\helix.proj /restore /t:Test /p:configuration=$(_BuildConfig) /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog $(_InternalRuntimeDownloadArgs)
+ displayName: Send job to helix
+ env:
+ HelixAccessToken: $(_HelixAccessToken)
+ # We need to set this env var to publish helix results to Azure Dev Ops
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
+ MSSQL_SA_PASSWORD: "Password12!"
+ # Work-around for https://github.com/dotnet/runtime/issues/70758
+ COMPlus_EnableWriteXorExecute: 0
+ DotNetBuildsInternalReadSasToken: $(dotnetbuilds-internal-container-read-token)
+ - ${{ if notin(variables['Build.Reason'], 'PullRequest') }}:
+ - template: /eng/common/templates-official/post-build/post-build.yml@self
+ parameters:
+ publishingInfraVersion: 3
+ # Symbol validation isn't being very reliable lately. This should be enabled back
+ # once this issue is resolved: https://github.com/dotnet/arcade/issues/2871
+ enableSymbolValidation: false
+ enableSigningValidation: false
+ enableNugetValidation: false
+ enableSourceLinkValidation: false
+ SDLValidationParameters:
+ enable: false
\ No newline at end of file
diff --git a/eng/Publishing.props b/eng/Publishing.props
index 1da7cb4e448..6f77090ffcd 100644
--- a/eng/Publishing.props
+++ b/eng/Publishing.props
@@ -5,5 +5,6 @@
false
+ true
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index d5aec717843..bd37246d819 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -29,44 +29,44 @@
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
5535e31a712343a63f5d7d796cd874e563e5ac14
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 1381d5ebd2ab1f292848d5b19b80cf71ac332508
+ 087e15321bb712ef6fe8b0ba6f8bd12facf92629
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
5535e31a712343a63f5d7d796cd874e563e5ac14
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 1381d5ebd2ab1f292848d5b19b80cf71ac332508
+ 9f4b1f5d664afdfc80e1508ab7ed099dff210fbd
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 1381d5ebd2ab1f292848d5b19b80cf71ac332508
+ 087e15321bb712ef6fe8b0ba6f8bd12facf92629
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 1381d5ebd2ab1f292848d5b19b80cf71ac332508
+ 087e15321bb712ef6fe8b0ba6f8bd12facf92629
-
+
https://dev.azure.com/dnceng/internal/_git/dotnet-runtime
- 1381d5ebd2ab1f292848d5b19b80cf71ac332508
+ 087e15321bb712ef6fe8b0ba6f8bd12facf92629
-
+
https://github.com/dotnet/arcade
- 61ae141d2bf3534619265c8f691fd55dc3e75147
+ 188340e12c0a372b1681ad6a5e72c608021efdba
-
+
https://github.com/dotnet/arcade
- 61ae141d2bf3534619265c8f691fd55dc3e75147
+ 188340e12c0a372b1681ad6a5e72c608021efdba
-
+
https://github.com/dotnet/arcade
- 61ae141d2bf3534619265c8f691fd55dc3e75147
+ 188340e12c0a372b1681ad6a5e72c608021efdba
diff --git a/eng/Versions.props b/eng/Versions.props
index ccfe6d5b20d..4340235984a 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -1,6 +1,6 @@
- 8.0.2
+ 8.0.5
servicing
@@ -24,19 +24,19 @@
8.0.0
8.0.0
8.0.0
- 8.0.2-servicing.24067.11
+ 8.0.5-servicing.24216.15
8.0.0
- 8.0.2
- 8.0.2
- 8.0.2
- 8.0.2-servicing.24067.11
+ 8.0.3
+ 8.0.5
+ 8.0.5
+ 8.0.5-servicing.24216.15
- 8.0.0-beta.24059.4
+ 8.0.0-beta.24204.3
4.5.0
- 1.1.2-beta1.23371.1
+ 1.1.2-beta1.24121.1
diff --git a/eng/common/SetupNugetSources.ps1 b/eng/common/SetupNugetSources.ps1
index 6c65e81925f..efa2fd72bfa 100644
--- a/eng/common/SetupNugetSources.ps1
+++ b/eng/common/SetupNugetSources.ps1
@@ -35,7 +35,7 @@ Set-StrictMode -Version 2.0
. $PSScriptRoot\tools.ps1
# Add source entry to PackageSources
-function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Username, $Password) {
+function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Username, $pwd) {
$packageSource = $sources.SelectSingleNode("add[@key='$SourceName']")
if ($packageSource -eq $null)
@@ -48,12 +48,11 @@ function AddPackageSource($sources, $SourceName, $SourceEndPoint, $creds, $Usern
else {
Write-Host "Package source $SourceName already present."
}
-
- AddCredential -Creds $creds -Source $SourceName -Username $Username -Password $Password
+ AddCredential -Creds $creds -Source $SourceName -Username $Username -pwd $pwd
}
# Add a credential node for the specified source
-function AddCredential($creds, $source, $username, $password) {
+function AddCredential($creds, $source, $username, $pwd) {
# Looks for credential configuration for the given SourceName. Create it if none is found.
$sourceElement = $creds.SelectSingleNode($Source)
if ($sourceElement -eq $null)
@@ -82,17 +81,18 @@ function AddCredential($creds, $source, $username, $password) {
$passwordElement.SetAttribute("key", "ClearTextPassword")
$sourceElement.AppendChild($passwordElement) | Out-Null
}
- $passwordElement.SetAttribute("value", $Password)
+
+ $passwordElement.SetAttribute("value", $pwd)
}
-function InsertMaestroPrivateFeedCredentials($Sources, $Creds, $Username, $Password) {
+function InsertMaestroPrivateFeedCredentials($Sources, $Creds, $Username, $pwd) {
$maestroPrivateSources = $Sources.SelectNodes("add[contains(@key,'darc-int')]")
Write-Host "Inserting credentials for $($maestroPrivateSources.Count) Maestro's private feeds."
ForEach ($PackageSource in $maestroPrivateSources) {
Write-Host "`tInserting credential for Maestro's feed:" $PackageSource.Key
- AddCredential -Creds $creds -Source $PackageSource.Key -Username $Username -Password $Password
+ AddCredential -Creds $creds -Source $PackageSource.Key -Username $Username -pwd $pwd
}
}
@@ -144,13 +144,13 @@ if ($disabledSources -ne $null) {
$userName = "dn-bot"
# Insert credential nodes for Maestro's private feeds
-InsertMaestroPrivateFeedCredentials -Sources $sources -Creds $creds -Username $userName -Password $Password
+InsertMaestroPrivateFeedCredentials -Sources $sources -Creds $creds -Username $userName -pwd $Password
# 3.1 uses a different feed url format so it's handled differently here
$dotnet31Source = $sources.SelectSingleNode("add[@key='dotnet3.1']")
if ($dotnet31Source -ne $null) {
- AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal/nuget/v2" -Creds $creds -Username $userName -Password $Password
- 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 -Password $Password
+ AddPackageSource -Sources $sources -SourceName "dotnet3.1-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/_packaging/dotnet3.1-internal/nuget/v2" -Creds $creds -Username $userName -pwd $Password
+ 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')
@@ -159,9 +159,9 @@ foreach ($dotnetVersion in $dotnetVersions) {
$feedPrefix = "dotnet" + $dotnetVersion;
$dotnetSource = $sources.SelectSingleNode("add[@key='$feedPrefix']")
if ($dotnetSource -ne $null) {
- AddPackageSource -Sources $sources -SourceName "$feedPrefix-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal/nuget/v2" -Creds $creds -Username $userName -Password $Password
- AddPackageSource -Sources $sources -SourceName "$feedPrefix-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal-transport/nuget/v2" -Creds $creds -Username $userName -Password $Password
+ AddPackageSource -Sources $sources -SourceName "$feedPrefix-internal" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal/nuget/v2" -Creds $creds -Username $userName -pwd $Password
+ AddPackageSource -Sources $sources -SourceName "$feedPrefix-internal-transport" -SourceEndPoint "https://pkgs.dev.azure.com/dnceng/internal/_packaging/$feedPrefix-internal-transport/nuget/v2" -Creds $creds -Username $userName -pwd $Password
}
}
-$doc.Save($filename)
+$doc.Save($filename)
\ No newline at end of file
diff --git a/eng/common/native/init-compiler.sh b/eng/common/native/init-compiler.sh
index f5c1ec7eafe..2d5660642b8 100644
--- a/eng/common/native/init-compiler.sh
+++ b/eng/common/native/init-compiler.sh
@@ -63,7 +63,7 @@ if [ -z "$CLR_CC" ]; then
# Set default versions
if [ -z "$majorVersion" ]; then
# note: gcc (all versions) and clang versions higher than 6 do not have minor version in file name, if it is zero.
- if [ "$compiler" = "clang" ]; then versions="17 16 15 14 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5"
+ if [ "$compiler" = "clang" ]; then versions="18 17 16 15 14 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5"
elif [ "$compiler" = "gcc" ]; then versions="13 12 11 10 9 8 7 6 5 4.9"; fi
for version in $versions; do
diff --git a/eng/common/post-build/publish-using-darc.ps1 b/eng/common/post-build/publish-using-darc.ps1
index 1e779fec4dd..5a3a32ea8d7 100644
--- a/eng/common/post-build/publish-using-darc.ps1
+++ b/eng/common/post-build/publish-using-darc.ps1
@@ -12,7 +12,7 @@ param(
try {
. $PSScriptRoot\post-build-utils.ps1
- $darc = Get-Darc
+ $darc = Get-Darc
$optionalParams = [System.Collections.ArrayList]::new()
@@ -46,7 +46,7 @@ try {
}
Write-Host 'done.'
-}
+}
catch {
Write-Host $_
Write-PipelineTelemetryError -Category 'PromoteBuild' -Message "There was an error while trying to publish build '$BuildId' to default channels."
diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml
new file mode 100644
index 00000000000..1f035fee73f
--- /dev/null
+++ b/eng/common/templates-official/job/job.yml
@@ -0,0 +1,264 @@
+# Internal resources (telemetry, microbuild) can only be accessed from non-public projects,
+# and some (Microbuild) should only be applied to non-PR cases for internal builds.
+
+parameters:
+# Job schema parameters - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job
+ cancelTimeoutInMinutes: ''
+ condition: ''
+ container: ''
+ continueOnError: false
+ dependsOn: ''
+ displayName: ''
+ pool: ''
+ steps: []
+ strategy: ''
+ timeoutInMinutes: ''
+ variables: []
+ workspace: ''
+ templateContext: ''
+
+# Job base template specific parameters
+ # See schema documentation - https://github.com/dotnet/arcade/blob/master/Documentation/AzureDevOps/TemplateSchema.md
+ artifacts: ''
+ enableMicrobuild: false
+ enablePublishBuildArtifacts: false
+ enablePublishBuildAssets: false
+ enablePublishTestResults: false
+ enablePublishUsingPipelines: false
+ enableBuildRetry: false
+ disableComponentGovernance: ''
+ componentGovernanceIgnoreDirectories: ''
+ mergeTestResults: false
+ testRunTitle: ''
+ testResultsFormat: ''
+ name: ''
+ preSteps: []
+ runAsPublic: false
+# Sbom related params
+ enableSbom: true
+ PackageVersion: 7.0.0
+ BuildDropPath: '$(Build.SourcesDirectory)/artifacts'
+
+jobs:
+- job: ${{ parameters.name }}
+
+ ${{ if ne(parameters.cancelTimeoutInMinutes, '') }}:
+ cancelTimeoutInMinutes: ${{ parameters.cancelTimeoutInMinutes }}
+
+ ${{ if ne(parameters.condition, '') }}:
+ condition: ${{ parameters.condition }}
+
+ ${{ if ne(parameters.container, '') }}:
+ container: ${{ parameters.container }}
+
+ ${{ if ne(parameters.continueOnError, '') }}:
+ continueOnError: ${{ parameters.continueOnError }}
+
+ ${{ if ne(parameters.dependsOn, '') }}:
+ dependsOn: ${{ parameters.dependsOn }}
+
+ ${{ if ne(parameters.displayName, '') }}:
+ displayName: ${{ parameters.displayName }}
+
+ ${{ if ne(parameters.pool, '') }}:
+ pool: ${{ parameters.pool }}
+
+ ${{ if ne(parameters.strategy, '') }}:
+ strategy: ${{ parameters.strategy }}
+
+ ${{ if ne(parameters.timeoutInMinutes, '') }}:
+ timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
+
+ ${{ if ne(parameters.templateContext, '') }}:
+ templateContext: ${{ parameters.templateContext }}
+
+ variables:
+ - ${{ if ne(parameters.enableTelemetry, 'false') }}:
+ - name: DOTNET_CLI_TELEMETRY_PROFILE
+ value: '$(Build.Repository.Uri)'
+ - ${{ if eq(parameters.enableRichCodeNavigation, 'true') }}:
+ - name: EnableRichCodeNavigation
+ value: 'true'
+ # Retry signature validation up to three times, waiting 2 seconds between attempts.
+ # See https://learn.microsoft.com/en-us/nuget/reference/errors-and-warnings/nu3028#retry-untrusted-root-failures
+ - name: NUGET_EXPERIMENTAL_CHAIN_BUILD_RETRY_POLICY
+ value: 3,2000
+ - ${{ each variable in parameters.variables }}:
+ # handle name-value variable syntax
+ # example:
+ # - name: [key]
+ # value: [value]
+ - ${{ if ne(variable.name, '') }}:
+ - name: ${{ variable.name }}
+ value: ${{ variable.value }}
+
+ # handle variable groups
+ - ${{ if ne(variable.group, '') }}:
+ - group: ${{ variable.group }}
+
+ # handle template variable syntax
+ # example:
+ # - template: path/to/template.yml
+ # parameters:
+ # [key]: [value]
+ - ${{ if ne(variable.template, '') }}:
+ - template: ${{ variable.template }}
+ ${{ if ne(variable.parameters, '') }}:
+ parameters: ${{ variable.parameters }}
+
+ # handle key-value variable syntax.
+ # example:
+ # - [key]: [value]
+ - ${{ if and(eq(variable.name, ''), eq(variable.group, ''), eq(variable.template, '')) }}:
+ - ${{ each pair in variable }}:
+ - name: ${{ pair.key }}
+ value: ${{ pair.value }}
+
+ # DotNet-HelixApi-Access provides 'HelixApiAccessToken' for internal builds
+ - ${{ if and(eq(parameters.enableTelemetry, 'true'), eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
+ - group: DotNet-HelixApi-Access
+
+ ${{ if ne(parameters.workspace, '') }}:
+ workspace: ${{ parameters.workspace }}
+
+ steps:
+ - ${{ if ne(parameters.preSteps, '') }}:
+ - ${{ each preStep in parameters.preSteps }}:
+ - ${{ preStep }}
+
+ - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
+ - ${{ if eq(parameters.enableMicrobuild, 'true') }}:
+ - task: MicroBuildSigningPlugin@4
+ displayName: Install MicroBuild plugin
+ inputs:
+ signType: $(_SignType)
+ zipSources: false
+ feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json
+ env:
+ TeamName: $(_TeamName)
+ MicroBuildOutputFolderOverride: '$(Agent.TempDirectory)'
+ continueOnError: ${{ parameters.continueOnError }}
+ condition: and(succeeded(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT'))
+
+ - ${{ if and(eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal')) }}:
+ - task: NuGetAuthenticate@1
+
+ - ${{ if and(ne(parameters.artifacts.download, 'false'), ne(parameters.artifacts.download, '')) }}:
+ - task: DownloadPipelineArtifact@2
+ inputs:
+ buildType: current
+ artifactName: ${{ coalesce(parameters.artifacts.download.name, 'Artifacts_$(Agent.OS)_$(_BuildConfig)') }}
+ targetPath: ${{ coalesce(parameters.artifacts.download.path, 'artifacts') }}
+ itemPattern: ${{ coalesce(parameters.artifacts.download.pattern, '**') }}
+
+ - ${{ each step in parameters.steps }}:
+ - ${{ step }}
+
+ - ${{ if eq(parameters.enableRichCodeNavigation, true) }}:
+ - task: RichCodeNavIndexer@0
+ displayName: RichCodeNav Upload
+ inputs:
+ languages: ${{ coalesce(parameters.richCodeNavigationLanguage, 'csharp') }}
+ environment: ${{ coalesce(parameters.richCodeNavigationEnvironment, 'production') }}
+ richNavLogOutputDirectory: $(Build.SourcesDirectory)/artifacts/bin
+ uploadRichNavArtifacts: ${{ coalesce(parameters.richCodeNavigationUploadArtifacts, false) }}
+ continueOnError: true
+
+ - template: /eng/common/templates-official/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
+ ${{ else }}:
+ disableComponentGovernance: ${{ parameters.disableComponentGovernance }}
+ componentGovernanceIgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }}
+
+ - ${{ if eq(parameters.enableMicrobuild, 'true') }}:
+ - ${{ 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'))
+ continueOnError: ${{ parameters.continueOnError }}
+ env:
+ TeamName: $(_TeamName)
+
+ - ${{ if ne(parameters.artifacts.publish, '') }}:
+ - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}:
+ - task: CopyFiles@2
+ displayName: Gather binaries for publish to artifacts
+ inputs:
+ SourceFolder: 'artifacts/bin'
+ Contents: '**'
+ TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/bin'
+ - task: CopyFiles@2
+ displayName: Gather packages for publish to artifacts
+ inputs:
+ SourceFolder: 'artifacts/packages'
+ Contents: '**'
+ TargetFolder: '$(Build.ArtifactStagingDirectory)/artifacts/packages'
+ - task: 1ES.PublishBuildArtifacts@1
+ displayName: Publish pipeline artifacts
+ inputs:
+ 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, '')) }}:
+ - task: 1ES.PublishPipelineArtifact@1
+ inputs:
+ targetPath: 'artifacts/log'
+ artifactName: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)') }}
+ displayName: 'Publish logs'
+ continueOnError: true
+ condition: always()
+
+ - ${{ if ne(parameters.enablePublishBuildArtifacts, 'false') }}:
+ - task: 1ES.PublishBuildArtifacts@1
+ displayName: Publish Logs
+ inputs:
+ PathtoPublish: '$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)'
+ PublishLocation: Container
+ ArtifactName: ${{ coalesce(parameters.enablePublishBuildArtifacts.artifactName, '$(Agent.Os)_$(Agent.JobName)' ) }}
+ continueOnError: true
+ condition: always()
+
+ - ${{ if or(and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')), eq(parameters.testResultsFormat, 'xunit')) }}:
+ - task: PublishTestResults@2
+ displayName: Publish XUnit Test Results
+ inputs:
+ testResultsFormat: 'xUnit'
+ testResultsFiles: '*.xml'
+ searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)'
+ testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-xunit
+ mergeTestResults: ${{ parameters.mergeTestResults }}
+ continueOnError: true
+ condition: always()
+ - ${{ if or(and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')), eq(parameters.testResultsFormat, 'vstest')) }}:
+ - task: PublishTestResults@2
+ displayName: Publish TRX Test Results
+ inputs:
+ testResultsFormat: 'VSTest'
+ testResultsFiles: '*.trx'
+ searchFolder: '$(Build.SourcesDirectory)/artifacts/TestResults/$(_BuildConfig)'
+ testRunTitle: ${{ coalesce(parameters.testRunTitle, parameters.name, '$(System.JobName)') }}-trx
+ mergeTestResults: ${{ parameters.mergeTestResults }}
+ continueOnError: true
+ condition: always()
+
+ - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.enableSbom, 'true')) }}:
+ - template: /eng/common/templates-official/steps/generate-sbom.yml
+ parameters:
+ PackageVersion: ${{ parameters.packageVersion}}
+ BuildDropPath: ${{ parameters.buildDropPath }}
+ IgnoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }}
+
+ - ${{ if eq(parameters.enableBuildRetry, 'true') }}:
+ - task: 1ES.PublishPipelineArtifact@1
+ inputs:
+ targetPath: '$(Build.SourcesDirectory)\eng\common\BuildConfiguration'
+ artifactName: 'BuildConfiguration'
+ displayName: 'Publish build retry configuration'
+ continueOnError: true
\ No newline at end of file
diff --git a/eng/common/templates-official/job/onelocbuild.yml b/eng/common/templates-official/job/onelocbuild.yml
new file mode 100644
index 00000000000..52b4d05d3f8
--- /dev/null
+++ b/eng/common/templates-official/job/onelocbuild.yml
@@ -0,0 +1,112 @@
+parameters:
+ # Optional: dependencies of the job
+ dependsOn: ''
+
+ # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool
+ pool: ''
+
+ CeapexPat: $(dn-bot-ceapex-package-r) # PAT for the loc AzDO instance https://dev.azure.com/ceapex
+ GithubPat: $(BotAccount-dotnet-bot-repo-PAT)
+
+ SourcesDirectory: $(Build.SourcesDirectory)
+ CreatePr: true
+ AutoCompletePr: false
+ ReusePr: true
+ UseLfLineEndings: true
+ UseCheckedInLocProjectJson: false
+ SkipLocProjectJsonGeneration: false
+ LanguageSet: VS_Main_Languages
+ LclSource: lclFilesInRepo
+ LclPackageId: ''
+ RepoType: gitHub
+ GitHubOrg: dotnet
+ MirrorRepo: ''
+ MirrorBranch: main
+ condition: ''
+ JobNameSuffix: ''
+
+jobs:
+- job: OneLocBuild${{ parameters.JobNameSuffix }}
+
+ dependsOn: ${{ parameters.dependsOn }}
+
+ displayName: OneLocBuild${{ parameters.JobNameSuffix }}
+
+ variables:
+ - group: OneLocBuildVariables # Contains the CeapexPat and GithubPat
+ - name: _GenerateLocProjectArguments
+ value: -SourcesDirectory ${{ parameters.SourcesDirectory }}
+ -LanguageSet "${{ parameters.LanguageSet }}"
+ -CreateNeutralXlfs
+ - ${{ if eq(parameters.UseCheckedInLocProjectJson, 'true') }}:
+ - name: _GenerateLocProjectArguments
+ value: ${{ variables._GenerateLocProjectArguments }} -UseCheckedInLocProjectJson
+ - template: /eng/common/templates-official/variables/pool-providers.yml
+
+ ${{ if ne(parameters.pool, '') }}:
+ pool: ${{ parameters.pool }}
+ ${{ if eq(parameters.pool, '') }}:
+ pool:
+ # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com)
+ ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}:
+ name: AzurePipelines-EO
+ image: 1ESPT-Windows2022
+ demands: Cmd
+ os: windows
+ # If it's not devdiv, it's dnceng
+ ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}:
+ name: $(DncEngInternalBuildPool)
+ image: 1es-windows-2022
+ os: windows
+
+ steps:
+ - ${{ if ne(parameters.SkipLocProjectJsonGeneration, 'true') }}:
+ - task: Powershell@2
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/generate-locproject.ps1
+ arguments: $(_GenerateLocProjectArguments)
+ displayName: Generate LocProject.json
+ condition: ${{ parameters.condition }}
+
+ - task: OneLocBuild@2
+ displayName: OneLocBuild
+ env:
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
+ inputs:
+ locProj: eng/Localize/LocProject.json
+ outDir: $(Build.ArtifactStagingDirectory)
+ lclSource: ${{ parameters.LclSource }}
+ lclPackageId: ${{ parameters.LclPackageId }}
+ isCreatePrSelected: ${{ parameters.CreatePr }}
+ isAutoCompletePrSelected: ${{ parameters.AutoCompletePr }}
+ ${{ if eq(parameters.CreatePr, true) }}:
+ isUseLfLineEndingsSelected: ${{ parameters.UseLfLineEndings }}
+ ${{ if eq(parameters.RepoType, 'gitHub') }}:
+ isShouldReusePrSelected: ${{ parameters.ReusePr }}
+ packageSourceAuth: patAuth
+ patVariable: ${{ parameters.CeapexPat }}
+ ${{ if eq(parameters.RepoType, 'gitHub') }}:
+ repoType: ${{ parameters.RepoType }}
+ gitHubPatVariable: "${{ parameters.GithubPat }}"
+ ${{ if ne(parameters.MirrorRepo, '') }}:
+ isMirrorRepoSelected: true
+ gitHubOrganization: ${{ parameters.GitHubOrg }}
+ mirrorRepo: ${{ parameters.MirrorRepo }}
+ mirrorBranch: ${{ parameters.MirrorBranch }}
+ condition: ${{ parameters.condition }}
+
+ - task: 1ES.PublishBuildArtifacts@1
+ displayName: Publish Localization Files
+ inputs:
+ PathtoPublish: '$(Build.ArtifactStagingDirectory)/loc'
+ PublishLocation: Container
+ ArtifactName: Loc
+ condition: ${{ parameters.condition }}
+
+ - task: 1ES.PublishBuildArtifacts@1
+ displayName: Publish LocProject.json
+ inputs:
+ PathtoPublish: '$(Build.SourcesDirectory)/eng/Localize/'
+ PublishLocation: Container
+ ArtifactName: Loc
+ condition: ${{ parameters.condition }}
\ No newline at end of file
diff --git a/eng/common/templates-official/job/publish-build-assets.yml b/eng/common/templates-official/job/publish-build-assets.yml
new file mode 100644
index 00000000000..589ac80a18b
--- /dev/null
+++ b/eng/common/templates-official/job/publish-build-assets.yml
@@ -0,0 +1,155 @@
+parameters:
+ configuration: 'Debug'
+
+ # Optional: condition for the job to run
+ condition: ''
+
+ # Optional: 'true' if future jobs should run even if this job fails
+ continueOnError: false
+
+ # Optional: dependencies of the job
+ dependsOn: ''
+
+ # Optional: Include PublishBuildArtifacts task
+ enablePublishBuildArtifacts: false
+
+ # Optional: A defined YAML pool - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#pool
+ pool: {}
+
+ # Optional: should run as a public build even in the internal project
+ # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects.
+ runAsPublic: false
+
+ # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing
+ publishUsingPipelines: false
+
+ # Optional: whether the build's artifacts will be published using release pipelines or direct feed publishing
+ publishAssetsImmediately: false
+
+ artifactsPublishingAdditionalParameters: ''
+
+ signingValidationAdditionalParameters: ''
+
+jobs:
+- job: Asset_Registry_Publish
+
+ dependsOn: ${{ parameters.dependsOn }}
+ timeoutInMinutes: 150
+
+ ${{ if eq(parameters.publishAssetsImmediately, 'true') }}:
+ displayName: Publish Assets
+ ${{ else }}:
+ displayName: Publish to Build Asset Registry
+
+ variables:
+ - template: /eng/common/templates-official/variables/pool-providers.yml
+ - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
+ - group: Publish-Build-Assets
+ - group: AzureDevOps-Artifact-Feeds-Pats
+ - name: runCodesignValidationInjection
+ value: false
+ - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}:
+ - template: /eng/common/templates-official/post-build/common-variables.yml
+
+ pool:
+ # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com)
+ ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}:
+ name: AzurePipelines-EO
+ image: 1ESPT-Windows2022
+ demands: Cmd
+ os: windows
+ # If it's not devdiv, it's dnceng
+ ${{ if ne(variables['System.TeamProject'], 'DevDiv') }}:
+ name: NetCore1ESPool-Publishing-Internal
+ image: windows.vs2019.amd64
+ os: windows
+ steps:
+ - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
+ - task: DownloadBuildArtifacts@0
+ displayName: Download artifact
+ inputs:
+ artifactName: AssetManifests
+ downloadPath: '$(Build.StagingDirectory)/Download'
+ checkDownloadedFiles: true
+ condition: ${{ parameters.condition }}
+ continueOnError: ${{ parameters.continueOnError }}
+
+ - task: NuGetAuthenticate@1
+
+ - task: PowerShell@2
+ displayName: Publish Build Assets
+ inputs:
+ filePath: eng\common\sdk-task.ps1
+ arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet
+ /p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests'
+ /p:BuildAssetRegistryToken=$(MaestroAccessToken)
+ /p:MaestroApiEndpoint=https://maestro-prod.westus2.cloudapp.azure.com
+ /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }}
+ /p:OfficialBuildId=$(Build.BuildNumber)
+ condition: ${{ parameters.condition }}
+ continueOnError: ${{ parameters.continueOnError }}
+
+ - task: powershell@2
+ displayName: Create ReleaseConfigs Artifact
+ inputs:
+ targetType: inline
+ script: |
+ New-Item -Path "$(Build.StagingDirectory)/ReleaseConfigs" -ItemType Directory -Force
+ $filePath = "$(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt"
+ Add-Content -Path $filePath -Value $(BARBuildId)
+ Add-Content -Path $filePath -Value "$(DefaultChannels)"
+ Add-Content -Path $filePath -Value $(IsStableBuild)
+
+ - task: 1ES.PublishBuildArtifacts@1
+ displayName: Publish ReleaseConfigs Artifact
+ inputs:
+ PathtoPublish: '$(Build.StagingDirectory)/ReleaseConfigs'
+ PublishLocation: Container
+ ArtifactName: ReleaseConfigs
+
+ - task: powershell@2
+ displayName: Check if SymbolPublishingExclusionsFile.txt exists
+ inputs:
+ targetType: inline
+ script: |
+ $symbolExclusionfile = "$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt"
+ if(Test-Path -Path $symbolExclusionfile)
+ {
+ Write-Host "SymbolExclusionFile exists"
+ Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]true"
+ }
+ else{
+ Write-Host "Symbols Exclusion file does not exists"
+ Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]false"
+ }
+
+ - task: 1ES.PublishBuildArtifacts@1
+ displayName: Publish SymbolPublishingExclusionsFile Artifact
+ condition: eq(variables['SymbolExclusionFile'], 'true')
+ inputs:
+ PathtoPublish: '$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt'
+ PublishLocation: Container
+ ArtifactName: ReleaseConfigs
+
+ - ${{ if eq(parameters.publishAssetsImmediately, 'true') }}:
+ - template: /eng/common/templates-official/post-build/setup-maestro-vars.yml
+ parameters:
+ BARBuildId: ${{ parameters.BARBuildId }}
+ PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }}
+
+ - task: PowerShell@2
+ displayName: Publish Using Darc
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1
+ arguments: -BuildId $(BARBuildId)
+ -PublishingInfraVersion 3
+ -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)'
+ -MaestroToken '$(MaestroApiAccessToken)'
+ -WaitPublishingFinish true
+ -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}'
+ -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}'
+
+ - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}:
+ - template: /eng/common/templates-official/steps/publish-logs.yml
+ parameters:
+ JobLabel: 'Publish_Artifacts_Logs'
diff --git a/eng/common/templates-official/job/source-build.yml b/eng/common/templates-official/job/source-build.yml
new file mode 100644
index 00000000000..f193dfbe236
--- /dev/null
+++ b/eng/common/templates-official/job/source-build.yml
@@ -0,0 +1,67 @@
+parameters:
+ # This template adds arcade-powered source-build to CI. The template produces a server job with a
+ # default ID 'Source_Build_Complete' to put in a dependency list if necessary.
+
+ # Specifies the prefix for source-build jobs added to pipeline. Use this if disambiguation needed.
+ jobNamePrefix: 'Source_Build'
+
+ # Defines the platform on which to run the job. By default, a linux-x64 machine, suitable for
+ # managed-only repositories. This is an object with these properties:
+ #
+ # name: ''
+ # The name of the job. This is included in the job ID.
+ # targetRID: ''
+ # The name of the target RID to use, instead of the one auto-detected by Arcade.
+ # nonPortable: false
+ # Enables non-portable mode. This means a more specific RID (e.g. fedora.32-x64 rather than
+ # linux-x64), and compiling against distro-provided packages rather than portable ones.
+ # skipPublishValidation: false
+ # Disables publishing validation. By default, a check is performed to ensure no packages are
+ # published by source-build.
+ # container: ''
+ # A container to use. Runs in docker.
+ # pool: {}
+ # A pool to use. Runs directly on an agent.
+ # buildScript: ''
+ # Specifies the build script to invoke to perform the build in the repo. The default
+ # './build.sh' should work for typical Arcade repositories, but this is customizable for
+ # difficult situations.
+ # jobProperties: {}
+ # A list of job properties to inject at the top level, for potential extensibility beyond
+ # container and pool.
+ platform: {}
+
+jobs:
+- job: ${{ parameters.jobNamePrefix }}_${{ parameters.platform.name }}
+ displayName: Source-Build (${{ parameters.platform.name }})
+
+ ${{ each property in parameters.platform.jobProperties }}:
+ ${{ property.key }}: ${{ property.value }}
+
+ ${{ if ne(parameters.platform.container, '') }}:
+ container: ${{ parameters.platform.container }}
+
+ ${{ if eq(parameters.platform.pool, '') }}:
+ # The default VM host AzDO pool. This should be capable of running Docker containers: almost all
+ # source-build builds run in Docker, including the default managed platform.
+ # /eng/common/templates-official/variables/pool-providers.yml can't be used here (some customers declare variables already), so duplicate its logic
+ pool:
+ ${{ if eq(variables['System.TeamProject'], 'public') }}:
+ name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')]
+ demands: ImageOverride -equals Build.Ubuntu.1804.Amd64.Open
+
+ ${{ if eq(variables['System.TeamProject'], 'internal') }}:
+ name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')]
+ image: 1es-mariner-2
+ os: linux
+
+ ${{ if ne(parameters.platform.pool, '') }}:
+ pool: ${{ parameters.platform.pool }}
+
+ workspace:
+ clean: all
+
+ steps:
+ - template: /eng/common/templates-official/steps/source-build.yml
+ parameters:
+ platform: ${{ parameters.platform }}
diff --git a/eng/common/templates-official/job/source-index-stage1.yml b/eng/common/templates-official/job/source-index-stage1.yml
new file mode 100644
index 00000000000..f0513aee5b0
--- /dev/null
+++ b/eng/common/templates-official/job/source-index-stage1.yml
@@ -0,0 +1,68 @@
+parameters:
+ runAsPublic: false
+ sourceIndexPackageVersion: 1.0.1-20230228.2
+ 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
+ condition: ''
+ dependsOn: ''
+ pool: ''
+
+jobs:
+- job: SourceIndexStage1
+ dependsOn: ${{ parameters.dependsOn }}
+ condition: ${{ parameters.condition }}
+ variables:
+ - name: SourceIndexPackageVersion
+ value: ${{ parameters.sourceIndexPackageVersion }}
+ - name: SourceIndexPackageSource
+ value: ${{ parameters.sourceIndexPackageSource }}
+ - name: BinlogPath
+ value: ${{ parameters.binlogPath }}
+ - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
+ - group: source-dot-net stage1 variables
+ - template: /eng/common/templates-official/variables/pool-providers.yml
+
+ ${{ if ne(parameters.pool, '') }}:
+ pool: ${{ parameters.pool }}
+ ${{ if eq(parameters.pool, '') }}:
+ pool:
+ ${{ if eq(variables['System.TeamProject'], 'public') }}:
+ name: $(DncEngPublicBuildPool)
+ demands: ImageOverride -equals windows.vs2019.amd64.open
+ ${{ if eq(variables['System.TeamProject'], 'internal') }}:
+ name: $(DncEngInternalBuildPool)
+ image: windows.vs2022.amd64
+ os: windows
+
+ steps:
+ - ${{ each preStep in parameters.preSteps }}:
+ - ${{ preStep }}
+
+ - task: UseDotNet@2
+ displayName: Use .NET Core SDK 6
+ inputs:
+ packageType: sdk
+ version: 6.0.x
+ installationPath: $(Agent.TempDirectory)/dotnet
+ workingDirectory: $(Agent.TempDirectory)
+
+ - script: |
+ $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools
+ $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(SourceIndexPackageVersion) --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')) }}:
+ - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name)
+ displayName: Upload stage1 artifacts to source index
+ env:
+ BLOB_CONTAINER_URL: $(source-dot-net-stage1-blob-container-url)
diff --git a/eng/common/templates-official/jobs/codeql-build.yml b/eng/common/templates-official/jobs/codeql-build.yml
new file mode 100644
index 00000000000..b68d3c2f319
--- /dev/null
+++ b/eng/common/templates-official/jobs/codeql-build.yml
@@ -0,0 +1,31 @@
+parameters:
+ # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md
+ continueOnError: false
+ # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job
+ jobs: []
+ # Optional: if specified, restore and use this version of Guardian instead of the default.
+ overrideGuardianVersion: ''
+
+jobs:
+- template: /eng/common/templates-official/jobs/jobs.yml
+ parameters:
+ enableMicrobuild: false
+ enablePublishBuildArtifacts: false
+ enablePublishTestResults: false
+ enablePublishBuildAssets: false
+ enablePublishUsingPipelines: false
+ enableTelemetry: true
+
+ variables:
+ - group: Publish-Build-Assets
+ # The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in
+ # sync with the packages.config file.
+ - name: DefaultGuardianVersion
+ value: 0.109.0
+ - name: GuardianPackagesConfigFile
+ value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config
+ - name: GuardianVersion
+ value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }}
+
+ jobs: ${{ parameters.jobs }}
+
diff --git a/eng/common/templates-official/jobs/jobs.yml b/eng/common/templates-official/jobs/jobs.yml
new file mode 100644
index 00000000000..857a0f8ba43
--- /dev/null
+++ b/eng/common/templates-official/jobs/jobs.yml
@@ -0,0 +1,97 @@
+parameters:
+ # See schema documentation in /Documentation/AzureDevOps/TemplateSchema.md
+ continueOnError: false
+
+ # Optional: Include PublishBuildArtifacts task
+ enablePublishBuildArtifacts: false
+
+ # Optional: Enable publishing using release pipelines
+ enablePublishUsingPipelines: false
+
+ # Optional: Enable running the source-build jobs to build repo from source
+ enableSourceBuild: false
+
+ # Optional: Parameters for source-build template.
+ # See /eng/common/templates-official/jobs/source-build.yml for options
+ sourceBuildParameters: []
+
+ graphFileGeneration:
+ # Optional: Enable generating the graph files at the end of the build
+ enabled: false
+ # Optional: Include toolset dependencies in the generated graph files
+ includeToolset: false
+
+ # Required: A collection of jobs to run - https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=vsts&tabs=schema#job
+ jobs: []
+
+ # Optional: Override automatically derived dependsOn value for "publish build assets" job
+ publishBuildAssetsDependsOn: ''
+
+ # Optional: Publish the assets as soon as the publish to BAR stage is complete, rather doing so in a separate stage.
+ publishAssetsImmediately: false
+
+ # Optional: If using publishAssetsImmediately and additional parameters are needed, can be used to send along additional parameters (normally sent to post-build.yml)
+ artifactsPublishingAdditionalParameters: ''
+ signingValidationAdditionalParameters: ''
+
+ # Optional: should run as a public build even in the internal project
+ # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects.
+ runAsPublic: false
+
+ enableSourceIndex: false
+ sourceIndexParams: {}
+
+# Internal resources (telemetry, microbuild) can only be accessed from non-public projects,
+# and some (Microbuild) should only be applied to non-PR cases for internal builds.
+
+jobs:
+- ${{ each job in parameters.jobs }}:
+ - template: ../job/job.yml
+ parameters:
+ # pass along parameters
+ ${{ each parameter in parameters }}:
+ ${{ if ne(parameter.key, 'jobs') }}:
+ ${{ parameter.key }}: ${{ parameter.value }}
+
+ # pass along job properties
+ ${{ each property in job }}:
+ ${{ if ne(property.key, 'job') }}:
+ ${{ property.key }}: ${{ property.value }}
+
+ name: ${{ job.job }}
+
+- ${{ if eq(parameters.enableSourceBuild, true) }}:
+ - template: /eng/common/templates-official/jobs/source-build.yml
+ parameters:
+ allCompletedJobId: Source_Build_Complete
+ ${{ each parameter in parameters.sourceBuildParameters }}:
+ ${{ parameter.key }}: ${{ parameter.value }}
+
+- ${{ if eq(parameters.enableSourceIndex, 'true') }}:
+ - template: ../job/source-index-stage1.yml
+ parameters:
+ runAsPublic: ${{ parameters.runAsPublic }}
+ ${{ each parameter in parameters.sourceIndexParams }}:
+ ${{ parameter.key }}: ${{ parameter.value }}
+
+- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
+ - ${{ if or(eq(parameters.enablePublishBuildAssets, true), eq(parameters.artifacts.publish.manifests, 'true'), ne(parameters.artifacts.publish.manifests, '')) }}:
+ - template: ../job/publish-build-assets.yml
+ parameters:
+ continueOnError: ${{ parameters.continueOnError }}
+ dependsOn:
+ - ${{ if ne(parameters.publishBuildAssetsDependsOn, '') }}:
+ - ${{ each job in parameters.publishBuildAssetsDependsOn }}:
+ - ${{ job.job }}
+ - ${{ if eq(parameters.publishBuildAssetsDependsOn, '') }}:
+ - ${{ each job in parameters.jobs }}:
+ - ${{ job.job }}
+ - ${{ if eq(parameters.enableSourceBuild, true) }}:
+ - Source_Build_Complete
+
+ runAsPublic: ${{ parameters.runAsPublic }}
+ publishUsingPipelines: ${{ parameters.enablePublishUsingPipelines }}
+ publishAssetsImmediately: ${{ parameters.publishAssetsImmediately }}
+ enablePublishBuildArtifacts: ${{ parameters.enablePublishBuildArtifacts }}
+ artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }}
+ signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }}
diff --git a/eng/common/templates-official/jobs/source-build.yml b/eng/common/templates-official/jobs/source-build.yml
new file mode 100644
index 00000000000..08e5db9bb11
--- /dev/null
+++ b/eng/common/templates-official/jobs/source-build.yml
@@ -0,0 +1,46 @@
+parameters:
+ # This template adds arcade-powered source-build to CI. A job is created for each platform, as
+ # well as an optional server job that completes when all platform jobs complete.
+
+ # The name of the "join" job for all source-build platforms. If set to empty string, the job is
+ # not included. Existing repo pipelines can use this job depend on all source-build jobs
+ # completing without maintaining a separate list of every single job ID: just depend on this one
+ # server job. By default, not included. Recommended name if used: 'Source_Build_Complete'.
+ allCompletedJobId: ''
+
+ # See /eng/common/templates-official/job/source-build.yml
+ jobNamePrefix: 'Source_Build'
+
+ # This is the default platform provided by Arcade, intended for use by a managed-only repo.
+ defaultManagedPlatform:
+ name: 'Managed'
+ container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-stream8'
+
+ # Defines the platforms on which to run build jobs. One job is created for each platform, and the
+ # object in this array is sent to the job template as 'platform'. If no platforms are specified,
+ # one job runs on 'defaultManagedPlatform'.
+ platforms: []
+
+jobs:
+
+- ${{ if ne(parameters.allCompletedJobId, '') }}:
+ - job: ${{ parameters.allCompletedJobId }}
+ displayName: Source-Build Complete
+ pool: server
+ dependsOn:
+ - ${{ each platform in parameters.platforms }}:
+ - ${{ parameters.jobNamePrefix }}_${{ platform.name }}
+ - ${{ if eq(length(parameters.platforms), 0) }}:
+ - ${{ parameters.jobNamePrefix }}_${{ parameters.defaultManagedPlatform.name }}
+
+- ${{ each platform in parameters.platforms }}:
+ - template: /eng/common/templates-official/job/source-build.yml
+ parameters:
+ jobNamePrefix: ${{ parameters.jobNamePrefix }}
+ platform: ${{ platform }}
+
+- ${{ if eq(length(parameters.platforms), 0) }}:
+ - template: /eng/common/templates-official/job/source-build.yml
+ parameters:
+ jobNamePrefix: ${{ parameters.jobNamePrefix }}
+ platform: ${{ parameters.defaultManagedPlatform }}
diff --git a/eng/common/templates-official/post-build/common-variables.yml b/eng/common/templates-official/post-build/common-variables.yml
new file mode 100644
index 00000000000..c24193acfc9
--- /dev/null
+++ b/eng/common/templates-official/post-build/common-variables.yml
@@ -0,0 +1,22 @@
+variables:
+ - group: Publish-Build-Assets
+
+ # Whether the build is internal or not
+ - name: IsInternalBuild
+ value: ${{ and(ne(variables['System.TeamProject'], 'public'), contains(variables['Build.SourceBranch'], 'internal')) }}
+
+ # Default Maestro++ API Endpoint and API Version
+ - name: MaestroApiEndPoint
+ value: "https://maestro-prod.westus2.cloudapp.azure.com"
+ - name: MaestroApiAccessToken
+ value: $(MaestroAccessToken)
+ - name: MaestroApiVersion
+ value: "2020-02-20"
+
+ - name: SourceLinkCLIVersion
+ value: 3.0.0
+ - name: SymbolToolVersion
+ value: 1.0.1
+
+ - name: runCodesignValidationInjection
+ value: false
diff --git a/eng/common/templates-official/post-build/post-build.yml b/eng/common/templates-official/post-build/post-build.yml
new file mode 100644
index 00000000000..da1f40958b4
--- /dev/null
+++ b/eng/common/templates-official/post-build/post-build.yml
@@ -0,0 +1,285 @@
+parameters:
+ # Which publishing infra should be used. THIS SHOULD MATCH THE VERSION ON THE BUILD MANIFEST.
+ # Publishing V1 is no longer supported
+ # Publishing V2 is no longer supported
+ # Publishing V3 is the default
+ - name: publishingInfraVersion
+ displayName: Which version of publishing should be used to promote the build definition?
+ type: number
+ default: 3
+ values:
+ - 3
+
+ - name: BARBuildId
+ displayName: BAR Build Id
+ type: number
+ default: 0
+
+ - name: PromoteToChannelIds
+ displayName: Channel to promote BARBuildId to
+ type: string
+ default: ''
+
+ - name: enableSourceLinkValidation
+ displayName: Enable SourceLink validation
+ type: boolean
+ default: false
+
+ - name: enableSigningValidation
+ displayName: Enable signing validation
+ type: boolean
+ default: true
+
+ - name: enableSymbolValidation
+ displayName: Enable symbol validation
+ type: boolean
+ default: false
+
+ - name: enableNugetValidation
+ displayName: Enable NuGet validation
+ type: boolean
+ default: true
+
+ - name: publishInstallersAndChecksums
+ displayName: Publish installers and checksums
+ type: boolean
+ default: true
+
+ - name: SDLValidationParameters
+ type: object
+ default:
+ enable: false
+ publishGdn: false
+ continueOnError: false
+ params: ''
+ artifactNames: ''
+ downloadArtifacts: true
+
+ # These parameters let the user customize the call to sdk-task.ps1 for publishing
+ # symbols & general artifacts as well as for signing validation
+ - name: symbolPublishingAdditionalParameters
+ displayName: Symbol publishing additional parameters
+ type: string
+ default: ''
+
+ - name: artifactsPublishingAdditionalParameters
+ displayName: Artifact publishing additional parameters
+ type: string
+ default: ''
+
+ - name: signingValidationAdditionalParameters
+ displayName: Signing validation additional parameters
+ type: string
+ default: ''
+
+ # Which stages should finish execution before post-build stages start
+ - name: validateDependsOn
+ type: object
+ default:
+ - build
+
+ - name: publishDependsOn
+ type: object
+ default:
+ - Validate
+
+ # Optional: Call asset publishing rather than running in a separate stage
+ - name: publishAssetsImmediately
+ type: boolean
+ default: false
+
+stages:
+- ${{ if or(eq( parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}:
+ - stage: Validate
+ dependsOn: ${{ parameters.validateDependsOn }}
+ displayName: Validate Build Assets
+ variables:
+ - template: common-variables.yml
+ - template: /eng/common/templates-official/variables/pool-providers.yml
+ jobs:
+ - job:
+ displayName: NuGet Validation
+ condition: and(succeededOrFailed(), eq( ${{ parameters.enableNugetValidation }}, 'true'))
+ pool:
+ # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com)
+ ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}:
+ name: AzurePipelines-EO
+ image: 1ESPT-Windows2022
+ demands: Cmd
+ os: windows
+ # If it's not devdiv, it's dnceng
+ ${{ else }}:
+ name: $(DncEngInternalBuildPool)
+ image: 1es-windows-2022
+ os: windows
+
+ steps:
+ - template: setup-maestro-vars.yml
+ parameters:
+ BARBuildId: ${{ parameters.BARBuildId }}
+ PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }}
+
+ - task: DownloadBuildArtifacts@0
+ displayName: Download Package Artifacts
+ inputs:
+ buildType: specific
+ buildVersionToDownload: specific
+ project: $(AzDOProjectName)
+ pipeline: $(AzDOPipelineId)
+ buildId: $(AzDOBuildId)
+ artifactName: PackageArtifacts
+ checkDownloadedFiles: true
+
+ - task: PowerShell@2
+ displayName: Validate
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/post-build/nuget-validation.ps1
+ arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/
+ -ToolDestinationPath $(Agent.BuildDirectory)/Extract/
+
+ - job:
+ displayName: Signing Validation
+ condition: and( eq( ${{ parameters.enableSigningValidation }}, 'true'), ne( variables['PostBuildSign'], 'true'))
+ pool:
+ # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com)
+ ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}:
+ name: AzurePipelines-EO
+ image: 1ESPT-Windows2022
+ demands: Cmd
+ os: windows
+ # If it's not devdiv, it's dnceng
+ ${{ else }}:
+ name: $(DncEngInternalBuildPool)
+ image: 1es-windows-2022
+ os: windows
+ steps:
+ - template: setup-maestro-vars.yml
+ parameters:
+ BARBuildId: ${{ parameters.BARBuildId }}
+ PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }}
+
+ - task: DownloadBuildArtifacts@0
+ displayName: Download Package Artifacts
+ inputs:
+ buildType: specific
+ buildVersionToDownload: specific
+ project: $(AzDOProjectName)
+ pipeline: $(AzDOPipelineId)
+ buildId: $(AzDOBuildId)
+ artifactName: PackageArtifacts
+ checkDownloadedFiles: true
+ itemPattern: |
+ **
+ !**/Microsoft.SourceBuild.Intermediate.*.nupkg
+
+ # This is necessary whenever we want to publish/restore to an AzDO private feed
+ # Since sdk-task.ps1 tries to restore packages we need to do this authentication here
+ # otherwise it'll complain about accessing a private feed.
+ - task: NuGetAuthenticate@1
+ displayName: 'Authenticate to AzDO Feeds'
+
+ # Signing validation will optionally work with the buildmanifest file which is downloaded from
+ # Azure DevOps above.
+ - task: PowerShell@2
+ displayName: Validate
+ inputs:
+ filePath: eng\common\sdk-task.ps1
+ arguments: -task SigningValidation -restore -msbuildEngine vs
+ /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts'
+ /p:SignCheckExclusionsFile='$(Build.SourcesDirectory)/eng/SignCheckExclusionsFile.txt'
+ ${{ parameters.signingValidationAdditionalParameters }}
+
+ - template: ../steps/publish-logs.yml
+ parameters:
+ StageLabel: 'Validation'
+ JobLabel: 'Signing'
+ BinlogToolVersion: $(BinlogToolVersion)
+
+ - job:
+ displayName: SourceLink Validation
+ condition: eq( ${{ parameters.enableSourceLinkValidation }}, 'true')
+ pool:
+ # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com)
+ ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}:
+ name: AzurePipelines-EO
+ image: 1ESPT-Windows2022
+ demands: Cmd
+ os: windows
+ # If it's not devdiv, it's dnceng
+ ${{ else }}:
+ name: $(DncEngInternalBuildPool)
+ image: 1es-windows-2022
+ os: windows
+ steps:
+ - template: setup-maestro-vars.yml
+ parameters:
+ BARBuildId: ${{ parameters.BARBuildId }}
+ PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }}
+
+ - task: DownloadBuildArtifacts@0
+ displayName: Download Blob Artifacts
+ inputs:
+ buildType: specific
+ buildVersionToDownload: specific
+ project: $(AzDOProjectName)
+ pipeline: $(AzDOPipelineId)
+ buildId: $(AzDOBuildId)
+ artifactName: BlobArtifacts
+ checkDownloadedFiles: true
+
+ - task: PowerShell@2
+ displayName: Validate
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/post-build/sourcelink-validation.ps1
+ arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/
+ -ExtractPath $(Agent.BuildDirectory)/Extract/
+ -GHRepoName $(Build.Repository.Name)
+ -GHCommit $(Build.SourceVersion)
+ -SourcelinkCliVersion $(SourceLinkCLIVersion)
+ continueOnError: true
+
+- ${{ if ne(parameters.publishAssetsImmediately, 'true') }}:
+ - stage: publish_using_darc
+ ${{ if or(eq(parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}:
+ dependsOn: ${{ parameters.publishDependsOn }}
+ ${{ else }}:
+ dependsOn: ${{ parameters.validateDependsOn }}
+ displayName: Publish using Darc
+ variables:
+ - template: common-variables.yml
+ - template: /eng/common/templates-official/variables/pool-providers.yml
+ jobs:
+ - job:
+ displayName: Publish Using Darc
+ timeoutInMinutes: 120
+ pool:
+ # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com)
+ ${{ if eq(variables['System.TeamProject'], 'DevDiv') }}:
+ name: AzurePipelines-EO
+ image: 1ESPT-Windows2022
+ demands: Cmd
+ os: windows
+ # If it's not devdiv, it's dnceng
+ ${{ else }}:
+ name: NetCore1ESPool-Publishing-Internal
+ image: windows.vs2019.amd64
+ os: windows
+ steps:
+ - template: setup-maestro-vars.yml
+ parameters:
+ BARBuildId: ${{ parameters.BARBuildId }}
+ PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }}
+
+ - task: NuGetAuthenticate@1
+
+ - task: PowerShell@2
+ displayName: Publish Using Darc
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1
+ arguments: -BuildId $(BARBuildId)
+ -PublishingInfraVersion ${{ parameters.publishingInfraVersion }}
+ -AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)'
+ -MaestroToken '$(MaestroApiAccessToken)'
+ -WaitPublishingFinish true
+ -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}'
+ -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}'
diff --git a/eng/common/templates-official/post-build/setup-maestro-vars.yml b/eng/common/templates-official/post-build/setup-maestro-vars.yml
new file mode 100644
index 00000000000..0c87f149a4a
--- /dev/null
+++ b/eng/common/templates-official/post-build/setup-maestro-vars.yml
@@ -0,0 +1,70 @@
+parameters:
+ BARBuildId: ''
+ PromoteToChannelIds: ''
+
+steps:
+ - ${{ if eq(coalesce(parameters.PromoteToChannelIds, 0), 0) }}:
+ - task: DownloadBuildArtifacts@0
+ displayName: Download Release Configs
+ inputs:
+ buildType: current
+ artifactName: ReleaseConfigs
+ checkDownloadedFiles: true
+
+ - task: PowerShell@2
+ name: setReleaseVars
+ displayName: Set Release Configs Vars
+ inputs:
+ targetType: inline
+ pwsh: true
+ script: |
+ try {
+ if (!$Env:PromoteToMaestroChannels -or $Env:PromoteToMaestroChannels.Trim() -eq '') {
+ $Content = Get-Content $(Build.StagingDirectory)/ReleaseConfigs/ReleaseConfigs.txt
+
+ $BarId = $Content | Select -Index 0
+ $Channels = $Content | Select -Index 1
+ $IsStableBuild = $Content | Select -Index 2
+
+ $AzureDevOpsProject = $Env:System_TeamProject
+ $AzureDevOpsBuildDefinitionId = $Env:System_DefinitionId
+ $AzureDevOpsBuildId = $Env:Build_BuildId
+ }
+ else {
+ $buildApiEndpoint = "${Env:MaestroApiEndPoint}/api/builds/${Env:BARBuildId}?api-version=${Env:MaestroApiVersion}"
+
+ $apiHeaders = New-Object 'System.Collections.Generic.Dictionary[[String],[String]]'
+ $apiHeaders.Add('Accept', 'application/json')
+ $apiHeaders.Add('Authorization',"Bearer ${Env:MAESTRO_API_TOKEN}")
+
+ $buildInfo = try { Invoke-WebRequest -Method Get -Uri $buildApiEndpoint -Headers $apiHeaders | ConvertFrom-Json } catch { Write-Host "Error: $_" }
+
+ $BarId = $Env:BARBuildId
+ $Channels = $Env:PromoteToMaestroChannels -split ","
+ $Channels = $Channels -join "]["
+ $Channels = "[$Channels]"
+
+ $IsStableBuild = $buildInfo.stable
+ $AzureDevOpsProject = $buildInfo.azureDevOpsProject
+ $AzureDevOpsBuildDefinitionId = $buildInfo.azureDevOpsBuildDefinitionId
+ $AzureDevOpsBuildId = $buildInfo.azureDevOpsBuildId
+ }
+
+ Write-Host "##vso[task.setvariable variable=BARBuildId]$BarId"
+ Write-Host "##vso[task.setvariable variable=TargetChannels]$Channels"
+ Write-Host "##vso[task.setvariable variable=IsStableBuild]$IsStableBuild"
+
+ Write-Host "##vso[task.setvariable variable=AzDOProjectName]$AzureDevOpsProject"
+ Write-Host "##vso[task.setvariable variable=AzDOPipelineId]$AzureDevOpsBuildDefinitionId"
+ Write-Host "##vso[task.setvariable variable=AzDOBuildId]$AzureDevOpsBuildId"
+ }
+ catch {
+ Write-Host $_
+ Write-Host $_.Exception
+ Write-Host $_.ScriptStackTrace
+ exit 1
+ }
+ env:
+ MAESTRO_API_TOKEN: $(MaestroApiAccessToken)
+ BARBuildId: ${{ parameters.BARBuildId }}
+ PromoteToMaestroChannels: ${{ parameters.PromoteToChannelIds }}
diff --git a/eng/common/templates-official/post-build/trigger-subscription.yml b/eng/common/templates-official/post-build/trigger-subscription.yml
new file mode 100644
index 00000000000..da669030daf
--- /dev/null
+++ b/eng/common/templates-official/post-build/trigger-subscription.yml
@@ -0,0 +1,13 @@
+parameters:
+ ChannelId: 0
+
+steps:
+- task: PowerShell@2
+ displayName: Triggering subscriptions
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/post-build/trigger-subscriptions.ps1
+ arguments: -SourceRepo $(Build.Repository.Uri)
+ -ChannelId ${{ parameters.ChannelId }}
+ -MaestroApiAccessToken $(MaestroAccessToken)
+ -MaestroApiEndPoint $(MaestroApiEndPoint)
+ -MaestroApiVersion $(MaestroApiVersion)
diff --git a/eng/common/templates-official/steps/add-build-to-channel.yml b/eng/common/templates-official/steps/add-build-to-channel.yml
new file mode 100644
index 00000000000..f67a210d62f
--- /dev/null
+++ b/eng/common/templates-official/steps/add-build-to-channel.yml
@@ -0,0 +1,13 @@
+parameters:
+ ChannelId: 0
+
+steps:
+- task: PowerShell@2
+ displayName: Add Build to Channel
+ inputs:
+ filePath: $(Build.SourcesDirectory)/eng/common/post-build/add-build-to-channel.ps1
+ arguments: -BuildId $(BARBuildId)
+ -ChannelId ${{ parameters.ChannelId }}
+ -MaestroApiAccessToken $(MaestroApiAccessToken)
+ -MaestroApiEndPoint $(MaestroApiEndPoint)
+ -MaestroApiVersion $(MaestroApiVersion)
diff --git a/eng/common/templates-official/steps/build-reason.yml b/eng/common/templates-official/steps/build-reason.yml
new file mode 100644
index 00000000000..eba58109b52
--- /dev/null
+++ b/eng/common/templates-official/steps/build-reason.yml
@@ -0,0 +1,12 @@
+# build-reason.yml
+# Description: runs steps if build.reason condition is valid. conditions is a string of valid build reasons
+# to include steps (',' separated).
+parameters:
+ conditions: ''
+ steps: []
+
+steps:
+ - ${{ if and( not(startsWith(parameters.conditions, 'not')), contains(parameters.conditions, variables['build.reason'])) }}:
+ - ${{ parameters.steps }}
+ - ${{ if and( startsWith(parameters.conditions, 'not'), not(contains(parameters.conditions, variables['build.reason']))) }}:
+ - ${{ parameters.steps }}
diff --git a/eng/common/templates-official/steps/component-governance.yml b/eng/common/templates-official/steps/component-governance.yml
new file mode 100644
index 00000000000..cbba0596709
--- /dev/null
+++ b/eng/common/templates-official/steps/component-governance.yml
@@ -0,0 +1,13 @@
+parameters:
+ disableComponentGovernance: false
+ componentGovernanceIgnoreDirectories: ''
+
+steps:
+- ${{ if eq(parameters.disableComponentGovernance, 'true') }}:
+ - script: echo "##vso[task.setvariable variable=skipComponentGovernanceDetection]true"
+ displayName: Set skipComponentGovernanceDetection variable
+- ${{ if ne(parameters.disableComponentGovernance, 'true') }}:
+ - task: ComponentGovernanceComponentDetection@0
+ continueOnError: true
+ inputs:
+ ignoreDirectories: ${{ parameters.componentGovernanceIgnoreDirectories }}
\ No newline at end of file
diff --git a/eng/common/templates-official/steps/execute-codeql.yml b/eng/common/templates-official/steps/execute-codeql.yml
new file mode 100644
index 00000000000..9b4a5ffa30a
--- /dev/null
+++ b/eng/common/templates-official/steps/execute-codeql.yml
@@ -0,0 +1,32 @@
+parameters:
+ # Language that should be analyzed. Defaults to csharp
+ language: csharp
+ # Build Commands
+ buildCommands: ''
+ overrideParameters: '' # Optional: to override values for parameters.
+ additionalParameters: '' # Optional: parameters that need user specific values eg: '-SourceToolsList @("abc","def") -ArtifactToolsList @("ghi","jkl")'
+ # Optional: if specified, restore and use this version of Guardian instead of the default.
+ overrideGuardianVersion: ''
+ # Optional: if true, publish the '.gdn' folder as a pipeline artifact. This can help with in-depth
+ # diagnosis of problems with specific tool configurations.
+ publishGuardianDirectoryToPipeline: false
+ # The script to run to execute all SDL tools. Use this if you want to use a script to define SDL
+ # parameters rather than relying on YAML. It may be better to use a local script, because you can
+ # reproduce results locally without piecing together a command based on the YAML.
+ executeAllSdlToolsScript: 'eng/common/sdl/execute-all-sdl-tools.ps1'
+ # There is some sort of bug (has been reported) in Azure DevOps where if this parameter is named
+ # 'continueOnError', the parameter value is not correctly picked up.
+ # This can also be remedied by the caller (post-build.yml) if it does not use a nested parameter
+ # optional: determines whether to continue the build if the step errors;
+ sdlContinueOnError: false
+
+steps:
+- template: /eng/common/templates-official/steps/execute-sdl.yml
+ parameters:
+ overrideGuardianVersion: ${{ parameters.overrideGuardianVersion }}
+ executeAllSdlToolsScript: ${{ parameters.executeAllSdlToolsScript }}
+ overrideParameters: ${{ parameters.overrideParameters }}
+ additionalParameters: '${{ parameters.additionalParameters }}
+ -CodeQLAdditionalRunConfigParams @("BuildCommands < ${{ parameters.buildCommands }}", "Language < ${{ parameters.language }}")'
+ publishGuardianDirectoryToPipeline: ${{ parameters.publishGuardianDirectoryToPipeline }}
+ sdlContinueOnError: ${{ parameters.sdlContinueOnError }}
\ No newline at end of file
diff --git a/eng/common/templates-official/steps/execute-sdl.yml b/eng/common/templates-official/steps/execute-sdl.yml
new file mode 100644
index 00000000000..07426fde05d
--- /dev/null
+++ b/eng/common/templates-official/steps/execute-sdl.yml
@@ -0,0 +1,88 @@
+parameters:
+ overrideGuardianVersion: ''
+ executeAllSdlToolsScript: ''
+ overrideParameters: ''
+ additionalParameters: ''
+ publishGuardianDirectoryToPipeline: false
+ sdlContinueOnError: false
+ condition: ''
+
+steps:
+- task: NuGetAuthenticate@1
+ inputs:
+ nuGetServiceConnections: GuardianConnect
+
+- task: NuGetToolInstaller@1
+ displayName: 'Install NuGet.exe'
+
+- ${{ if ne(parameters.overrideGuardianVersion, '') }}:
+ - pwsh: |
+ Set-Location -Path $(Build.SourcesDirectory)\eng\common\sdl
+ . .\sdl.ps1
+ $guardianCliLocation = Install-Gdn -Path $(Build.SourcesDirectory)\.artifacts -Version ${{ parameters.overrideGuardianVersion }}
+ Write-Host "##vso[task.setvariable variable=GuardianCliLocation]$guardianCliLocation"
+ displayName: Install Guardian (Overridden)
+
+- ${{ if eq(parameters.overrideGuardianVersion, '') }}:
+ - pwsh: |
+ Set-Location -Path $(Build.SourcesDirectory)\eng\common\sdl
+ . .\sdl.ps1
+ $guardianCliLocation = Install-Gdn -Path $(Build.SourcesDirectory)\.artifacts
+ Write-Host "##vso[task.setvariable variable=GuardianCliLocation]$guardianCliLocation"
+ displayName: Install Guardian
+
+- ${{ if ne(parameters.overrideParameters, '') }}:
+ - powershell: ${{ parameters.executeAllSdlToolsScript }} ${{ parameters.overrideParameters }}
+ displayName: Execute SDL (Overridden)
+ continueOnError: ${{ parameters.sdlContinueOnError }}
+ condition: ${{ parameters.condition }}
+
+- ${{ if eq(parameters.overrideParameters, '') }}:
+ - powershell: ${{ parameters.executeAllSdlToolsScript }}
+ -GuardianCliLocation $(GuardianCliLocation)
+ -NugetPackageDirectory $(Build.SourcesDirectory)\.packages
+ -AzureDevOpsAccessToken $(dn-bot-dotnet-build-rw-code-rw)
+ ${{ parameters.additionalParameters }}
+ displayName: Execute SDL
+ continueOnError: ${{ parameters.sdlContinueOnError }}
+ condition: ${{ parameters.condition }}
+
+- ${{ if ne(parameters.publishGuardianDirectoryToPipeline, 'false') }}:
+ # We want to publish the Guardian results and configuration for easy diagnosis. However, the
+ # '.gdn' dir is a mix of configuration, results, extracted dependencies, and Guardian default
+ # tooling files. Some of these files are large and aren't useful during an investigation, so
+ # exclude them by simply deleting them before publishing. (As of writing, there is no documented
+ # way to selectively exclude a dir from the pipeline artifact publish task.)
+ - task: DeleteFiles@1
+ displayName: Delete Guardian dependencies to avoid uploading
+ inputs:
+ SourceFolder: $(Agent.BuildDirectory)/.gdn
+ Contents: |
+ c
+ i
+ condition: succeededOrFailed()
+
+ - publish: $(Agent.BuildDirectory)/.gdn
+ artifact: GuardianConfiguration
+ displayName: Publish GuardianConfiguration
+ condition: succeededOrFailed()
+
+ # Publish the SARIF files in a container named CodeAnalysisLogs to enable integration
+ # with the "SARIF SAST Scans Tab" Azure DevOps extension
+ - task: CopyFiles@2
+ displayName: Copy SARIF files
+ inputs:
+ flattenFolders: true
+ sourceFolder: $(Agent.BuildDirectory)/.gdn/rc/
+ contents: '**/*.sarif'
+ targetFolder: $(Build.SourcesDirectory)/CodeAnalysisLogs
+ condition: succeededOrFailed()
+
+ # Use PublishBuildArtifacts because the SARIF extension only checks this case
+ # see microsoft/sarif-azuredevops-extension#4
+ - task: PublishBuildArtifacts@1
+ displayName: Publish SARIF files to CodeAnalysisLogs container
+ inputs:
+ pathToPublish: $(Build.SourcesDirectory)/CodeAnalysisLogs
+ artifactName: CodeAnalysisLogs
+ condition: succeededOrFailed()
\ No newline at end of file
diff --git a/eng/common/templates-official/steps/generate-sbom.yml b/eng/common/templates-official/steps/generate-sbom.yml
new file mode 100644
index 00000000000..1bf43bf807a
--- /dev/null
+++ b/eng/common/templates-official/steps/generate-sbom.yml
@@ -0,0 +1,48 @@
+# BuildDropPath - The root folder of the drop directory for which the manifest file will be generated.
+# PackageName - The name of the package this SBOM represents.
+# PackageVersion - The version of the package this SBOM represents.
+# ManifestDirPath - The path of the directory where the generated manifest files will be placed
+# IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector.
+
+parameters:
+ PackageVersion: 8.0.0
+ BuildDropPath: '$(Build.SourcesDirectory)/artifacts'
+ PackageName: '.NET'
+ ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom
+ IgnoreDirectories: ''
+ sbomContinueOnError: true
+
+steps:
+- task: PowerShell@2
+ displayName: Prep for SBOM generation in (Non-linux)
+ condition: or(eq(variables['Agent.Os'], 'Windows_NT'), eq(variables['Agent.Os'], 'Darwin'))
+ inputs:
+ filePath: ./eng/common/generate-sbom-prep.ps1
+ arguments: ${{parameters.manifestDirPath}}
+
+# Chmodding is a workaround for https://github.com/dotnet/arcade/issues/8461
+- script: |
+ chmod +x ./eng/common/generate-sbom-prep.sh
+ ./eng/common/generate-sbom-prep.sh ${{parameters.manifestDirPath}}
+ displayName: Prep for SBOM generation in (Linux)
+ condition: eq(variables['Agent.Os'], 'Linux')
+ continueOnError: ${{ parameters.sbomContinueOnError }}
+
+- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0
+ displayName: 'Generate SBOM manifest'
+ continueOnError: ${{ parameters.sbomContinueOnError }}
+ inputs:
+ PackageName: ${{ parameters.packageName }}
+ BuildDropPath: ${{ parameters.buildDropPath }}
+ PackageVersion: ${{ parameters.packageVersion }}
+ ManifestDirPath: ${{ parameters.manifestDirPath }}
+ ${{ if ne(parameters.IgnoreDirectories, '') }}:
+ AdditionalComponentDetectorArgs: '--IgnoreDirectories ${{ parameters.IgnoreDirectories }}'
+
+- task: 1ES.PublishPipelineArtifact@1
+ displayName: Publish SBOM manifest
+ continueOnError: ${{parameters.sbomContinueOnError}}
+ inputs:
+ targetPath: '${{parameters.manifestDirPath}}'
+ artifactName: $(ARTIFACT_NAME)
+
diff --git a/eng/common/templates-official/steps/publish-logs.yml b/eng/common/templates-official/steps/publish-logs.yml
new file mode 100644
index 00000000000..04012fed182
--- /dev/null
+++ b/eng/common/templates-official/steps/publish-logs.yml
@@ -0,0 +1,23 @@
+parameters:
+ StageLabel: ''
+ JobLabel: ''
+
+steps:
+- task: Powershell@2
+ displayName: Prepare Binlogs to Upload
+ inputs:
+ targetType: inline
+ script: |
+ New-Item -ItemType Directory $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/
+ Move-Item -Path $(Build.SourcesDirectory)/artifacts/log/Debug/* $(Build.SourcesDirectory)/PostBuildLogs/${{parameters.StageLabel}}/${{parameters.JobLabel}}/
+ continueOnError: true
+ condition: always()
+
+- task: 1ES.PublishBuildArtifacts@1
+ displayName: Publish Logs
+ inputs:
+ PathtoPublish: '$(Build.SourcesDirectory)/PostBuildLogs'
+ PublishLocation: Container
+ ArtifactName: PostBuildLogs
+ continueOnError: true
+ condition: always()
diff --git a/eng/common/templates-official/steps/retain-build.yml b/eng/common/templates-official/steps/retain-build.yml
new file mode 100644
index 00000000000..83d97a26a01
--- /dev/null
+++ b/eng/common/templates-official/steps/retain-build.yml
@@ -0,0 +1,28 @@
+parameters:
+ # Optional azure devops PAT with build execute permissions for the build's organization,
+ # only needed if the build that should be retained ran on a different organization than
+ # the pipeline where this template is executing from
+ Token: ''
+ # Optional BuildId to retain, defaults to the current running build
+ BuildId: ''
+ # Azure devops Organization URI for the build in the https://dev.azure.com/ format.
+ # Defaults to the organization the current pipeline is running on
+ AzdoOrgUri: '$(System.CollectionUri)'
+ # Azure devops project for the build. Defaults to the project the current pipeline is running on
+ AzdoProject: '$(System.TeamProject)'
+
+steps:
+ - task: powershell@2
+ inputs:
+ targetType: 'filePath'
+ filePath: eng/common/retain-build.ps1
+ pwsh: true
+ arguments: >
+ -AzdoOrgUri: ${{parameters.AzdoOrgUri}}
+ -AzdoProject ${{parameters.AzdoProject}}
+ -Token ${{coalesce(parameters.Token, '$env:SYSTEM_ACCESSTOKEN') }}
+ -BuildId ${{coalesce(parameters.BuildId, '$env:BUILD_ID')}}
+ displayName: Enable permanent build retention
+ env:
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
+ BUILD_ID: $(Build.BuildId)
\ No newline at end of file
diff --git a/eng/common/templates-official/steps/send-to-helix.yml b/eng/common/templates-official/steps/send-to-helix.yml
new file mode 100644
index 00000000000..3eb7e2d5f84
--- /dev/null
+++ b/eng/common/templates-official/steps/send-to-helix.yml
@@ -0,0 +1,91 @@
+# Please remember to update the documentation if you make changes to these parameters!
+parameters:
+ HelixSource: 'pr/default' # required -- sources must start with pr/, official/, prodcon/, or agent/
+ HelixType: 'tests/default/' # required -- Helix telemetry which identifies what type of data this is; should include "test" for clarity and must end in '/'
+ HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number
+ HelixTargetQueues: '' # required -- semicolon-delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues
+ HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group
+ HelixConfiguration: '' # optional -- additional property attached to a job
+ HelixPreCommands: '' # optional -- commands to run before Helix work item execution
+ HelixPostCommands: '' # optional -- commands to run after Helix work item execution
+ WorkItemDirectory: '' # optional -- a payload directory to zip up and send to Helix; requires WorkItemCommand; incompatible with XUnitProjects
+ WorkItemCommand: '' # optional -- a command to execute on the payload; requires WorkItemDirectory; incompatible with XUnitProjects
+ WorkItemTimeout: '' # optional -- a timeout in TimeSpan.Parse-ready value (e.g. 00:02:00) for the work item command; requires WorkItemDirectory; incompatible with XUnitProjects
+ CorrelationPayloadDirectory: '' # optional -- a directory to zip up and send to Helix as a correlation payload
+ XUnitProjects: '' # optional -- semicolon-delimited list of XUnitProjects to parse and send to Helix; requires XUnitRuntimeTargetFramework, XUnitPublishTargetFramework, XUnitRunnerVersion, and IncludeDotNetCli=true
+ XUnitWorkItemTimeout: '' # optional -- the workitem timeout in seconds for all workitems created from the xUnit projects specified by XUnitProjects
+ XUnitPublishTargetFramework: '' # optional -- framework to use to publish your xUnit projects
+ XUnitRuntimeTargetFramework: '' # optional -- framework to use for the xUnit console runner
+ XUnitRunnerVersion: '' # optional -- version of the xUnit nuget package you wish to use on Helix; required for XUnitProjects
+ IncludeDotNetCli: false # optional -- true will download a version of the .NET CLI onto the Helix machine as a correlation payload; requires DotNetCliPackageType and DotNetCliVersion
+ DotNetCliPackageType: '' # optional -- either 'sdk', 'runtime' or 'aspnetcore-runtime'; determines whether the sdk or runtime will be sent to Helix; see https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json
+ DotNetCliVersion: '' # optional -- version of the CLI to send to Helix; based on this: https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json
+ WaitForWorkItemCompletion: true # optional -- true will make the task wait until work items have been completed and fail the build if work items fail. False is "fire and forget."
+ IsExternal: false # [DEPRECATED] -- doesn't do anything, jobs are external if HelixAccessToken is empty and Creator is set
+ HelixBaseUri: 'https://helix.dot.net/' # optional -- sets the Helix API base URI (allows targeting https://helix.int-dot.net )
+ Creator: '' # optional -- if the build is external, use this to specify who is sending the job
+ DisplayNamePrefix: 'Run Tests' # optional -- rename the beginning of the displayName of the steps in AzDO
+ condition: succeeded() # optional -- condition for step to execute; defaults to succeeded()
+ continueOnError: false # optional -- determines whether to continue the build if the step errors; defaults to false
+
+steps:
+ - powershell: 'powershell "$env:BUILD_SOURCESDIRECTORY\eng\common\msbuild.ps1 $env:BUILD_SOURCESDIRECTORY\eng\common\helixpublish.proj /restore /p:TreatWarningsAsErrors=false /t:Test /bl:$env:BUILD_SOURCESDIRECTORY\artifacts\log\$env:BuildConfig\SendToHelix.binlog"'
+ displayName: ${{ parameters.DisplayNamePrefix }} (Windows)
+ env:
+ BuildConfig: $(_BuildConfig)
+ HelixSource: ${{ parameters.HelixSource }}
+ HelixType: ${{ parameters.HelixType }}
+ HelixBuild: ${{ parameters.HelixBuild }}
+ HelixConfiguration: ${{ parameters.HelixConfiguration }}
+ HelixTargetQueues: ${{ parameters.HelixTargetQueues }}
+ HelixAccessToken: ${{ parameters.HelixAccessToken }}
+ HelixPreCommands: ${{ parameters.HelixPreCommands }}
+ HelixPostCommands: ${{ parameters.HelixPostCommands }}
+ WorkItemDirectory: ${{ parameters.WorkItemDirectory }}
+ WorkItemCommand: ${{ parameters.WorkItemCommand }}
+ WorkItemTimeout: ${{ parameters.WorkItemTimeout }}
+ CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }}
+ XUnitProjects: ${{ parameters.XUnitProjects }}
+ XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }}
+ XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }}
+ XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }}
+ XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }}
+ IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }}
+ DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }}
+ DotNetCliVersion: ${{ parameters.DotNetCliVersion }}
+ WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }}
+ HelixBaseUri: ${{ parameters.HelixBaseUri }}
+ Creator: ${{ parameters.Creator }}
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
+ condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT'))
+ continueOnError: ${{ parameters.continueOnError }}
+ - script: $BUILD_SOURCESDIRECTORY/eng/common/msbuild.sh $BUILD_SOURCESDIRECTORY/eng/common/helixpublish.proj /restore /p:TreatWarningsAsErrors=false /t:Test /bl:$BUILD_SOURCESDIRECTORY/artifacts/log/$BuildConfig/SendToHelix.binlog
+ displayName: ${{ parameters.DisplayNamePrefix }} (Unix)
+ env:
+ BuildConfig: $(_BuildConfig)
+ HelixSource: ${{ parameters.HelixSource }}
+ HelixType: ${{ parameters.HelixType }}
+ HelixBuild: ${{ parameters.HelixBuild }}
+ HelixConfiguration: ${{ parameters.HelixConfiguration }}
+ HelixTargetQueues: ${{ parameters.HelixTargetQueues }}
+ HelixAccessToken: ${{ parameters.HelixAccessToken }}
+ HelixPreCommands: ${{ parameters.HelixPreCommands }}
+ HelixPostCommands: ${{ parameters.HelixPostCommands }}
+ WorkItemDirectory: ${{ parameters.WorkItemDirectory }}
+ WorkItemCommand: ${{ parameters.WorkItemCommand }}
+ WorkItemTimeout: ${{ parameters.WorkItemTimeout }}
+ CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }}
+ XUnitProjects: ${{ parameters.XUnitProjects }}
+ XUnitWorkItemTimeout: ${{ parameters.XUnitWorkItemTimeout }}
+ XUnitPublishTargetFramework: ${{ parameters.XUnitPublishTargetFramework }}
+ XUnitRuntimeTargetFramework: ${{ parameters.XUnitRuntimeTargetFramework }}
+ XUnitRunnerVersion: ${{ parameters.XUnitRunnerVersion }}
+ IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }}
+ DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }}
+ DotNetCliVersion: ${{ parameters.DotNetCliVersion }}
+ WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }}
+ HelixBaseUri: ${{ parameters.HelixBaseUri }}
+ Creator: ${{ parameters.Creator }}
+ SYSTEM_ACCESSTOKEN: $(System.AccessToken)
+ condition: and(${{ parameters.condition }}, ne(variables['Agent.Os'], 'Windows_NT'))
+ continueOnError: ${{ parameters.continueOnError }}
diff --git a/eng/common/templates-official/steps/source-build.yml b/eng/common/templates-official/steps/source-build.yml
new file mode 100644
index 00000000000..829f17c34d1
--- /dev/null
+++ b/eng/common/templates-official/steps/source-build.yml
@@ -0,0 +1,129 @@
+parameters:
+ # This template adds arcade-powered source-build to CI.
+
+ # This is a 'steps' template, and is intended for advanced scenarios where the existing build
+ # infra has a careful build methodology that must be followed. For example, a repo
+ # (dotnet/runtime) might choose to clone the GitHub repo only once and store it as a pipeline
+ # artifact for all subsequent jobs to use, to reduce dependence on a strong network connection to
+ # GitHub. Using this steps template leaves room for that infra to be included.
+
+ # Defines the platform on which to run the steps. See 'eng/common/templates-official/job/source-build.yml'
+ # for details. The entire object is described in the 'job' template for simplicity, even though
+ # the usage of the properties on this object is split between the 'job' and 'steps' templates.
+ platform: {}
+
+steps:
+# Build. Keep it self-contained for simple reusability. (No source-build-specific job variables.)
+- script: |
+ set -x
+ df -h
+
+ # If building on the internal project, the artifact feeds variable may be available (usually only if needed)
+ # In that case, call the feed setup script to add internal feeds corresponding to public ones.
+ # In addition, add an msbuild argument to copy the WIP from the repo to the target build location.
+ # This is because SetupNuGetSources.sh will alter the current NuGet.config file, and we need to preserve those
+ # changes.
+ internalRestoreArgs=
+ if [ '$(dn-bot-dnceng-artifact-feeds-rw)' != '$''(dn-bot-dnceng-artifact-feeds-rw)' ]; then
+ # Temporarily work around https://github.com/dotnet/arcade/issues/7709
+ chmod +x $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh
+ $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh $(Build.SourcesDirectory)/NuGet.config $(dn-bot-dnceng-artifact-feeds-rw)
+ internalRestoreArgs='/p:CopyWipIntoInnerSourceBuildRepo=true'
+
+ # The 'Copy WIP' feature of source build uses git stash to apply changes from the original repo.
+ # This only works if there is a username/email configured, which won't be the case in most CI runs.
+ git config --get user.email
+ if [ $? -ne 0 ]; then
+ git config user.email dn-bot@microsoft.com
+ git config user.name dn-bot
+ fi
+ fi
+
+ # If building on the internal project, the internal storage variable may be available (usually only if needed)
+ # In that case, add variables to allow the download of internal runtimes if the specified versions are not found
+ # in the default public locations.
+ internalRuntimeDownloadArgs=
+ if [ '$(dotnetbuilds-internal-container-read-token-base64)' != '$''(dotnetbuilds-internal-container-read-token-base64)' ]; then
+ internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://dotnetbuilds.blob.core.windows.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) --runtimesourcefeed https://dotnetbuilds.blob.core.windows.net/internal --runtimesourcefeedkey $(dotnetbuilds-internal-container-read-token-base64)'
+ fi
+
+ buildConfig=Release
+ # Check if AzDO substitutes in a build config from a variable, and use it if so.
+ if [ '$(_BuildConfig)' != '$''(_BuildConfig)' ]; then
+ buildConfig='$(_BuildConfig)'
+ fi
+
+ officialBuildArgs=
+ if [ '${{ and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}' = 'True' ]; then
+ officialBuildArgs='/p:DotNetPublishUsingPipelines=true /p:OfficialBuildId=$(BUILD.BUILDNUMBER)'
+ fi
+
+ targetRidArgs=
+ if [ '${{ parameters.platform.targetRID }}' != '' ]; then
+ targetRidArgs='/p:TargetRid=${{ parameters.platform.targetRID }}'
+ fi
+
+ runtimeOsArgs=
+ if [ '${{ parameters.platform.runtimeOS }}' != '' ]; then
+ runtimeOsArgs='/p:RuntimeOS=${{ parameters.platform.runtimeOS }}'
+ fi
+
+ baseOsArgs=
+ if [ '${{ parameters.platform.baseOS }}' != '' ]; then
+ baseOsArgs='/p:BaseOS=${{ parameters.platform.baseOS }}'
+ fi
+
+ publishArgs=
+ if [ '${{ parameters.platform.skipPublishValidation }}' != 'true' ]; then
+ publishArgs='--publish'
+ fi
+
+ assetManifestFileName=SourceBuild_RidSpecific.xml
+ if [ '${{ parameters.platform.name }}' != '' ]; then
+ assetManifestFileName=SourceBuild_${{ parameters.platform.name }}.xml
+ fi
+
+ ${{ coalesce(parameters.platform.buildScript, './build.sh') }} --ci \
+ --configuration $buildConfig \
+ --restore --build --pack $publishArgs -bl \
+ $officialBuildArgs \
+ $internalRuntimeDownloadArgs \
+ $internalRestoreArgs \
+ $targetRidArgs \
+ $runtimeOsArgs \
+ $baseOsArgs \
+ /p:SourceBuildNonPortable=${{ parameters.platform.nonPortable }} \
+ /p:ArcadeBuildFromSource=true \
+ /p:AssetManifestFileName=$assetManifestFileName
+ displayName: Build
+
+# Upload build logs for diagnosis.
+- task: CopyFiles@2
+ displayName: Prepare BuildLogs staging directory
+ inputs:
+ SourceFolder: '$(Build.SourcesDirectory)'
+ Contents: |
+ **/*.log
+ **/*.binlog
+ artifacts/source-build/self/prebuilt-report/**
+ TargetFolder: '$(Build.StagingDirectory)/BuildLogs'
+ CleanTargetFolder: true
+ continueOnError: true
+ condition: succeededOrFailed()
+
+- task: 1ES.PublishPipelineArtifact@1
+ displayName: Publish BuildLogs
+ inputs:
+ targetPath: '$(Build.StagingDirectory)/BuildLogs'
+ artifactName: BuildLogs_SourceBuild_${{ parameters.platform.name }}_Attempt$(System.JobAttempt)
+ continueOnError: true
+ condition: succeededOrFailed()
+
+# Manually inject component detection so that we can ignore the source build upstream cache, which contains
+# a nupkg cache of input packages (a local feed).
+# This path must match the upstream cache path in property 'CurrentRepoSourceBuiltNupkgCacheDir'
+# in src\Microsoft.DotNet.Arcade.Sdk\tools\SourceBuild\SourceBuildArcade.targets
+- task: ComponentGovernanceComponentDetection@0
+ displayName: Component Detection (Exclude upstream cache)
+ inputs:
+ ignoreDirectories: '$(Build.SourcesDirectory)/artifacts/source-build/self/src/artifacts/obj/source-built-upstream-cache'
diff --git a/eng/common/templates-official/variables/pool-providers.yml b/eng/common/templates-official/variables/pool-providers.yml
new file mode 100644
index 00000000000..1f308b24efc
--- /dev/null
+++ b/eng/common/templates-official/variables/pool-providers.yml
@@ -0,0 +1,45 @@
+# Select a pool provider based off branch name. Anything with branch name containing 'release' must go into an -Svc pool,
+# otherwise it should go into the "normal" pools. This separates out the queueing and billing of released branches.
+
+# Motivation:
+# Once a given branch of a repository's output has been officially "shipped" once, it is then considered to be COGS
+# (Cost of goods sold) and should be moved to a servicing pool provider. This allows both separation of queueing
+# (allowing release builds and main PR builds to not intefere with each other) and billing (required for COGS.
+# Additionally, the pool provider name itself may be subject to change when the .NET Core Engineering Services
+# team needs to move resources around and create new and potentially differently-named pools. Using this template
+# file from an Arcade-ified repo helps guard against both having to update one's release/* branches and renaming.
+
+# How to use:
+# This yaml assumes your shipped product branches use the naming convention "release/..." (which many do).
+# If we find alternate naming conventions in broad usage it can be added to the condition below.
+#
+# First, import the template in an arcade-ified repo to pick up the variables, e.g.:
+#
+# variables:
+# - template: /eng/common/templates-official/variables/pool-providers.yml
+#
+# ... then anywhere specifying the pool provider use the runtime variables,
+# $(DncEngInternalBuildPool)
+#
+# pool:
+# name: $(DncEngInternalBuildPool)
+# image: 1es-windows-2022
+
+variables:
+ # Coalesce the target and source branches so we know when a PR targets a release branch
+ # If these variables are somehow missing, fall back to main (tends to have more capacity)
+
+ # Any new -Svc alternative pools should have variables added here to allow for splitting work
+
+ - name: DncEngInternalBuildPool
+ value: $[
+ replace(
+ replace(
+ eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'),
+ True,
+ 'NetCore1ESPool-Svc-Internal'
+ ),
+ False,
+ 'NetCore1ESPool-Internal'
+ )
+ ]
\ No newline at end of file
diff --git a/eng/common/templates-official/variables/sdl-variables.yml b/eng/common/templates-official/variables/sdl-variables.yml
new file mode 100644
index 00000000000..dbdd66d4a4b
--- /dev/null
+++ b/eng/common/templates-official/variables/sdl-variables.yml
@@ -0,0 +1,7 @@
+variables:
+# The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in
+# sync with the packages.config file.
+- name: DefaultGuardianVersion
+ value: 0.109.0
+- name: GuardianPackagesConfigFile
+ value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config
\ No newline at end of file
diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml
index e24ca2f46f9..8ec5c4f2d9f 100644
--- a/eng/common/templates/job/job.yml
+++ b/eng/common/templates/job/job.yml
@@ -15,6 +15,7 @@ parameters:
timeoutInMinutes: ''
variables: []
workspace: ''
+ templateContext: ''
# Job base template specific parameters
# See schema documentation - https://github.com/dotnet/arcade/blob/master/Documentation/AzureDevOps/TemplateSchema.md
@@ -68,6 +69,9 @@ jobs:
${{ if ne(parameters.timeoutInMinutes, '') }}:
timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
+ ${{ if ne(parameters.templateContext, '') }}:
+ templateContext: ${{ parameters.templateContext }}
+
variables:
- ${{ if ne(parameters.enableTelemetry, 'false') }}:
- name: DOTNET_CLI_TELEMETRY_PROFILE
diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml
index fa5446c093d..8ec0151def2 100644
--- a/eng/common/templates/job/publish-build-assets.yml
+++ b/eng/common/templates/job/publish-build-assets.yml
@@ -58,7 +58,7 @@ jobs:
demands: Cmd
# If it's not devdiv, it's dnceng
${{ if ne(variables['System.TeamProject'], 'DevDiv') }}:
- name: $(DncEngInternalBuildPool)
+ name: NetCore1ESPool-Publishing-Internal
demands: ImageOverride -equals windows.vs2019.amd64
steps:
@@ -71,7 +71,7 @@ jobs:
checkDownloadedFiles: true
condition: ${{ parameters.condition }}
continueOnError: ${{ parameters.continueOnError }}
-
+
- task: NuGetAuthenticate@1
- task: PowerShell@2
@@ -86,7 +86,7 @@ jobs:
/p:OfficialBuildId=$(Build.BuildNumber)
condition: ${{ parameters.condition }}
continueOnError: ${{ parameters.continueOnError }}
-
+
- task: powershell@2
displayName: Create ReleaseConfigs Artifact
inputs:
@@ -95,7 +95,7 @@ jobs:
Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value $(BARBuildId)
Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value "$(DefaultChannels)"
Add-Content -Path "$(Build.StagingDirectory)/ReleaseConfigs.txt" -Value $(IsStableBuild)
-
+
- task: PublishBuildArtifacts@1
displayName: Publish ReleaseConfigs Artifact
inputs:
@@ -121,7 +121,7 @@ jobs:
- task: PublishBuildArtifacts@1
displayName: Publish SymbolPublishingExclusionsFile Artifact
- condition: eq(variables['SymbolExclusionFile'], 'true')
+ condition: eq(variables['SymbolExclusionFile'], 'true')
inputs:
PathtoPublish: '$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt'
PublishLocation: Container
@@ -137,7 +137,7 @@ jobs:
displayName: Publish Using Darc
inputs:
filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1
- arguments: -BuildId $(BARBuildId)
+ arguments: -BuildId $(BARBuildId)
-PublishingInfraVersion 3
-AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)'
-MaestroToken '$(MaestroApiAccessToken)'
@@ -148,4 +148,4 @@ jobs:
- ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}:
- template: /eng/common/templates/steps/publish-logs.yml
parameters:
- JobLabel: 'Publish_Artifacts_Logs'
+ JobLabel: 'Publish_Artifacts_Logs'
diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml
index 3f74abf7ce0..aba44a25a33 100644
--- a/eng/common/templates/post-build/post-build.yml
+++ b/eng/common/templates/post-build/post-build.yml
@@ -39,7 +39,7 @@ parameters:
displayName: Enable NuGet validation
type: boolean
default: true
-
+
- name: publishInstallersAndChecksums
displayName: Publish installers and checksums
type: boolean
@@ -131,8 +131,8 @@ stages:
displayName: Validate
inputs:
filePath: $(Build.SourcesDirectory)/eng/common/post-build/nuget-validation.ps1
- arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/
- -ToolDestinationPath $(Agent.BuildDirectory)/Extract/
+ arguments: -PackagesPath $(Build.ArtifactStagingDirectory)/PackageArtifacts/
+ -ToolDestinationPath $(Agent.BuildDirectory)/Extract/
- job:
displayName: Signing Validation
@@ -221,9 +221,9 @@ stages:
displayName: Validate
inputs:
filePath: $(Build.SourcesDirectory)/eng/common/post-build/sourcelink-validation.ps1
- arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/
- -ExtractPath $(Agent.BuildDirectory)/Extract/
- -GHRepoName $(Build.Repository.Name)
+ arguments: -InputPath $(Build.ArtifactStagingDirectory)/BlobArtifacts/
+ -ExtractPath $(Agent.BuildDirectory)/Extract/
+ -GHRepoName $(Build.Repository.Name)
-GHCommit $(Build.SourceVersion)
-SourcelinkCliVersion $(SourceLinkCLIVersion)
continueOnError: true
@@ -258,7 +258,7 @@ stages:
demands: Cmd
# If it's not devdiv, it's dnceng
${{ else }}:
- name: $(DncEngInternalBuildPool)
+ name: NetCore1ESPool-Publishing-Internal
demands: ImageOverride -equals windows.vs2019.amd64
steps:
- template: setup-maestro-vars.yml
@@ -272,7 +272,7 @@ stages:
displayName: Publish Using Darc
inputs:
filePath: $(Build.SourcesDirectory)/eng/common/post-build/publish-using-darc.ps1
- arguments: -BuildId $(BARBuildId)
+ arguments: -BuildId $(BARBuildId)
-PublishingInfraVersion ${{ parameters.publishingInfraVersion }}
-AzdoToken '$(publishing-dnceng-devdiv-code-r-build-re)'
-MaestroToken '$(MaestroApiAccessToken)'
diff --git a/eng/common/templates/steps/component-governance.yml b/eng/common/templates/steps/component-governance.yml
index 0ecec47b0c9..cbba0596709 100644
--- a/eng/common/templates/steps/component-governance.yml
+++ b/eng/common/templates/steps/component-governance.yml
@@ -4,7 +4,7 @@ parameters:
steps:
- ${{ if eq(parameters.disableComponentGovernance, 'true') }}:
- - script: "echo ##vso[task.setvariable variable=skipComponentGovernanceDetection]true"
+ - script: echo "##vso[task.setvariable variable=skipComponentGovernanceDetection]true"
displayName: Set skipComponentGovernanceDetection variable
- ${{ if ne(parameters.disableComponentGovernance, 'true') }}:
- task: ComponentGovernanceComponentDetection@0
diff --git a/eng/common/templates/steps/generate-sbom.yml b/eng/common/templates/steps/generate-sbom.yml
index a06373f38fa..2b21eae4273 100644
--- a/eng/common/templates/steps/generate-sbom.yml
+++ b/eng/common/templates/steps/generate-sbom.yml
@@ -5,7 +5,7 @@
# IgnoreDirectories - Directories to ignore for SBOM generation. This will be passed through to the CG component detector.
parameters:
- PackageVersion: 7.0.0
+ PackageVersion: 8.0.0
BuildDropPath: '$(Build.SourcesDirectory)/artifacts'
PackageName: '.NET'
ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom
diff --git a/eng/common/templates/variables/pool-providers.yml b/eng/common/templates/variables/pool-providers.yml
index 9cc5c550d3b..d236f9fdbb1 100644
--- a/eng/common/templates/variables/pool-providers.yml
+++ b/eng/common/templates/variables/pool-providers.yml
@@ -1,15 +1,15 @@
-# Select a pool provider based off branch name. Anything with branch name containing 'release' must go into an -Svc pool,
+# Select a pool provider based off branch name. Anything with branch name containing 'release' must go into an -Svc pool,
# otherwise it should go into the "normal" pools. This separates out the queueing and billing of released branches.
-# Motivation:
+# Motivation:
# Once a given branch of a repository's output has been officially "shipped" once, it is then considered to be COGS
# (Cost of goods sold) and should be moved to a servicing pool provider. This allows both separation of queueing
# (allowing release builds and main PR builds to not intefere with each other) and billing (required for COGS.
-# Additionally, the pool provider name itself may be subject to change when the .NET Core Engineering Services
-# team needs to move resources around and create new and potentially differently-named pools. Using this template
+# Additionally, the pool provider name itself may be subject to change when the .NET Core Engineering Services
+# team needs to move resources around and create new and potentially differently-named pools. Using this template
# file from an Arcade-ified repo helps guard against both having to update one's release/* branches and renaming.
-# How to use:
+# How to use:
# This yaml assumes your shipped product branches use the naming convention "release/..." (which many do).
# If we find alternate naming conventions in broad usage it can be added to the condition below.
#
@@ -54,4 +54,4 @@ variables:
False,
'NetCore1ESPool-Internal'
)
- ]
\ No newline at end of file
+ ]
diff --git a/global.json b/global.json
index ba4499884a8..83a7611b408 100644
--- a/global.json
+++ b/global.json
@@ -13,7 +13,7 @@
}
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24059.4",
- "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.24059.4"
+ "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24204.3",
+ "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.24204.3"
}
}
diff --git a/src/EFCore.Abstractions/PACKAGE.md b/src/EFCore.Abstractions/PACKAGE.md
new file mode 100644
index 00000000000..0d43afc8caf
--- /dev/null
+++ b/src/EFCore.Abstractions/PACKAGE.md
@@ -0,0 +1,13 @@
+`Microsoft.EntityFrameworkCore.Abstractions` is a small package containing abstractions which may be useful for applications in places where a dependency on the full `Microsoft.EntityFrameworkCore` is not desirable.
+
+## Usage
+
+This package is included automatically as a dependency of the main [Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore) package. Usually, the abstractions package is only explicitly installed in places where it is undesirable to use the main package. For example, it can be installed to use mapping attributes on POCO entity types which are otherwise independent of EF Core.
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
diff --git a/src/EFCore.Analyzers/AnalyzerReleases.Shipped.md b/src/EFCore.Analyzers/AnalyzerReleases.Shipped.md
index 9b4322118c3..ed10f609a61 100644
--- a/src/EFCore.Analyzers/AnalyzerReleases.Shipped.md
+++ b/src/EFCore.Analyzers/AnalyzerReleases.Shipped.md
@@ -22,4 +22,4 @@ EF1000 | Security | Disabled | RawSqlStringInjectionDiagnosticAnalyzer, [Docume
### New Rules
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
-EF1002 | Security | Warning | InterpolatedStringUsageInRawQueriesDiagnosticAnalyzer, [Documentation](https://learn.microsoft.com/en-us/ef/core/querying/sql-queries#passing-parameters)
\ No newline at end of file
+EF1002 | Security | Warning | InterpolatedStringUsageInRawQueriesDiagnosticAnalyzer, [Documentation](https://learn.microsoft.com/ef/core/querying/sql-queries#passing-parameters)
\ No newline at end of file
diff --git a/src/EFCore.Analyzers/EFCore.Analyzers.nuspec b/src/EFCore.Analyzers/EFCore.Analyzers.nuspec
index 66cccde5ecc..3ad15319e11 100644
--- a/src/EFCore.Analyzers/EFCore.Analyzers.nuspec
+++ b/src/EFCore.Analyzers/EFCore.Analyzers.nuspec
@@ -5,10 +5,12 @@
+ docs\PACKAGE.md
$CommonFileElements$
+
diff --git a/src/EFCore.Analyzers/PACKAGE.md b/src/EFCore.Analyzers/PACKAGE.md
new file mode 100644
index 00000000000..5d9877db6c4
--- /dev/null
+++ b/src/EFCore.Analyzers/PACKAGE.md
@@ -0,0 +1,13 @@
+`Microsoft.EntityFrameworkCore.Analyzers` contains C# analyzers for Entity Framework Core.
+
+## Usage
+
+This package is included automatically as a dependency of the main [Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore) package. It is unusual to install it explicitly.
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
diff --git a/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs b/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs
index e26e454ba6d..85a1cd37136 100644
--- a/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs
+++ b/src/EFCore.Cosmos/Infrastructure/CosmosDbContextOptionsBuilder.cs
@@ -93,7 +93,7 @@ public virtual CosmosDbContextOptionsBuilder LimitToEndpoint(bool enable = true)
///
///
/// Use
- ///
+ ///
/// static lambda expressions
///
/// to avoid creating multiple instances.
diff --git a/src/EFCore.Cosmos/PACKAGE.md b/src/EFCore.Cosmos/PACKAGE.md
new file mode 100644
index 00000000000..297535b7fec
--- /dev/null
+++ b/src/EFCore.Cosmos/PACKAGE.md
@@ -0,0 +1,25 @@
+`Microsoft.EntityFrameworkCore.Cosmos` is the EF Core database provider package for Azure Cosmos DB.
+
+## Usage
+
+Call the `UseCosmos` method to choose the Azure Cosmos DB database provider for your `DbContext`. For example:
+
+```csharp
+protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ => optionsBuilder.UseCosmos(
+ "https://localhost:8081",
+ "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
+ databaseName: "OrdersDB");
+```
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Additional documentation
+
+See [Microsoft SQL Server EF Core Database Provider](https://learn.microsoft.com/ef/core/providers/cosmos/) for more information about the features of this database provider.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
diff --git a/src/EFCore.Cosmos/ValueGeneration/Internal/IdValueGenerator.cs b/src/EFCore.Cosmos/ValueGeneration/Internal/IdValueGenerator.cs
index 26a4dc2e855..70bb2c43bc1 100644
--- a/src/EFCore.Cosmos/ValueGeneration/Internal/IdValueGenerator.cs
+++ b/src/EFCore.Cosmos/ValueGeneration/Internal/IdValueGenerator.cs
@@ -117,7 +117,7 @@ private static StringBuilder AppendEscape(StringBuilder builder, string stringVa
return builder.Append(stringValue)
// We need this to avoid collisions with the value separator
.Replace("|", "^|", startingIndex, builder.Length - startingIndex)
- // These are invalid characters, see https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.documents.resource.id
+ // These are invalid characters, see https://docs.microsoft.com/dotnet/api/microsoft.azure.documents.resource.id
.Replace("/", "^2F", startingIndex, builder.Length - startingIndex)
.Replace("\\", "^5C", startingIndex, builder.Length - startingIndex)
.Replace("?", "^3F", startingIndex, builder.Length - startingIndex)
diff --git a/src/EFCore.Design/PACKAGE.md b/src/EFCore.Design/PACKAGE.md
new file mode 100644
index 00000000000..0f8531c9bb6
--- /dev/null
+++ b/src/EFCore.Design/PACKAGE.md
@@ -0,0 +1,24 @@
+The Entity Framework Core tools help with design-time development tasks. They're primarily used to manage Migrations and to scaffold a `DbContext` and entity types by reverse engineering the schema of a database.
+
+The `Microsoft.EntityFrameworkCore.Design` package is required for either command-line or Package Manager Console-based tooling, and is a dependency of [dotnet-ef](https://www.nuget.org/packages/dotnet-ef) and [Microsoft.EntityFrameworkCore.Tools](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Tools).
+
+## Usage
+
+Install the package into your project and then use either [dotnet-ef](https://www.nuget.org/packages/dotnet-ef) or [Microsoft.EntityFrameworkCore.Tools](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Tools).
+
+By default, the package will install with `PrivateAssets="All" `so that the tooling assembly will not be included with your published app. For example:
+
+```xml
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+```
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
diff --git a/src/EFCore.Design/README.md b/src/EFCore.Design/README.md
deleted file mode 100644
index ba2b49b132b..00000000000
--- a/src/EFCore.Design/README.md
+++ /dev/null
@@ -1,45 +0,0 @@
-The Entity Framework Core tools help with design-time development tasks. They're primarily used to manage Migrations and to scaffold a DbContext and entity types by reverse engineering the schema of a database.
-Microsoft.EntityFrameworkCore.Design is for cross-platform command line tooling.
-
-## Getting started
-
-`Microsoft.EntityFrameworkCore.Design` contains all the design-time logic for Entity Framework Core. It's the code that all of the various tools (PMC cmdlets like `Add-Migration`, `dotnet ef` & `ef.exe`) call into.
-
-If you don't use Migrations or Reverse Engineering, you don't need it.
-
-And when you do need it, we encourage `PrivateAssets="All" `so it doesn't get published to the server where you almost certainly won't need it.
-
-### Prerequisites
-
-Before using the tools:
-
-- [Understand the difference between target and startup project](https://learn.microsoft.com/en-us/ef/core/cli/powershell#target-and-startup-project).
-- [Learn how to use the tools with .NET Standard class libraries](https://learn.microsoft.com/en-us/ef/core/cli/powershell#other-target-frameworks).
-- [For ASP.NET Core projects, set the environment](https://learn.microsoft.com/en-us/ef/core/cli/powershell#aspnet-core-environment).
-
-## Usage
-
-PMC Command | Usage
--- | --
-Get-Help entityframework |Displays information about entity framework commands.
-[Add-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#add-migration) | Creates a migration by adding a migration snapshot.
-[Bundle-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#bundle-migration) | Creates an executable to update the database.
-[Get-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#get-dbcontext) | Gets information about a DbContext type.
-[Drop-Database](https://learn.microsoft.com/en-us/ef/core/cli/powershell#drop-database) | Drops the database.
-[Get-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#get-migration) | Lists available migrations.
-[Optimize-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#optimize-dbcontext) | Generates a compiled version of the model used by the `DbContext`.
-[Remove-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#remove-migration) | Removes the last migration snapshot.
-[Scaffold-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#scaffold-dbcontext) | Generates a DbContext and entity type classes for a specified database. This is called reverse engineering.
-[Script-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#script-dbcontext) | Generates a SQL script from the DbContext. Bypasses any migrations.
-[Script-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#script-migration) | Generates a SQL script using all the migration snapshots.
-[Update-Database](https://learn.microsoft.com/en-us/ef/core/cli/powershell#update-database) | Updates the database schema based on the last migration snapshot.
-
-## Additional documentation
-
-- [Migrations](https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/)
-- [Reverse Engineering](https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/?tabs=dotnet-core-cli)
-- [Compiled models](https://learn.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-di%2Cwith-constant#compiled-models)
-
-## Feedback
-
-If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
\ No newline at end of file
diff --git a/src/EFCore.InMemory/PACKAGE.md b/src/EFCore.InMemory/PACKAGE.md
new file mode 100644
index 00000000000..33e824a3add
--- /dev/null
+++ b/src/EFCore.InMemory/PACKAGE.md
@@ -0,0 +1,26 @@
+`Microsoft.EntityFrameworkCore.InMemory` is the EF Core database provider package for the built-in in-memory database.
+
+This database provider allows Entity Framework Core to be used with an in-memory database. While it has become common to use the in-memory database for testing, this is discouraged. For more information on how to test EF Core applications, see [Testing EF Core Applications](https://learn.microsoft.com/ef/core/testing/).
+
+## Usage
+
+Call the `UseInMemoryDatabase` method to choose the SQL Server/Azure SQL database provider for your `DbContext`. For example:
+
+```csharp
+protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+{
+ optionsBuilder.UseInMemoryDatabase("MyDatabase");
+}
+```
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Additional documentation
+
+See [Microsoft SQL Server EF Core Database Provider](https://learn.microsoft.com/en-us/ef/core/providers/in-memory/) for more information about the features of this database provider.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
\ No newline at end of file
diff --git a/src/EFCore.Proxies/PACKAGE.md b/src/EFCore.Proxies/PACKAGE.md
new file mode 100644
index 00000000000..2c76ea8955f
--- /dev/null
+++ b/src/EFCore.Proxies/PACKAGE.md
@@ -0,0 +1,34 @@
+The `Microsoft.EntityFrameworkCore.Proxies` package contains implementations of dynamic proxies for [lazy-loading](https://learn.microsoft.com/ef/core/querying/related-data/lazy#lazy-loading-with-proxies) and/or [change-tracking](https://learn.microsoft.com/ef/core/change-tracking/change-detection#change-tracking-proxies).
+
+## Usage
+
+Call `UseLazyLoadingProxies` when configuring your `DbContext` to enable lazy-loading proxies. For example:
+
+```csharp
+protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ => optionsBuilder.UseLazyLoadingProxies();
+```
+
+Call `UseChangeTrackingProxies` when configuring your `DbContext` to enable change-tracking proxies. For example:
+
+```csharp
+protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ => optionsBuilder.UseChangeTrackingProxies();
+```
+
+Call both methods for proxies that implement both lazy-loading and change-tracking. For example:
+
+```csharp
+protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ => optionsBuilder
+ .UseLazyLoadingProxies()
+ .UseChangeTrackingProxies();
+```
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
\ No newline at end of file
diff --git a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
index 97ba303a91a..929fb4f470e 100644
--- a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
@@ -17,6 +17,12 @@ namespace Microsoft.EntityFrameworkCore;
///
public static class RelationalPropertyExtensions
{
+ private static readonly bool UseOldBehavior32763 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32763", out var enabled32763) && enabled32763;
+
+ private static readonly bool UseOldBehavior33004 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue33004", out var enabled33004) && enabled33004;
+
private static readonly MethodInfo GetFieldValueMethod =
typeof(DbDataReader).GetRuntimeMethod(nameof(DbDataReader.GetFieldValue), new[] { typeof(int) })!;
@@ -183,7 +189,10 @@ public static string GetDefaultColumnName(this IReadOnlyProperty property)
var foreignKey = property.GetContainingForeignKeys().First();
var principalEntityType = foreignKey.PrincipalEntityType;
if (principalEntityType is { HasSharedClrType: false, ClrType.IsConstructedGenericType: true }
- && foreignKey.DependentToPrincipal == null)
+ && foreignKey.DependentToPrincipal == null
+ && (UseOldBehavior32763
+ || principalEntityType.GetTableName() != foreignKey.DeclaringEntityType.GetTableName()
+ || principalEntityType.GetSchema() != foreignKey.DeclaringEntityType.GetSchema()))
{
var principalProperty = property.FindFirstPrincipal()!;
var principalName = principalEntityType.ShortName();
@@ -1185,8 +1194,12 @@ public static bool IsColumnNullable(this IReadOnlyProperty property, in StoreObj
return sharedTableRootProperty.IsColumnNullable(storeObject);
}
+ var declaringEntityType = UseOldBehavior33004
+ ? property.DeclaringType
+ : property.DeclaringType.ContainingEntityType;
+
return property.IsNullable
- || (property.DeclaringType is IReadOnlyEntityType entityType
+ || (declaringEntityType is IReadOnlyEntityType entityType
&& ((entityType.BaseType != null
&& entityType.GetMappingStrategy() == RelationalAnnotationNames.TphMappingStrategy)
|| IsOptionalSharingDependent(entityType, storeObject, 0)));
diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
index 816cf4b74ba..8b07aa9ae54 100644
--- a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
+++ b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
@@ -1,6 +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.Text;
using System.Text.Json;
namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
@@ -13,6 +14,9 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
///
public class RelationalModel : Annotatable, IRelationalModel
{
+ internal static readonly bool UseOldBehavior32699 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32699", out var enabled32699) && enabled32699;
+
private bool _isReadOnly;
///
@@ -340,31 +344,38 @@ private static void AddDefaultMappings(
}
else
{
- foreach (var property in entityType.GetProperties())
+ if (UseOldBehavior32699)
{
- var columnName = property.IsPrimaryKey() || isTpc || isTph || property.DeclaringType == mappedType
- ? property.GetColumnName()
- : null;
- if (columnName == null)
+ foreach (var property in entityType.GetProperties())
{
- continue;
- }
+ var columnName = property.IsPrimaryKey() || isTpc || isTph || property.DeclaringType == mappedType
+ ? property.GetColumnName()
+ : null;
+ if (columnName == null)
+ {
+ continue;
+ }
- var column = (ColumnBase?)defaultTable.FindColumn(columnName);
- if (column == null)
- {
- column = new ColumnBase(columnName, property.GetColumnType(), defaultTable)
+ var column = (ColumnBase?)defaultTable.FindColumn(columnName);
+ if (column == null)
{
- IsNullable = property.IsColumnNullable()
- };
- defaultTable.Columns.Add(columnName, column);
- }
- else if (!property.IsColumnNullable())
- {
- column.IsNullable = false;
- }
+ column = new ColumnBase(columnName, property.GetColumnType(), defaultTable)
+ {
+ IsNullable = property.IsColumnNullable()
+ };
+ defaultTable.Columns.Add(columnName, column);
+ }
+ else if (!property.IsColumnNullable())
+ {
+ column.IsNullable = false;
+ }
- CreateColumnMapping(column, property, tableMapping);
+ CreateColumnMapping(column, property, tableMapping);
+ }
+ }
+ else
+ {
+ CreateDefaultColumnMapping(entityType, mappedType, defaultTable, tableMapping, isTph, isTpc);
}
}
@@ -386,6 +397,83 @@ private static void AddDefaultMappings(
tableMappings.Reverse();
}
+ private static void CreateDefaultColumnMapping(
+ ITypeBase typeBase,
+ ITypeBase mappedType,
+ TableBase defaultTable,
+ TableMappingBase tableMapping,
+ bool isTph,
+ bool isTpc)
+ {
+ foreach (var property in typeBase.GetProperties())
+ {
+ var columnName = property.IsPrimaryKey() || isTpc || isTph || property.DeclaringType == mappedType
+ ? GetColumnName(property)
+ : null;
+
+ if (columnName == null)
+ {
+ continue;
+ }
+
+ var column = (ColumnBase?)defaultTable.FindColumn(columnName);
+ if (column == null)
+ {
+ column = new ColumnBase(columnName, property.GetColumnType(), defaultTable)
+ {
+ IsNullable = property.IsColumnNullable()
+ };
+ defaultTable.Columns.Add(columnName, column);
+ }
+ else if (!property.IsColumnNullable())
+ {
+ column.IsNullable = false;
+ }
+
+ CreateColumnMapping(column, property, tableMapping);
+ }
+
+ foreach (var complexProperty in typeBase.GetDeclaredComplexProperties())
+ {
+ var complexType = complexProperty.ComplexType;
+ tableMapping = new TableMappingBase(complexType, defaultTable, includesDerivedTypes: false);
+
+ CreateDefaultColumnMapping(complexType, complexType, defaultTable, tableMapping, isTph, isTpc);
+
+ var tableMappings = (List>?)complexType
+ .FindRuntimeAnnotationValue(RelationalAnnotationNames.DefaultMappings);
+ if (tableMappings == null)
+ {
+ tableMappings = new List>();
+ complexType.AddRuntimeAnnotation(RelationalAnnotationNames.DefaultMappings, tableMappings);
+ }
+ tableMappings.Add(tableMapping);
+
+ defaultTable.ComplexTypeMappings.Add(tableMapping);
+ }
+
+ static string GetColumnName(IProperty property)
+ {
+ var complexType = property.DeclaringType as IComplexType;
+ if (complexType != null)
+ {
+ var builder = new StringBuilder();
+ builder.Append(property.Name);
+ while (complexType != null)
+ {
+ builder.Insert(0, "_");
+ builder.Insert(0, complexType.ComplexProperty.Name);
+
+ complexType = complexType.ComplexProperty.DeclaringType as IComplexType;
+ }
+
+ return builder.ToString();
+ }
+
+ return property.GetColumnName();
+ }
+ }
+
private static void AddTables(
RelationalModel databaseModel,
IEntityType entityType,
diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
index a14998c9fd4..04cc44f1621 100644
--- a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
+++ b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
@@ -16,6 +16,9 @@ namespace Microsoft.EntityFrameworkCore.Migrations.Internal;
///
public class MigrationsModelDiffer : IMigrationsModelDiffer
{
+ private static readonly bool UseOldBehavior32972 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32972", out var enabled32972) && enabled32972;
+
private static readonly Type[] DropOperationTypes =
{
typeof(DropIndexOperation),
@@ -1190,7 +1193,14 @@ private void Initialize(
if (!column.TryGetDefaultValue(out var defaultValue))
{
- defaultValue = null;
+ // for non-nullable collections of primitives that are mapped to JSON we set a default value corresponding to empty JSON collection
+ defaultValue = !UseOldBehavior32972
+ && !inline
+ && column is { IsNullable: false, StoreTypeMapping: { ElementTypeMapping: not null, Converter: ValueConverter columnValueConverter } }
+ && columnValueConverter.GetType() is Type { IsGenericType: true } columnValueConverterType
+ && columnValueConverterType.GetGenericTypeDefinition() == typeof(CollectionToJsonStringConverter<>)
+ ? "[]"
+ : null;
}
columnOperation.ColumnType = column.StoreType;
diff --git a/src/EFCore.Relational/PACKAGE.md b/src/EFCore.Relational/PACKAGE.md
new file mode 100644
index 00000000000..938c855927e
--- /dev/null
+++ b/src/EFCore.Relational/PACKAGE.md
@@ -0,0 +1,13 @@
+`Microsoft.EntityFrameworkCore.Relational` package contains common code for relational database providers that are based on ADO.NET.
+
+## Usage
+
+This package is included automatically as a dependency of each relational provider package. Adding an explicit reference can be useful to ensure the latest version is used even if the provider is depending on an older version.
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
\ No newline at end of file
diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs
index f4f48b500b8..7ac113066b1 100644
--- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs
+++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs
@@ -30,6 +30,8 @@ public sealed partial class SelectExpression : TableExpressionBase
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue31107", out var enabled31107) && enabled31107;
private static readonly bool UseOldBehavior32234 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32234", out var enabled32234) && enabled32234;
+ private static readonly bool UseOldBehavior32911 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32911", out var enabled32911) && enabled32911;
private static readonly IdentifierComparer IdentifierComparerInstance = new();
@@ -2706,116 +2708,243 @@ void HandleStructuralTypeProjection(
projection1.StructuralType.DisplayName(), projection2.StructuralType.DisplayName()));
}
- var propertyExpressions = new Dictionary();
-
- ProcessStructuralType(projection1, projection2);
-
- void ProcessStructuralType(
- StructuralTypeProjectionExpression nestedProjection1,
- StructuralTypeProjectionExpression nestedProjection2)
+ if (UseOldBehavior32911)
{
- var type = nestedProjection1.StructuralType;
+ var propertyExpressions = new Dictionary();
+
+ ProcessStructuralType(projection1, projection2);
- foreach (var property in GetAllPropertiesInHierarchy(type))
+ void ProcessStructuralType(
+ StructuralTypeProjectionExpression nestedProjection1,
+ StructuralTypeProjectionExpression nestedProjection2)
{
- var column1 = nestedProjection1.BindProperty(property);
- var column2 = nestedProjection2.BindProperty(property);
- var alias = GenerateUniqueColumnAlias(column1.Name);
- var innerProjection = new ProjectionExpression(column1, alias);
- select1._projection.Add(innerProjection);
- select2._projection.Add(new ProjectionExpression(column2, alias));
- var outerColumn = new ConcreteColumnExpression(innerProjection, tableReferenceExpression);
- if (column1.IsNullable
- || column2.IsNullable)
+ var type = nestedProjection1.StructuralType;
+
+ foreach (var property in GetAllPropertiesInHierarchy(type))
{
- outerColumn = outerColumn.MakeNullable();
- }
+ var column1 = nestedProjection1.BindProperty(property);
+ var column2 = nestedProjection2.BindProperty(property);
+ var alias = GenerateUniqueColumnAlias(column1.Name);
+ var innerProjection = new ProjectionExpression(column1, alias);
+ select1._projection.Add(innerProjection);
+ select2._projection.Add(new ProjectionExpression(column2, alias));
+ var outerColumn = new ConcreteColumnExpression(innerProjection, tableReferenceExpression);
+ if (column1.IsNullable
+ || column2.IsNullable)
+ {
+ outerColumn = outerColumn.MakeNullable();
+ }
- propertyExpressions[property] = outerColumn;
+ propertyExpressions[property] = outerColumn;
- // Lift up any identifier columns to the set operation result (the outer).
- // This is typically the entity primary key columns, but can also be all of a complex type's properties if Distinct
- // was previously called.
- if (outerIdentifiers.Length > 0)
- {
- var index = select1._identifier.FindIndex(e => e.Column.Equals(column1));
- if (index != -1)
+ // Lift up any identifier columns to the set operation result (the outer).
+ // This is typically the entity primary key columns, but can also be all of a complex type's properties if Distinct
+ // was previously called.
+ if (outerIdentifiers.Length > 0)
{
- if (select2._identifier[index].Column.Equals(column2))
+ var index = select1._identifier.FindIndex(e => e.Column.Equals(column1));
+ if (index != -1)
{
- outerIdentifiers[index] = outerColumn;
+ if (select2._identifier[index].Column.Equals(column2))
+ {
+ outerIdentifiers[index] = outerColumn;
+ }
+ else
+ {
+ // If select1 matched but select2 did not then we erase all identifiers
+ // TODO: We could make this little more robust by allow the indexes to be different. See issue#24475
+ // i.e. Identifier ordering being different.
+ outerIdentifiers = Array.Empty();
+ }
}
- else
+ // If the top-level projection - not the current nested one - is a complex type and not an entity type, then add
+ // all its columns to the "otherExpressions" list (i.e. columns not part of a an entity primary key). This is
+ // the same as with a non-structural type projection.
+ else if (projection1.StructuralType is IComplexType)
{
- // If select1 matched but select2 did not then we erase all identifiers
- // TODO: We could make this little more robust by allow the indexes to be different. See issue#24475
- // i.e. Identifier ordering being different.
- outerIdentifiers = Array.Empty();
+ var outerTypeMapping = column1.TypeMapping ?? column1.TypeMapping;
+ if (outerTypeMapping == null)
+ {
+ throw new InvalidOperationException(
+ RelationalStrings.SetOperationsRequireAtLeastOneSideWithValidTypeMapping(setOperationType));
+ }
+
+ otherExpressions.Add((outerColumn, outerTypeMapping.KeyComparer));
}
}
- // If the top-level projection - not the current nested one - is a complex type and not an entity type, then add
- // all its columns to the "otherExpressions" list (i.e. columns not part of a an entity primary key). This is
- // the same as with a non-structural type projection.
- else if (projection1.StructuralType is IComplexType)
- {
- var outerTypeMapping = column1.TypeMapping ?? column1.TypeMapping;
- if (outerTypeMapping == null)
- {
- throw new InvalidOperationException(
- RelationalStrings.SetOperationsRequireAtLeastOneSideWithValidTypeMapping(setOperationType));
- }
+ }
- otherExpressions.Add((outerColumn, outerTypeMapping.KeyComparer));
- }
+ foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(nestedProjection1.StructuralType))
+ {
+ ProcessStructuralType(
+ (StructuralTypeProjectionExpression)nestedProjection1.BindComplexProperty(complexProperty).ValueBufferExpression,
+ (StructuralTypeProjectionExpression)nestedProjection2.BindComplexProperty(complexProperty).ValueBufferExpression);
}
}
- foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(nestedProjection1.StructuralType))
+ Check.DebugAssert(
+ projection1.TableMap.Count == projection2.TableMap.Count,
+ "Set operation over entity projections with different table map counts");
+ Check.DebugAssert(
+ projection1.TableMap.Keys.All(t => projection2.TableMap.ContainsKey(t)),
+ "Set operation over entity projections with table map discrepancy");
+
+ var tableMap = projection1.TableMap.ToDictionary(kvp => kvp.Key, kvp => tableReferenceExpression);
+
+ var discriminatorExpression = projection1.DiscriminatorExpression;
+ if (projection1.DiscriminatorExpression != null
+ && projection2.DiscriminatorExpression != null)
{
- ProcessStructuralType(
- (StructuralTypeProjectionExpression)nestedProjection1.BindComplexProperty(complexProperty).ValueBufferExpression,
- (StructuralTypeProjectionExpression)nestedProjection2.BindComplexProperty(complexProperty).ValueBufferExpression);
+ var alias = GenerateUniqueColumnAlias(DiscriminatorColumnAlias);
+ var innerProjection = new ProjectionExpression(projection1.DiscriminatorExpression, alias);
+ select1._projection.Add(innerProjection);
+ select2._projection.Add(new ProjectionExpression(projection2.DiscriminatorExpression, alias));
+ discriminatorExpression = new ConcreteColumnExpression(innerProjection, tableReferenceExpression);
}
- }
-
- Check.DebugAssert(
- projection1.TableMap.Count == projection2.TableMap.Count,
- "Set operation over entity projections with different table map counts");
- Check.DebugAssert(
- projection1.TableMap.Keys.All(t => projection2.TableMap.ContainsKey(t)),
- "Set operation over entity projections with table map discrepancy");
- var tableMap = projection1.TableMap.ToDictionary(kvp => kvp.Key, kvp => tableReferenceExpression);
+ var outerProjection = new StructuralTypeProjectionExpression(
+ projection1.StructuralType, propertyExpressions, tableMap, nullable: false, discriminatorExpression);
- var discriminatorExpression = projection1.DiscriminatorExpression;
- if (projection1.DiscriminatorExpression != null
- && projection2.DiscriminatorExpression != null)
- {
- var alias = GenerateUniqueColumnAlias(DiscriminatorColumnAlias);
- var innerProjection = new ProjectionExpression(projection1.DiscriminatorExpression, alias);
- select1._projection.Add(innerProjection);
- select2._projection.Add(new ProjectionExpression(projection2.DiscriminatorExpression, alias));
- discriminatorExpression = new ConcreteColumnExpression(innerProjection, tableReferenceExpression);
- }
+ if (outerIdentifiers.Length > 0 && outerProjection is { StructuralType: IEntityType entityType })
+ {
+ var primaryKey = entityType.FindPrimaryKey();
- var outerProjection = new StructuralTypeProjectionExpression(
- projection1.StructuralType, propertyExpressions, tableMap, nullable: false, discriminatorExpression);
+ // We know that there are existing identifiers (see condition above); we know we must have a key since a keyless
+ // entity type would have wiped the identifiers when generating the join.
+ Check.DebugAssert(primaryKey != null, "primary key is null.");
+ foreach (var property in primaryKey.Properties)
+ {
+ entityProjectionIdentifiers.Add(outerProjection.BindProperty(property));
+ entityProjectionValueComparers.Add(property.GetKeyValueComparer());
+ }
+ }
- if (outerIdentifiers.Length > 0 && outerProjection is { StructuralType: IEntityType entityType })
+ _projectionMapping[projectionMember] = outerProjection;
+ }
+ else
{
- var primaryKey = entityType.FindPrimaryKey();
+ var resultProjection = ProcessStructuralType(projection1, projection2);
+ _projectionMapping[projectionMember] = resultProjection;
- // We know that there are existing identifiers (see condition above); we know we must have a key since a keyless
- // entity type would have wiped the identifiers when generating the join.
- Check.DebugAssert(primaryKey != null, "primary key is null.");
- foreach (var property in primaryKey.Properties)
+ StructuralTypeProjectionExpression ProcessStructuralType(
+ StructuralTypeProjectionExpression structuralProjection1,
+ StructuralTypeProjectionExpression structuralProjection2)
{
- entityProjectionIdentifiers.Add(outerProjection.BindProperty(property));
- entityProjectionValueComparers.Add(property.GetKeyValueComparer());
+ var propertyExpressions = new Dictionary();
+ var complexPropertyCache = new Dictionary();
+ var type = structuralProjection1.StructuralType;
+
+ foreach (var property in GetAllPropertiesInHierarchy(type))
+ {
+ var column1 = structuralProjection1.BindProperty(property);
+ var column2 = structuralProjection2.BindProperty(property);
+ var alias = GenerateUniqueColumnAlias(column1.Name);
+ var innerProjection = new ProjectionExpression(column1, alias);
+ select1._projection.Add(innerProjection);
+ select2._projection.Add(new ProjectionExpression(column2, alias));
+ var outerColumn = new ConcreteColumnExpression(innerProjection, tableReferenceExpression);
+ if (column1.IsNullable
+ || column2.IsNullable)
+ {
+ outerColumn = outerColumn.MakeNullable();
+ }
+
+ propertyExpressions[property] = outerColumn;
+
+ // Lift up any identifier columns to the set operation result (the outer).
+ // This is typically the entity primary key columns, but can also be all of a complex type's properties if Distinct
+ // was previously called.
+ if (outerIdentifiers.Length > 0)
+ {
+ var index = select1._identifier.FindIndex(e => e.Column.Equals(column1));
+ if (index != -1)
+ {
+ if (select2._identifier[index].Column.Equals(column2))
+ {
+ outerIdentifiers[index] = outerColumn;
+ }
+ else
+ {
+ // If select1 matched but select2 did not then we erase all identifiers
+ // TODO: We could make this little more robust by allow the indexes to be different. See issue#24475
+ // i.e. Identifier ordering being different.
+ outerIdentifiers = Array.Empty();
+ }
+ }
+ // If the top-level projection - not the current nested one - is a complex type and not an entity type, then add
+ // all its columns to the "otherExpressions" list (i.e. columns not part of a an entity primary key). This is
+ // the same as with a non-structural type projection.
+ else if (projection1.StructuralType is IComplexType)
+ {
+ var outerTypeMapping = column1.TypeMapping ?? column1.TypeMapping;
+ if (outerTypeMapping == null)
+ {
+ throw new InvalidOperationException(
+ RelationalStrings.SetOperationsRequireAtLeastOneSideWithValidTypeMapping(setOperationType));
+ }
+
+ otherExpressions.Add((outerColumn, outerTypeMapping.KeyComparer));
+ }
+ }
+ }
+
+ foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(type))
+ {
+ var complexPropertyShaper1 = structuralProjection1.BindComplexProperty(complexProperty);
+ var complexPropertyShaper2 = structuralProjection2.BindComplexProperty(complexProperty);
+
+ var resultComplexProjection = ProcessStructuralType(
+ (StructuralTypeProjectionExpression)complexPropertyShaper1.ValueBufferExpression,
+ (StructuralTypeProjectionExpression)complexPropertyShaper2.ValueBufferExpression);
+
+ var resultComplexShaper = new RelationalStructuralTypeShaperExpression(
+ complexProperty.ComplexType,
+ resultComplexProjection,
+ resultComplexProjection.IsNullable);
+
+ complexPropertyCache[complexProperty] = resultComplexShaper;
+ }
+
+ Check.DebugAssert(
+ structuralProjection1.TableMap.Count == structuralProjection2.TableMap.Count,
+ "Set operation over entity projections with different table map counts");
+ Check.DebugAssert(
+ structuralProjection1.TableMap.Keys.All(t => structuralProjection2.TableMap.ContainsKey(t)),
+ "Set operation over entity projections with table map discrepancy");
+
+ var tableMap = structuralProjection1.TableMap.ToDictionary(kvp => kvp.Key, _ => tableReferenceExpression);
+
+ var discriminatorExpression = structuralProjection1.DiscriminatorExpression;
+ if (structuralProjection1.DiscriminatorExpression != null
+ && structuralProjection2.DiscriminatorExpression != null)
+ {
+ var alias = GenerateUniqueColumnAlias(DiscriminatorColumnAlias);
+ var innerProjection = new ProjectionExpression(structuralProjection1.DiscriminatorExpression, alias);
+ select1._projection.Add(innerProjection);
+ select2._projection.Add(new ProjectionExpression(structuralProjection2.DiscriminatorExpression, alias));
+ discriminatorExpression = new ConcreteColumnExpression(innerProjection, tableReferenceExpression);
+ }
+
+ var outerProjection = new StructuralTypeProjectionExpression(
+ type, propertyExpressions, complexPropertyCache, tableMap, nullable: false, discriminatorExpression);
+
+ if (outerIdentifiers.Length > 0 && outerProjection is { StructuralType: IEntityType entityType })
+ {
+ var primaryKey = entityType.FindPrimaryKey();
+
+ // We know that there are existing identifiers (see condition above); we know we must have a key since a keyless
+ // entity type would have wiped the identifiers when generating the join.
+ Check.DebugAssert(primaryKey != null, "primary key is null.");
+ foreach (var property in primaryKey.Properties)
+ {
+ entityProjectionIdentifiers.Add(outerProjection.BindProperty(property));
+ entityProjectionValueComparers.Add(property.GetKeyValueComparer());
+ }
+ }
+
+ return outerProjection;
}
}
-
- _projectionMapping[projectionMember] = outerProjection;
}
string GenerateUniqueColumnAlias(string baseAlias)
@@ -3201,9 +3330,18 @@ public static StructuralTypeShaperExpression GenerateComplexPropertyShaperExpres
var propertyExpressionMap = new Dictionary();
// We do not support complex type splitting, so we will only ever have a single table/view mapping to it.
+ // See Issue #32853 and Issue #31248
var complexTypeTable = complexProperty.ComplexType.GetViewOrTableMappings().Single().Table;
- var tableReferenceExpression = containerProjection.TableMap[complexTypeTable];
-
+ TableReferenceExpression? tableReferenceExpression;
+ if (RelationalModel.UseOldBehavior32699)
+ {
+ tableReferenceExpression = containerProjection.TableMap[complexTypeTable];
+ }
+ else if (!containerProjection.TableMap.TryGetValue(complexTypeTable, out tableReferenceExpression))
+ {
+ complexTypeTable = complexProperty.ComplexType.GetDefaultMappings().Single().Table;
+ tableReferenceExpression = containerProjection.TableMap[complexTypeTable];
+ }
var isComplexTypeNullable = containerProjection.IsNullable || complexProperty.IsNullable;
// If the complex property is declared on a type that's derived relative to the type being projected, the projected column is
@@ -4175,32 +4313,67 @@ StructuralTypeProjectionExpression LiftEntityProjectionFromSubquery(
TableReferenceExpression subqueryTableReference)
{
var propertyExpressions = new Dictionary();
+ var complexPropertyCache = new Dictionary();
+
+ if (UseOldBehavior32911)
+ {
+ HandleTypeProjection(projection);
+
+ void HandleTypeProjection(StructuralTypeProjectionExpression typeProjection)
+ {
+ foreach (var property in GetAllPropertiesInHierarchy(typeProjection.StructuralType))
+ {
+ // json entity projection (i.e. JSON entity that was transformed into query root) may have synthesized keys
+ // but they don't correspond to any columns - we need to skip those
+ if (typeProjection is { StructuralType: IEntityType entityType }
+ && entityType.IsMappedToJson()
+ && property.IsOrdinalKeyProperty())
+ {
+ continue;
+ }
- HandleTypeProjection(projection);
+ var innerColumn = typeProjection.BindProperty(property);
+ var outerColumn = subquery.GenerateOuterColumn(subqueryTableReferenceExpression, innerColumn);
+ projectionMap[innerColumn] = outerColumn;
+ propertyExpressions[property] = outerColumn;
+ }
- void HandleTypeProjection(StructuralTypeProjectionExpression typeProjection)
+ foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(typeProjection.StructuralType))
+ {
+ HandleTypeProjection(
+ (StructuralTypeProjectionExpression)typeProjection.BindComplexProperty(complexProperty).ValueBufferExpression);
+ }
+ }
+ }
+ else
{
- foreach (var property in GetAllPropertiesInHierarchy(typeProjection.StructuralType))
+ foreach (var property in GetAllPropertiesInHierarchy(projection.StructuralType))
{
// json entity projection (i.e. JSON entity that was transformed into query root) may have synthesized keys
// but they don't correspond to any columns - we need to skip those
- if (typeProjection is { StructuralType: IEntityType entityType }
+ if (projection is { StructuralType: IEntityType entityType }
&& entityType.IsMappedToJson()
&& property.IsOrdinalKeyProperty())
{
continue;
}
- var innerColumn = typeProjection.BindProperty(property);
- var outerColumn = subquery.GenerateOuterColumn(subqueryTableReferenceExpression, innerColumn);
+ var innerColumn = projection.BindProperty(property);
+ var outerColumn = subquery.GenerateOuterColumn(subqueryTableReference, innerColumn);
+
projectionMap[innerColumn] = outerColumn;
propertyExpressions[property] = outerColumn;
}
- foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(typeProjection.StructuralType))
+ foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(projection.StructuralType))
{
- HandleTypeProjection(
- (StructuralTypeProjectionExpression)typeProjection.BindComplexProperty(complexProperty).ValueBufferExpression);
+ var complexPropertyShaper = projection.BindComplexProperty(complexProperty);
+
+ var complexTypeProjectionExpression = LiftEntityProjectionFromSubquery(
+ (StructuralTypeProjectionExpression)complexPropertyShaper.ValueBufferExpression,
+ subqueryTableReference);
+
+ complexPropertyCache[complexProperty] = complexPropertyShaper.Update(complexTypeProjectionExpression);
}
}
@@ -4214,8 +4387,11 @@ void HandleTypeProjection(StructuralTypeProjectionExpression typeProjection)
var tableMap = projection.TableMap.ToDictionary(kvp => kvp.Key, _ => subqueryTableReference);
- var newEntityProjection = new StructuralTypeProjectionExpression(
- projection.StructuralType, propertyExpressions, tableMap, nullable: false, discriminatorExpression);
+ var newEntityProjection = UseOldBehavior32911
+ ? new StructuralTypeProjectionExpression(
+ projection.StructuralType, propertyExpressions, tableMap, nullable: false, discriminatorExpression)
+ : new StructuralTypeProjectionExpression(
+ projection.StructuralType, propertyExpressions, complexPropertyCache, tableMap, nullable: false, discriminatorExpression);
if (projection.StructuralType is IEntityType entityType2)
{
diff --git a/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs b/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs
index 828a7c95b2b..5b9c7dd152d 100644
--- a/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs
+++ b/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs
@@ -17,6 +17,9 @@ namespace Microsoft.EntityFrameworkCore.Query;
///
public class StructuralTypeProjectionExpression : Expression
{
+ private static readonly bool UseOldBehavior32911 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32911", out var enabled32911) && enabled32911;
+
private readonly IReadOnlyDictionary _propertyExpressionMap;
private readonly Dictionary _ownedNavigationMap;
private Dictionary? _complexPropertyCache;
@@ -38,6 +41,32 @@ public StructuralTypeProjectionExpression(
type,
propertyExpressionMap,
new Dictionary(),
+ null,
+ tableMap,
+ nullable,
+ discriminatorExpression)
+ {
+ }
+
+ ///
+ /// 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 StructuralTypeProjectionExpression(
+ ITypeBase type,
+ IReadOnlyDictionary propertyExpressionMap,
+ Dictionary complexPropertyCache,
+ IReadOnlyDictionary tableMap,
+ bool nullable = false,
+ SqlExpression? discriminatorExpression = null)
+ : this(
+ type,
+ propertyExpressionMap,
+ new Dictionary(),
+ complexPropertyCache,
tableMap,
nullable,
discriminatorExpression)
@@ -48,6 +77,7 @@ private StructuralTypeProjectionExpression(
ITypeBase type,
IReadOnlyDictionary propertyExpressionMap,
Dictionary ownedNavigationMap,
+ Dictionary? complexPropertyCache,
IReadOnlyDictionary tableMap,
bool nullable,
SqlExpression? discriminatorExpression = null)
@@ -55,6 +85,7 @@ private StructuralTypeProjectionExpression(
StructuralType = type;
_propertyExpressionMap = propertyExpressionMap;
_ownedNavigationMap = ownedNavigationMap;
+ _complexPropertyCache = complexPropertyCache;
TableMap = tableMap;
IsNullable = nullable;
DiscriminatorExpression = discriminatorExpression;
@@ -115,6 +146,21 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
propertyExpressionMap[property] = newExpression;
}
+ var complexPropertyCache = default(Dictionary);
+ if (!UseOldBehavior32911)
+ {
+ if (_complexPropertyCache != null)
+ {
+ complexPropertyCache = new();
+ foreach (var (complexProperty, complexShaper) in _complexPropertyCache)
+ {
+ var newComplexShaper = (StructuralTypeShaperExpression)visitor.Visit(complexShaper);
+ changed |= complexShaper != newComplexShaper;
+ complexPropertyCache[complexProperty] = newComplexShaper;
+ }
+ }
+ }
+
// We only need to visit the table map since TableReferenceUpdatingExpressionVisitor may need to modify it; it mutates
// TableReferenceExpression (a new TableReferenceExpression is never returned), so we never need a new table map.
foreach (var (_, tableExpression) in TableMap)
@@ -136,7 +182,7 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
return changed
? new StructuralTypeProjectionExpression(
- StructuralType, propertyExpressionMap, ownedNavigationMap, TableMap, IsNullable, discriminatorExpression)
+ StructuralType, propertyExpressionMap, ownedNavigationMap, complexPropertyCache, TableMap, IsNullable, discriminatorExpression)
: this;
}
@@ -159,6 +205,19 @@ public virtual StructuralTypeProjectionExpression MakeNullable()
discriminatorExpression = ce.MakeNullable();
}
+ var complexPropertyCache = default(Dictionary);
+ if (!UseOldBehavior32911)
+ {
+ if (_complexPropertyCache != null)
+ {
+ complexPropertyCache = new();
+ foreach (var (complexProperty, complexShaper) in _complexPropertyCache)
+ {
+ complexPropertyCache[complexProperty] = complexShaper.MakeNullable();
+ }
+ }
+ }
+
var ownedNavigationMap = new Dictionary();
foreach (var (navigation, shaper) in _ownedNavigationMap)
{
@@ -178,6 +237,7 @@ public virtual StructuralTypeProjectionExpression MakeNullable()
StructuralType,
propertyExpressionMap,
ownedNavigationMap,
+ complexPropertyCache,
TableMap,
nullable: true,
discriminatorExpression);
@@ -212,6 +272,23 @@ public virtual StructuralTypeProjectionExpression UpdateEntityType(IEntityType d
}
}
+ var complexPropertyCache = default(Dictionary);
+ if (!UseOldBehavior32911)
+ {
+ if (_complexPropertyCache != null)
+ {
+ complexPropertyCache = new();
+ foreach (var (complexProperty, complexShaper) in _complexPropertyCache)
+ {
+ if (derivedType.IsAssignableFrom(complexProperty.DeclaringType)
+ || complexProperty.DeclaringType.IsAssignableFrom(derivedType))
+ {
+ complexPropertyCache[complexProperty] = complexShaper;
+ }
+ }
+ }
+ }
+
var ownedNavigationMap = new Dictionary();
foreach (var (navigation, entityShaperExpression) in _ownedNavigationMap)
{
@@ -263,7 +340,7 @@ public virtual StructuralTypeProjectionExpression UpdateEntityType(IEntityType d
}
return new StructuralTypeProjectionExpression(
- derivedType, propertyExpressionMap, ownedNavigationMap, newTableMap ?? TableMap, IsNullable, discriminatorExpression);
+ derivedType, propertyExpressionMap, ownedNavigationMap, complexPropertyCache, newTableMap ?? TableMap, IsNullable, discriminatorExpression);
}
///
diff --git a/src/EFCore.Relational/Storage/RelationalTypeMappingInfo.cs b/src/EFCore.Relational/Storage/RelationalTypeMappingInfo.cs
index 32391ba4ab2..260b65b7e31 100644
--- a/src/EFCore.Relational/Storage/RelationalTypeMappingInfo.cs
+++ b/src/EFCore.Relational/Storage/RelationalTypeMappingInfo.cs
@@ -151,7 +151,7 @@ public RelationalTypeMappingInfo(
int? scale)
{
// Note: Empty string is allowed for store type name because SQLite
- _coreTypeMappingInfo = new TypeMappingInfo(null, null, false, unicode, size, null, precision, scale);
+ _coreTypeMappingInfo = new TypeMappingInfo(null, null, false, unicode, size, null, precision, scale, false);
StoreTypeName = storeTypeName;
StoreTypeNameBase = storeTypeNameBase;
IsFixedLength = null;
@@ -225,6 +225,42 @@ public RelationalTypeMappingInfo(
/// Specifies a precision for the mapping, or for default.
/// Specifies a scale for the mapping, or for default.
/// The suggested , or for default.
+ [Obsolete("Use overload that takes 'key' parameter.")]
+ public RelationalTypeMappingInfo(
+ Type? type,
+ RelationalTypeMapping? elementTypeMapping,
+ string? storeTypeName,
+ string? storeTypeNameBase,
+ bool keyOrIndex,
+ bool? unicode,
+ int? size,
+ bool? rowVersion,
+ bool? fixedLength,
+ int? precision,
+ int? scale,
+ DbType? dbType)
+ : this(
+ type, elementTypeMapping, storeTypeName, storeTypeNameBase, keyOrIndex, unicode, size, rowVersion, fixedLength,
+ precision, scale, dbType, keyOrIndex)
+ {
+ }
+
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The CLR type in the model for which mapping is needed.
+ /// The type mapping for elements, if known.
+ /// The database type name.
+ /// The provider-specific relational type name, with any facets removed.
+ /// If , then a special mapping for a key or index may be returned.
+ /// Specifies Unicode or ANSI mapping, or for default.
+ /// Specifies a size for the mapping, or for default.
+ /// Specifies a row-version, or for default.
+ /// Specifies a fixed length mapping, or for default.
+ /// Specifies a precision for the mapping, or for default.
+ /// Specifies a scale for the mapping, or for default.
+ /// The suggested , or for default.
+ /// If , then a special mapping for a key may be returned.
public RelationalTypeMappingInfo(
Type? type = null,
RelationalTypeMapping? elementTypeMapping = null,
@@ -237,9 +273,10 @@ public RelationalTypeMappingInfo(
bool? fixedLength = null,
int? precision = null,
int? scale = null,
- DbType? dbType = null)
+ DbType? dbType = null,
+ bool key = false)
{
- _coreTypeMappingInfo = new TypeMappingInfo(type, elementTypeMapping, keyOrIndex, unicode, size, rowVersion, precision, scale);
+ _coreTypeMappingInfo = new TypeMappingInfo(type, elementTypeMapping, keyOrIndex, unicode, size, rowVersion, precision, scale, key);
IsFixedLength = fixedLength;
StoreTypeName = storeTypeName;
@@ -301,7 +338,16 @@ public int? Scale
public DbType? DbType { get; init; }
///
- /// Indicates whether or not the mapping is part of a key or index.
+ /// Indicates whether or not the mapping is part of a key or foreign key.
+ ///
+ public bool IsKey
+ {
+ get => _coreTypeMappingInfo.IsKey;
+ init => _coreTypeMappingInfo = _coreTypeMappingInfo with { IsKey = value };
+ }
+
+ ///
+ /// Indicates whether or not the mapping is part of a key, foreign key, or index.
///
public bool IsKeyOrIndex
{
diff --git a/src/EFCore.SqlServer.Abstractions/PACKAGE.md b/src/EFCore.SqlServer.Abstractions/PACKAGE.md
new file mode 100644
index 00000000000..90452191d3a
--- /dev/null
+++ b/src/EFCore.SqlServer.Abstractions/PACKAGE.md
@@ -0,0 +1,13 @@
+`Microsoft.EntityFrameworkCore.SqlServer.Abstractions` is a small package containing abstractions which may be useful for applications in places where a dependency on the full `Microsoft.EntityFrameworkCore.SqlServer` is not desirable.
+
+## Usage
+
+This package is included automatically as a dependency of the main [Microsoft.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.SqlServer) package. Usually, the abstractions package is only explicitly installed in places where it is undesirable to use the main package. For example, it can be installed to use `HierarchyId` in POCO entity types which are otherwise independent of EF Core.
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
diff --git a/src/EFCore.SqlServer.HierarchyId/PACKAGE.md b/src/EFCore.SqlServer.HierarchyId/PACKAGE.md
new file mode 100644
index 00000000000..7a9fb3bdc9b
--- /dev/null
+++ b/src/EFCore.SqlServer.HierarchyId/PACKAGE.md
@@ -0,0 +1,22 @@
+`Microsoft.EntityFrameworkCore.SqlServer.HierarchyId` enables use of [hierarchical data for SQL Server and Azure SQL]() with [Entity Framework Core](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore/) and [NetTopologySuite](https://www.nuget.org/packages/NetTopologySuite/).
+
+## Usage
+
+Call `UseHierarchyId` inside the call to `UseSqServer` when configuring the SQLite database provider for your `DbContext`. For example:
+
+```csharp
+protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ => options.UseSqlServer(
+ "Server=localhost;Database=MyDatabase;Trusted_Connection=True;",
+ b => b.HierarchyId());
+```
+
+See [_Hierarchical Data in the SQL Server EF Core Provider_](https://learn.microsoft.com/ef/core/providers/sql-server/hierarchyid) for more information on `HierarchyId` with EF Core.
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
diff --git a/src/EFCore.SqlServer.NTS/PACKAGE.md b/src/EFCore.SqlServer.NTS/PACKAGE.md
new file mode 100644
index 00000000000..7491cd864a2
--- /dev/null
+++ b/src/EFCore.SqlServer.NTS/PACKAGE.md
@@ -0,0 +1,25 @@
+`Microsoft.EntityFrameworkCore.SqlServer.NetTopologySuite` enables use of spatial data for SQL Server and Azure SQL with [Entity Framework Core](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore/) and [NetTopologySuite](https://www.nuget.org/packages/NetTopologySuite/).
+
+## Usage
+
+Call `UseNetTopologySuite` inside the call to `UseSqServer` when configuring the SQLite database provider for your `DbContext`. For example:
+
+```csharp
+protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ => options.UseSqlServer(
+ "Server=localhost;Database=MyDatabase;Trusted_Connection=True;",
+ b => b.UseNetTopologySuite());
+```
+
+For more information on using spatial data with EF Core and SQLite, see:
+
+- [Spatial Data in EF Core](https://learn.microsoft.com/ef/core/modeling/spatial)
+- [Spatial Data in the SQL Server EF Core Provider](https://learn.microsoft.com/ef/core/providers/sql-server/spatial)
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
\ No newline at end of file
diff --git a/src/EFCore.SqlServer/EFCore.SqlServer.csproj b/src/EFCore.SqlServer/EFCore.SqlServer.csproj
index ad1cdb7d882..339e8774d57 100644
--- a/src/EFCore.SqlServer/EFCore.SqlServer.csproj
+++ b/src/EFCore.SqlServer/EFCore.SqlServer.csproj
@@ -48,7 +48,7 @@
-
+
diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOnDeleteConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOnDeleteConvention.cs
index d250176508b..6a8fff3c479 100644
--- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOnDeleteConvention.cs
+++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerOnDeleteConvention.cs
@@ -18,6 +18,9 @@ public class SqlServerOnDeleteConvention : CascadeDeleteConvention,
ISkipNavigationForeignKeyChangedConvention,
IEntityTypeAnnotationChangedConvention
{
+ private static readonly bool UseOldBehavior32732 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32732", out var enabled32732) && enabled32732;
+
///
/// Creates a new instance of .
///
@@ -68,7 +71,8 @@ protected override DeleteBehavior GetTargetDeleteBehavior(IConventionForeignKey
s => s.Inverse != null
&& IsMappedToSameTable(s.DeclaringEntityType, s.TargetEntityType));
- if (skipNavigation != null)
+ if (skipNavigation != null
+ && (UseOldBehavior32732 || skipNavigation.ForeignKey != null))
{
var isFirstSkipNavigation = IsFirstSkipNavigation(skipNavigation);
if (!isFirstSkipNavigation)
diff --git a/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs b/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs
index 1119544f93a..f4192293658 100644
--- a/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs
+++ b/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs
@@ -32,6 +32,9 @@ public class SqlServerMigrationsSqlGenerator : MigrationsSqlGenerator
private static readonly bool UseOldBehavior32457 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32457", out var enabled32457) && enabled32457;
+ private static readonly bool UseOldBehavior32730 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32730", out var enabled32730) && enabled32730;
+
private IReadOnlyList _operations = null!;
private int _variableCounter;
@@ -1415,17 +1418,18 @@ protected override void Generate(SqlOperation operation, IModel? model, Migratio
var batchBuilder = new StringBuilder();
foreach (var line in preBatched)
{
- if (string.IsNullOrWhiteSpace(line))
- {
- continue;
- }
-
var trimmed = line.TrimStart();
if (trimmed.StartsWith("GO", StringComparison.OrdinalIgnoreCase)
&& (UseOldBehavior32457
|| trimmed.Length == 2
|| char.IsWhiteSpace(trimmed[2])))
{
+ if (UseOldBehavior32730
+ && string.IsNullOrWhiteSpace(line))
+ {
+ continue;
+ }
+
var batch = batchBuilder.ToString();
batchBuilder.Clear();
diff --git a/src/EFCore.SqlServer/PACKAGE.md b/src/EFCore.SqlServer/PACKAGE.md
new file mode 100644
index 00000000000..4cd837f73e9
--- /dev/null
+++ b/src/EFCore.SqlServer/PACKAGE.md
@@ -0,0 +1,24 @@
+`Microsoft.EntityFrameworkCore.SqlServer` is the EF Core database provider package for Microsoft SQL Server and Azure SQL.
+
+## Usage
+
+Call the `UseSqlServer` method to choose the SQL Server/Azure SQL database provider for your `DbContext`. For example:
+
+```csharp
+protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+{
+ optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True;");
+}
+```
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Additional documentation
+
+See [Microsoft SQL Server EF Core Database Provider](https://learn.microsoft.com/ef/core/providers/sql-server/) for more information about the features of this database provider.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
\ No newline at end of file
diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs
index 4baa71272c9..28c74f76cbc 100644
--- a/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs
+++ b/src/EFCore.SqlServer/Query/Internal/SqlServerJsonPostprocessor.cs
@@ -32,6 +32,9 @@ private readonly
private RelationalTypeMapping? _nvarcharMaxTypeMapping;
+ private static readonly bool UseOldBehavior32976 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32976", out var enabled32976) && enabled32976;
+
///
/// 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
@@ -70,7 +73,14 @@ public virtual Expression Process(Expression expression)
switch (expression)
{
case ShapedQueryExpression shapedQueryExpression:
- return shapedQueryExpression.UpdateQueryExpression(Visit(shapedQueryExpression.QueryExpression));
+ shapedQueryExpression = shapedQueryExpression.UpdateQueryExpression(Visit(shapedQueryExpression.QueryExpression));
+
+ if (!UseOldBehavior32976)
+ {
+ shapedQueryExpression = shapedQueryExpression.UpdateShaperExpression(Visit(shapedQueryExpression.ShaperExpression));
+ }
+
+ return shapedQueryExpression;
case SelectExpression selectExpression:
{
diff --git a/src/EFCore.SqlServer/README.md b/src/EFCore.SqlServer/README.md
deleted file mode 100644
index e1540d20a2b..00000000000
--- a/src/EFCore.SqlServer/README.md
+++ /dev/null
@@ -1,35 +0,0 @@
-Microsoft.EntityFrameworkCore.SqlServer is the database provider for Microsoft SQL Server and Azure SQL. This providers allows you to use Entity Framework Core with Microsoft SQL Server and Azure SQL databases.
-
-## Getting started
-
-`Microsoft.EntityFrameworkCore.SqlServer` is the EF Core provider for Microsoft SQL Server and Azure SQL. See [Getting Started](https://learn.microsoft.com/ef/core/modeling/#use-fluent-api-to-configure-a-model) for more information.
-
-### Prerequisites
-
-- Supported database Engines: Microsoft SQL Server (2012 onwards)
-- The provider references Microsoft.Data.SqlClient (not System.Data.SqlClient). If your project takes a direct dependency on SqlClient, make sure it references the Microsoft.Data.SqlClient package.
-
-## Usage
-
-Once you've installed the package, you can use it in your Entity Framework Core application by specifying the SQL Server provider in your DbContext's OnConfiguring method:
-
-```csharp
-protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
-{
- optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True;");
-}
-```
-
-In this example, we're using the (localdb)\mssqllocaldb server with the MyDatabase database. You'll need to adjust the connection string to match your own SQL Server instance and database.
-
-## Features
-
-The SQL Server provider supports all common features of [Entity Framework Core](https://learn.microsoft.com/ef/core/) as well as some [SQL Server-specific features](https://learn.microsoft.com/ef/core/providers/sql-server/?tabs=dotnet-core-cli) including temporal tables and memory-optimized tables.
-
-## Additional documentation
-
-For more information on using the SQL Server provider for Entity Framework Core, you can refer to the official [documentation](https://learn.microsoft.com/en-us/ef/core/providers/sql-server/?tabs=dotnet-core-cli).
-
-## Feedback
-
-If you encounter a bug or would like to request a feature, [submit an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
\ No newline at end of file
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerDatabaseCreator.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerDatabaseCreator.cs
index c85be77e61c..589440790a1 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerDatabaseCreator.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerDatabaseCreator.cs
@@ -1,6 +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.ComponentModel;
using System.Transactions;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore.SqlServer.Internal;
@@ -322,7 +323,8 @@ private bool RetryOnExistsFailure(SqlException exception)
// Microsoft.Data.SqlClient.SqlException: Unable to open the physical file xxxxxxx.
// And (Number 18456)
// Microsoft.Data.SqlClient.SqlException: Login failed for user 'xxxxxxx'.
- if (exception.Number is 233 or -2 or 4060 or 1832 or 5120 or 18456)
+ if ((exception.Number is 203 && exception.InnerException is Win32Exception)
+ || (exception.Number is 233 or -2 or 4060 or 1832 or 5120 or 18456))
{
ClearPool();
return true;
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTransientExceptionDetector.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTransientExceptionDetector.cs
index 766d0c0504d..6b11941e6b3 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTransientExceptionDetector.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTransientExceptionDetector.cs
@@ -1,6 +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.ComponentModel;
using Microsoft.Data.SqlClient;
namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
@@ -670,6 +671,16 @@ public static bool ShouldRetryOn(Exception? ex)
// allowed connections) on the server. (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by
// the remote host.)
case 233:
+ return true;
+ // SQL Error Code: 203
+ // A connection was successfully established with the server, but then an error occurred during the pre-login handshake.
+ // (provider: TCP Provider, error: 0 - 20) ---> System.ComponentModel.Win32Exception (203): Unknown error: 203
+ case 203:
+ if (ex.InnerException is Win32Exception)
+ {
+ return true;
+ }
+ continue;
// SQL Error Code: 121
// The semaphore timeout period has expired
case 121:
diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs
index a736f5e9053..d3b01e5bdc4 100644
--- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs
+++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs
@@ -15,6 +15,9 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
///
public class SqlServerTypeMappingSource : RelationalTypeMappingSource
{
+ private static readonly bool UseOldBehavior32898 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32898", out var enabled32898) && enabled32898;
+
private static readonly SqlServerFloatTypeMapping RealAlias
= new("placeholder", storeTypePostfix: StoreTypePostfix.None);
@@ -338,7 +341,7 @@ public SqlServerTypeMappingSource(
size: size,
fixedLength: isFixedLength,
storeTypePostfix: storeTypeName == null ? StoreTypePostfix.Size : StoreTypePostfix.None,
- useKeyComparison: mappingInfo.IsKeyOrIndex);
+ useKeyComparison: UseOldBehavior32898 ? mappingInfo.IsKeyOrIndex : mappingInfo.IsKey);
}
if (clrType == typeof(byte[]))
diff --git a/src/EFCore.Sqlite.Core/PACKAGE.md b/src/EFCore.Sqlite.Core/PACKAGE.md
new file mode 100644
index 00000000000..e5b96b6b252
--- /dev/null
+++ b/src/EFCore.Sqlite.Core/PACKAGE.md
@@ -0,0 +1,21 @@
+The `Microsoft.EntityFrameworkCore.Sqlite.Core` package contains the code for the SQLite EF Core database provider. However, it does not automatically bring in any SQLite native binary, instead requiring that the application install and initialize the binary to use.
+
+## Usage
+
+Only use this package if you need to change to a different SQLite native binary that the one supplied by [Microsoft.EntityFrameworkCore.Sqlite](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite).
+
+To use this "Core" package, also install a [SQLite binary package](https://www.nuget.org/profiles/SQLitePCLRaw) and initialize it with `SQLitePCL.Batteries_V2.Init();` or similar. See [github.com/ericsink/SQLitePCL.raw](https://github.com/ericsink/SQLitePCL.raw) for more information.
+
+Following this, call `UseSqlite` just as you when using [Microsoft.EntityFrameworkCore.Sqlite](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite).
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Additional documentation
+
+See [SQLite EF Core Database Provider](https://learn.microsoft.com/ef/core/providers/sqlite/) for more information about the features of this database provider.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
\ No newline at end of file
diff --git a/src/EFCore.Sqlite.NTS/PACKAGE.md b/src/EFCore.Sqlite.NTS/PACKAGE.md
new file mode 100644
index 00000000000..3e6ee1c35a0
--- /dev/null
+++ b/src/EFCore.Sqlite.NTS/PACKAGE.md
@@ -0,0 +1,23 @@
+`Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite` enables use of [SpatiaLite spatial data for SQLite](https://www.gaia-gis.it/fossil/libspatialite/index) with [Entity Framework Core](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore/) and [NetTopologySuite](https://www.nuget.org/packages/NetTopologySuite/).
+
+## Usage
+
+Call `UseNetTopologySuite` inside the call to `UseSqlite` when configuring the SQLite database provider for your `DbContext`. For example:
+
+```csharp
+protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ => options.UseSqlite("Data Source=spatialdata.dat", b => b.UseNetTopologySuite());
+```
+
+For more information on using spatial data with EF Core and SQLite, see:
+
+- [Spatial Data in EF Core](https://learn.microsoft.com/ef/core/modeling/spatial)
+- [Spatial Data in the SQLite EF Core Provider](https://learn.microsoft.com/ef/core/providers/sqlite/spatial)
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
\ No newline at end of file
diff --git a/src/EFCore.Sqlite/PACKAGE.md b/src/EFCore.Sqlite/PACKAGE.md
new file mode 100644
index 00000000000..27712755653
--- /dev/null
+++ b/src/EFCore.Sqlite/PACKAGE.md
@@ -0,0 +1,24 @@
+`Microsoft.EntityFrameworkCore.Sqlite` is the EF Core database provider package for SQLite.
+
+## Usage
+
+Call the `UseSqlite` method to choose the SQLite database provider for your `DbContext`. For example:
+
+```csharp
+protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+{
+ optionsBuilder.UseSqlite("Data Source=databse.dat");
+}
+```
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Additional documentation
+
+See [SQLite EF Core Database Provider](https://learn.microsoft.com/ef/core/providers/sqlite/) for more information about the features of this database provider.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
\ No newline at end of file
diff --git a/src/EFCore.Templates/PACKAGE.md b/src/EFCore.Templates/PACKAGE.md
new file mode 100644
index 00000000000..4a6aa837842
--- /dev/null
+++ b/src/EFCore.Templates/PACKAGE.md
@@ -0,0 +1,25 @@
+The `Microsoft.EntityFrameworkCore.Templates` package contains T4 templates for scaffolding (reverse engineering) a `DbContext` and entity types from an existing database.
+
+## Usage
+
+First, install the templates NuGet package:
+
+```
+dotnet new install Microsoft.EntityFrameworkCore.Templates
+```
+
+Next, can templates to your project:
+
+```dotnetcli
+dotnet new ef-templates
+```
+
+See [_Custom Reverse Engineering Templates_](https://learn.microsoft.com/ef/core/managing-schemas/scaffolding/templates) for more information on using T4 templates with EF Core.
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
diff --git a/src/EFCore.Tools/EFCore.Tools.nuspec b/src/EFCore.Tools/EFCore.Tools.nuspec
index 23c5e9edff5..fcc3a5c9448 100644
--- a/src/EFCore.Tools/EFCore.Tools.nuspec
+++ b/src/EFCore.Tools/EFCore.Tools.nuspec
@@ -9,9 +9,11 @@
+ docs\PACKAGE.md
$CommonFileElements$
+
diff --git a/src/EFCore.Tools/PACKAGE.md b/src/EFCore.Tools/PACKAGE.md
new file mode 100644
index 00000000000..b8c900eb63d
--- /dev/null
+++ b/src/EFCore.Tools/PACKAGE.md
@@ -0,0 +1,38 @@
+The Entity Framework Core tools help with design-time development tasks. They're primarily used to manage Migrations and to scaffold a `DbContext` and entity types by reverse engineering the schema of a database.
+
+This package, `Microsoft.EntityFrameworkCore.Tools` is for PowerShell tooling that works in the Visual Studio Package Manager Console (PMC).
+
+## Usage
+
+Install the tools package by running the following in the Visual Studio PMC:
+
+```powershell
+Install-Package Microsoft.EntityFrameworkCore.Tools
+```
+
+The available commands are listed in the following table.
+
+| PMC Command | Usage |
+|--------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------|
+| [Add-Migration](https://learn.microsoft.com/ef/core/cli/powershell#add-migration) | Adds a new migration. |
+| [Bundle-Migration](https://learn.microsoft.com/ef/core/cli/powershell#bundle-migration) | Creates an executable to update the database. |
+| [Drop-Database](https://learn.microsoft.com/ef/core/cli/powershell#drop-database) | Drops the database. |
+| [Get-DbContext](https://learn.microsoft.com/ef/core/cli/powershell#get-dbcontext) | Gets information about a `DbContext` type. |
+| [Get-Help EntityFramework](https://learn.microsoft.com/en-us/ef/core/cli/powershell#common-parameters) | Displays information about Entity Framework commands. |
+| [Get-Migration](https://learn.microsoft.com/ef/core/cli/powershell#get-migration) | Lists available migrations. |
+| [Optimize-DbContext](https://learn.microsoft.com/ef/core/cli/powershell#optimize-dbcontext) | Generates a compiled version of the model used by the `DbContext`. |
+| [Remove-Migration](https://learn.microsoft.com/ef/core/cli/powershell#remove-migration) | Removes the last migration. |
+| [Scaffold-DbContext](https://learn.microsoft.com/ef/core/cli/powershell#scaffold-dbcontext) | Generates a `DbContext` and entity type classes for a specified database. |
+| [Script-DbContext](https://learn.microsoft.com/ef/core/cli/powershell#script-dbcontext) | Generates a SQL script from the `DbContext`. Bypasses any migrations. |
+| [Script-Migration](https://learn.microsoft.com/ef/core/cli/powershell#script-migration) | Generates a SQL script from the migrations. |
+| [Update-Database](https://learn.microsoft.com/ef/core/cli/powershell#update-database) | Updates the database to the last migration or to a specified migration. |
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
+
+
diff --git a/src/EFCore.Tools/README.md b/src/EFCore.Tools/README.md
deleted file mode 100644
index a82cd1c5ef2..00000000000
--- a/src/EFCore.Tools/README.md
+++ /dev/null
@@ -1,41 +0,0 @@
-The Entity Framework Core tools help with design-time development tasks. They're primarily used to manage Migrations and to scaffold a DbContext and entity types by reverse engineering the schema of a database.
-Microsoft.EntityFrameworkCore.Tools is for PowerShell tooling that works in the Visual Studio Package Manager Console.
-
-## Getting started
-
-The Package Manager Console (PMC) tools for Entity Framework Core perform design-time development tasks. For example, they create migrations, apply migrations, and generate code for a model based on an existing database. The commands run inside of Visual Studio using the Package Manager Console. These tools work with both .NET Framework and .NET Core projects.
-
-### Prerequisites
-
-Before using the tools:
-
-- [Understand the difference between target and startup project](https://learn.microsoft.com/en-us/ef/core/cli/powershell#target-and-startup-project).
-- [Learn how to use the tools with .NET Standard class libraries](https://learn.microsoft.com/en-us/ef/core/cli/powershell#other-target-frameworks).
-- [For ASP.NET Core projects, set the environment](https://learn.microsoft.com/en-us/ef/core/cli/powershell#aspnet-core-environment).
-
-## Usage
-
-PMC Command | Usage
--- | --
-Get-Help entityframework |Displays information about entity framework commands.
-[Add-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#add-migration) | Creates a migration by adding a migration snapshot.
-[Bundle-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#bundle-migration) | Creates an executable to update the database.
-[Get-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#get-dbcontext) | Gets information about a DbContext type.
-[Drop-Database](https://learn.microsoft.com/en-us/ef/core/cli/powershell#drop-database) | Drops the database.
-[Get-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#get-migration) | Lists available migrations.
-[Optimize-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#optimize-dbcontext) | Generates a compiled version of the model used by the `DbContext`.
-[Remove-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#remove-migration) | Removes the last migration snapshot.
-[Scaffold-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#scaffold-dbcontext) | Generates a DbContext and entity type classes for a specified database. This is called reverse engineering.
-[Script-DbContext](https://learn.microsoft.com/en-us/ef/core/cli/powershell#script-dbcontext) | Generates a SQL script from the DbContext. Bypasses any migrations.
-[Script-Migration](https://learn.microsoft.com/en-us/ef/core/cli/powershell#script-migration) | Generates a SQL script using all the migration snapshots.
-[Update-Database](https://learn.microsoft.com/en-us/ef/core/cli/powershell#update-database) | Updates the database schema based on the last migration snapshot.
-
-## Additional documentation
-
-- [Migrations](https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/)
-- [Reverse Engineering](https://learn.microsoft.com/en-us/ef/core/managing-schemas/scaffolding/?tabs=dotnet-core-cli)
-- [Compiled models](https://learn.microsoft.com/en-us/ef/core/performance/advanced-performance-topics?tabs=with-di%2Cwith-constant#compiled-models)
-
-## Feedback
-
-If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
\ No newline at end of file
diff --git a/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs b/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs
index 011bb336e01..b0b0b9692b6 100644
--- a/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs
+++ b/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.EntityFrameworkCore.Metadata.Internal;
+using Microsoft.EntityFrameworkCore.Query.Internal;
namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
@@ -99,7 +100,7 @@ public override void SetValues(PropertyValues propertyValues)
for (var i = 0; i < _values.Length; i++)
{
- SetValue(i, propertyValues[Properties[i].Name]);
+ SetValue(i, EntityMaterializerSource.UseOldBehavior32701 ? propertyValues[Properties[i].Name] : propertyValues[Properties[i]]);
}
}
@@ -110,7 +111,9 @@ public override void SetValues(PropertyValues propertyValues)
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public override IReadOnlyList Properties
- => _properties ??= EntityType.GetProperties().ToList();
+ => _properties ??= EntityMaterializerSource.UseOldBehavior32701
+ ? EntityType.GetProperties().ToList()
+ : EntityType.GetFlattenedProperties().ToList();
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs b/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs
index 927f2e1c678..3d3d68b973e 100644
--- a/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs
+++ b/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.EntityFrameworkCore.Metadata.Internal;
+using Microsoft.EntityFrameworkCore.Query.Internal;
namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
@@ -94,7 +95,8 @@ public override void SetValues(PropertyValues propertyValues)
foreach (var property in Properties)
{
- SetValueInternal(property, propertyValues[property.Name]);
+ SetValueInternal(
+ property, EntityMaterializerSource.UseOldBehavior32701 ? propertyValues[property.Name] : propertyValues[property]);
}
}
@@ -107,7 +109,9 @@ public override void SetValues(PropertyValues propertyValues)
public override IReadOnlyList Properties
{
[DebuggerStepThrough]
- get => _properties ??= EntityType.GetProperties().ToList();
+ get => _properties ??= EntityMaterializerSource.UseOldBehavior32701
+ ? EntityType.GetProperties().ToList()
+ : EntityType.GetFlattenedProperties().ToList();
}
///
diff --git a/src/EFCore/Internal/EntityFinder.cs b/src/EFCore/Internal/EntityFinder.cs
index 6e95ffababe..cc9d612a886 100644
--- a/src/EFCore/Internal/EntityFinder.cs
+++ b/src/EFCore/Internal/EntityFinder.cs
@@ -3,6 +3,7 @@
using System.Collections;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
+using Microsoft.EntityFrameworkCore.Query.Internal;
namespace Microsoft.EntityFrameworkCore.Internal;
@@ -859,17 +860,53 @@ private static Expression> BuildProjection(IEntityType en
var entityParameter = Expression.Parameter(typeof(object), "e");
var projections = new List();
- foreach (var property in entityType.GetProperties())
+
+ if (EntityMaterializerSource.UseOldBehavior32701)
{
- projections.Add(
- Expression.Convert(
+ foreach (var property in entityType.GetProperties())
+ {
+ projections.Add(
Expression.Convert(
- Expression.Call(
- EF.PropertyMethod.MakeGenericMethod(property.ClrType),
- entityParameter,
- Expression.Constant(property.Name, typeof(string))),
- property.ClrType),
- typeof(object)));
+ Expression.Convert(
+ Expression.Call(
+ EF.PropertyMethod.MakeGenericMethod(property.ClrType),
+ entityParameter,
+ Expression.Constant(property.Name, typeof(string))),
+ property.ClrType),
+ typeof(object)));
+ }
+ }
+ else
+ {
+ foreach (var property in entityType.GetFlattenedProperties())
+ {
+ var path = new List { property };
+ while (path[^1].DeclaringType is IComplexType complexType)
+ {
+ path.Add(complexType.ComplexProperty);
+ }
+
+ Expression instanceExpression = entityParameter;
+ for (var i = path.Count - 1; i >= 0; i--)
+ {
+ instanceExpression = Expression.Call(
+ EF.PropertyMethod.MakeGenericMethod(path[i].ClrType),
+ instanceExpression,
+ Expression.Constant(path[i].Name, typeof(string)));
+
+ if (i != 0 && instanceExpression.Type.IsValueType)
+ {
+ instanceExpression = Expression.Convert(instanceExpression, typeof(object));
+ }
+ }
+
+ projections.Add(
+ Expression.Convert(
+ Expression.Convert(
+ instanceExpression,
+ property.ClrType),
+ typeof(object)));
+ }
}
return Expression.Lambda>(
diff --git a/src/EFCore/Metadata/Internal/EntityTypeExtensions.cs b/src/EFCore/Metadata/Internal/EntityTypeExtensions.cs
index 30476e2396e..fcdf6afcd26 100644
--- a/src/EFCore/Metadata/Internal/EntityTypeExtensions.cs
+++ b/src/EFCore/Metadata/Internal/EntityTypeExtensions.cs
@@ -4,6 +4,8 @@
// ReSharper disable ArgumentsStyleOther
// ReSharper disable ArgumentsStyleNamedExpression
+using Microsoft.EntityFrameworkCore.Query.Internal;
+
namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
///
@@ -309,7 +311,9 @@ public static IProperty CheckPropertyBelongsToType(
{
Check.NotNull(property, nameof(property));
- if ((property.DeclaringType as IEntityType)?.IsAssignableFrom(entityType) != true)
+ if ((EntityMaterializerSource.UseOldBehavior32701
+ && (property.DeclaringType as IEntityType)?.IsAssignableFrom(entityType) != true)
+ || !property.DeclaringType.ContainingEntityType.IsAssignableFrom(entityType))
{
throw new InvalidOperationException(
CoreStrings.PropertyDoesNotBelong(property.Name, property.DeclaringType.DisplayName(), entityType.DisplayName()));
diff --git a/src/EFCore/Metadata/Internal/Property.cs b/src/EFCore/Metadata/Internal/Property.cs
index a8fcb4eee96..e88f9f4bef5 100644
--- a/src/EFCore/Metadata/Internal/Property.cs
+++ b/src/EFCore/Metadata/Internal/Property.cs
@@ -40,6 +40,9 @@ public class Property : PropertyBase, IMutableProperty, IConventionProperty, IPr
public static readonly bool UseOldBehavior32422 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32422", out var enabled32422) && enabled32422;
+ private static readonly bool UseOldBehavior33176 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue33176", out var enabled33176) && enabled33176;
+
///
/// 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
@@ -911,9 +914,10 @@ public virtual (ValueConverter? ValueConverter, Type? ValueConverterType, Type?
bool throwOnValueConverterConflict = true,
bool throwOnProviderClrTypeConflict = true)
{
- Queue<(Property CurrentProperty, Property CycleBreakingPropert, int CyclePosition, int MaxCycleLength)>? queue = null;
- (Property CurrentProperty, Property CycleBreakingPropert, int CyclePosition, int MaxCycleLength)? currentNode =
+ Queue<(Property CurrentProperty, Property CycleBreakingProperty, int CyclePosition, int MaxCycleLength)>? queue = null;
+ (Property CurrentProperty, Property CycleBreakingProperty, int CyclePosition, int MaxCycleLength)? currentNode =
(this, this, 0, 2);
+ HashSet? visitedProperties = null;
ValueConverter? valueConverter = null;
Type? valueConverterType = null;
@@ -922,12 +926,17 @@ public virtual (ValueConverter? ValueConverter, Type? ValueConverterType, Type?
{
var (property, cycleBreakingProperty, cyclePosition, maxCycleLength) = currentNode ?? queue!.Dequeue();
currentNode = null;
- if (cyclePosition >= ForeignKey.LongestFkChainAllowedLength)
+ if (cyclePosition >= ForeignKey.LongestFkChainAllowedLength
+ || (!UseOldBehavior33176
+ && queue is not null
+ && queue.Count >= ForeignKey.LongestFkChainAllowedLength))
{
throw new InvalidOperationException(
CoreStrings.RelationshipCycle(DeclaringType.DisplayName(), Name, "ValueConverter"));
}
+ visitedProperties?.Add(property);
+
foreach (var foreignKey in property.GetContainingForeignKeys())
{
for (var propertyIndex = 0; propertyIndex < foreignKey.Properties.Count; propertyIndex++)
@@ -956,6 +965,13 @@ public virtual (ValueConverter? ValueConverter, Type? ValueConverterType, Type?
{
queue = new();
queue.Enqueue(currentNode.Value);
+ visitedProperties = new() { property };
+ }
+
+ if (!UseOldBehavior33176
+ && visitedProperties?.Contains(principalProperty) == true)
+ {
+ break;
}
if (cyclePosition == maxCycleLength - 1)
diff --git a/src/EFCore/README.md b/src/EFCore/PACKAGE.md
similarity index 92%
rename from src/EFCore/README.md
rename to src/EFCore/PACKAGE.md
index 8d763f3028d..cffa0337676 100644
--- a/src/EFCore/README.md
+++ b/src/EFCore/PACKAGE.md
@@ -64,10 +64,10 @@ public class Customer
## Additional documentation
-- [Getting Started with Entity Framework Core](https://learn.microsoft.com/en-us/ef/core/get-started/overview/first-app?tabs=netcore-cli).
-- Follow the [ASP.NET Core Tutorial](https://learn.microsoft.com/en-us/aspnet/core/data/ef-rp/intro?view=aspnetcore-7.0&tabs=visual-studio) to use EF Core in a web app.
-- [Releases and planning(roadmap)](https://learn.microsoft.com/en-us/ef/core/what-is-new/)
-- [How to write an EF Core provider](https://learn.microsoft.com/en-us/ef/core/providers/writing-a-provider)
+- [Getting Started with Entity Framework Core](https://learn.microsoft.com/ef/core/get-started/overview/first-app).
+- Follow the [ASP.NET Core Tutorial](https://learn.microsoft.com/aspnet/core/data/ef-rp/intro?view=aspnetcore-7.0&tabs=visual-studio) to use EF Core in a web app.
+- [Releases and planning(roadmap)](https://learn.microsoft.com/ef/core/what-is-new/)
+- [How to write an EF Core provider](https://learn.microsoft.com/ef/core/providers/writing-a-provider)
## Feedback
diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs
index fd7a93208b0..8c9cd8e6df6 100644
--- a/src/EFCore/Properties/CoreStrings.Designer.cs
+++ b/src/EFCore/Properties/CoreStrings.Designer.cs
@@ -993,6 +993,12 @@ public static string EmptyComplexType(object? complexType)
GetString("EmptyComplexType", nameof(complexType)),
complexType);
+ ///
+ /// The empty string is not valid JSON.
+ ///
+ public static string EmptyJsonString
+ => GetString("EmptyJsonString");
+
///
/// Cannot translate '{comparisonOperator}' on a subquery expression of entity type '{entityType}' because it has a composite primary key. See https://go.microsoft.com/fwlink/?linkid=2141942 for information on how to rewrite your query.
///
diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx
index 45b45251ed8..e3f7f5d18d2 100644
--- a/src/EFCore/Properties/CoreStrings.resx
+++ b/src/EFCore/Properties/CoreStrings.resx
@@ -486,6 +486,9 @@
Complex type '{complexType}' has no properties defines. Configure at least one property or don't include this type in the model.
+
+ The empty string is not valid JSON.
+
Cannot translate '{comparisonOperator}' on a subquery expression of entity type '{entityType}' because it has a composite primary key. See https://go.microsoft.com/fwlink/?linkid=2141942 for information on how to rewrite your query.
diff --git a/src/EFCore/Query/Internal/EntityMaterializerSource.cs b/src/EFCore/Query/Internal/EntityMaterializerSource.cs
index b1a72bc24c3..7c1d09a73ec 100644
--- a/src/EFCore/Query/Internal/EntityMaterializerSource.cs
+++ b/src/EFCore/Query/Internal/EntityMaterializerSource.cs
@@ -24,6 +24,9 @@ public class EntityMaterializerSource : IEntityMaterializerSource
public static readonly bool UseOldBehavior31866 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue31866", out var enabled31866) && enabled31866;
+ internal static readonly bool UseOldBehavior32701 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32701", out var enabled32701) && enabled32701;
+
private static readonly MethodInfo InjectableServiceInjectedMethod
= typeof(IInjectableService).GetMethod(nameof(IInjectableService.Injected))!;
@@ -117,15 +120,16 @@ public Expression CreateMaterializeExpression(
var constructorExpression = constructorBinding.CreateConstructorExpression(bindingInfo);
- if (_materializationInterceptor == null)
+ if (_materializationInterceptor == null
+ // TODO: This currently applies the materialization interceptor only on the root structural type - any contained complex types
+ // don't get intercepted.
+ || (structuralType is not IEntityType && !UseOldBehavior32701))
{
return properties.Count == 0 && blockExpressions.Count == 0
? constructorExpression
: CreateMaterializeExpression(blockExpressions, instanceVariable, constructorExpression, properties, bindingInfo);
}
- // TODO: This currently applies the materialization interceptor only on the root structural type - any contained complex types
- // don't get intercepted.
return CreateInterceptionMaterializeExpression(
structuralType,
properties,
diff --git a/src/EFCore/Storage/Json/JsonValueReaderWriter.cs b/src/EFCore/Storage/Json/JsonValueReaderWriter.cs
index 92e2c2f3021..2a085ebbfed 100644
--- a/src/EFCore/Storage/Json/JsonValueReaderWriter.cs
+++ b/src/EFCore/Storage/Json/JsonValueReaderWriter.cs
@@ -14,6 +14,9 @@ namespace Microsoft.EntityFrameworkCore.Storage.Json;
///
public abstract class JsonValueReaderWriter
{
+ private static readonly bool UseOldBehavior32896 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32896", out var enabled32896) && enabled32896;
+
///
/// Ensures the external types extend from the generic
///
@@ -63,6 +66,12 @@ internal JsonValueReaderWriter()
/// The read value.
public object FromJsonString(string json, object? existingObject = null)
{
+ if (!UseOldBehavior32896
+ && string.IsNullOrWhiteSpace(json))
+ {
+ throw new InvalidOperationException(CoreStrings.EmptyJsonString);
+ }
+
var readerManager = new Utf8JsonReaderManager(new JsonReaderData(Encoding.UTF8.GetBytes(json)), null);
return FromJson(ref readerManager, existingObject);
}
diff --git a/src/EFCore/Storage/TypeMappingInfo.cs b/src/EFCore/Storage/TypeMappingInfo.cs
index 5cec9557220..b98b109c860 100644
--- a/src/EFCore/Storage/TypeMappingInfo.cs
+++ b/src/EFCore/Storage/TypeMappingInfo.cs
@@ -182,7 +182,8 @@ public TypeMappingInfo(
var property = principals[0];
ElementTypeMapping = property.GetElementType()?.FindTypeMapping();
- IsKeyOrIndex = property.IsKey() || property.IsForeignKey() || property.IsIndex();
+ IsKey = property.IsKey() || property.IsForeignKey();
+ IsKeyOrIndex = IsKey || property.IsIndex();
Size = fallbackSize ?? mappingHints?.Size;
IsUnicode = fallbackUnicode ?? mappingHints?.IsUnicode;
IsRowVersion = property is { IsConcurrencyToken: true, ValueGenerated: ValueGenerated.OnAddOrUpdate };
@@ -227,6 +228,32 @@ public TypeMappingInfo(
/// Specifies a row-version, or for default.
/// Specifies a precision for the mapping, or for default.
/// Specifies a scale for the mapping, or for default.
+ [Obsolete("Use overload that takes 'key' parameter.")]
+ public TypeMappingInfo(
+ Type? type,
+ CoreTypeMapping? elementTypeMapping,
+ bool keyOrIndex,
+ bool? unicode,
+ int? size,
+ bool? rowVersion,
+ int? precision,
+ int? scale)
+ : this(type, elementTypeMapping, keyOrIndex, unicode, size, rowVersion, precision, scale, false)
+ {
+ }
+
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The CLR type in the model for which mapping is needed.
+ /// The type mapping for elements, if known.
+ /// If , then a special mapping for a key or index may be returned.
+ /// Specifies Unicode or ANSI mapping, or for default.
+ /// Specifies a size for the mapping, or for default.
+ /// Specifies a row-version, or for default.
+ /// Specifies a precision for the mapping, or for default.
+ /// Specifies a scale for the mapping, or for default.
+ /// If , then a special mapping for a key may be returned.
public TypeMappingInfo(
Type? type = null,
CoreTypeMapping? elementTypeMapping = null,
@@ -235,11 +262,12 @@ public TypeMappingInfo(
int? size = null,
bool? rowVersion = null,
int? precision = null,
- int? scale = null)
+ int? scale = null,
+ bool key = false)
{
ClrType = type?.UnwrapNullableType();
ElementTypeMapping = elementTypeMapping;
-
+ IsKey = key;
IsKeyOrIndex = keyOrIndex;
Size = size;
IsUnicode = unicode;
@@ -266,6 +294,7 @@ public TypeMappingInfo(
int? scale = null)
{
IsRowVersion = source.IsRowVersion;
+ IsKey = source.IsKey;
IsKeyOrIndex = source.IsKeyOrIndex;
var mappingHints = converter.MappingHints;
@@ -295,7 +324,12 @@ public TypeMappingInfo WithConverter(in ValueConverterInfo converterInfo)
=> new(this, converterInfo);
///
- /// Indicates whether or not the mapping is part of a key or index.
+ /// Indicates whether or not the mapping is part of a key or foreign key.
+ ///
+ public bool IsKey { get; init; }
+
+ ///
+ /// Indicates whether or not the mapping is part of a key, foreign key, or index.
///
public bool IsKeyOrIndex { get; init; }
diff --git a/src/Microsoft.Data.Sqlite.Core/Package.md b/src/Microsoft.Data.Sqlite.Core/Package.md
new file mode 100644
index 00000000000..c1c17b8e6e1
--- /dev/null
+++ b/src/Microsoft.Data.Sqlite.Core/Package.md
@@ -0,0 +1,12 @@
+The `Microsoft.Data.Sqlite.Core` package contains the code for SQLite ADO.NET driver. However, it does not automatically bring in any SQLite native binary, instead requiring that the application install and initialize the binary to use.
+
+## Usage
+
+Only use this package if you need to change to a different SQLite native binary that the one supplied by [Microsoft.Data.Sqlite](https://www.nuget.org/packages/Microsoft.Data.Sqlite).
+
+To use this "Core" package, also install a [SQLite binary package](https://www.nuget.org/profiles/SQLitePCLRaw) and initialize it with `SQLitePCL.Batteries_V2.Init();` or similar. See [github.com/ericsink/SQLitePCL.raw](https://github.com/ericsink/SQLitePCL.raw) for more information.
+
+### Getting support
+
+If you have a specific question about using these projects, we encourage you to [ask it on Stack Overflow](https://stackoverflow.com/questions/tagged/microsoft.data.sqlite). If you encounter a bug or would like to request a feature, [submit an issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](.github/SUPPORT.md).
+
diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs b/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
index bd368b2370a..02e75c9aa25 100644
--- a/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
+++ b/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
@@ -66,9 +66,9 @@ static SqliteConnection()
storageFolderType = Type.GetType("Windows.Storage.StorageFolder, Windows, ContentType=WindowsRuntime")
?? Type.GetType("Windows.Storage.StorageFolder, Microsoft.Windows.SDK.NET");
}
- catch (FileLoadException)
+ catch (Exception)
{
- // Ignore "Could not load assembly."
+ // Ignore "Could not load assembly." or any type initialization error.
}
object? currentAppData = null;
diff --git a/src/Microsoft.Data.Sqlite/PACKAGE.md b/src/Microsoft.Data.Sqlite/PACKAGE.md
new file mode 100644
index 00000000000..4c89b180e4f
--- /dev/null
+++ b/src/Microsoft.Data.Sqlite/PACKAGE.md
@@ -0,0 +1,34 @@
+`Microsoft.Data.Sqlite` is a lightweight [ADO.NET provider]([ADO.NET](https://docs.microsoft.com/dotnet/framework/data/adonet/)) for [SQLite](https://www.sqlite.org/index.html). The EF Core provider for SQLite is built on top of this library. However, it can also be used independently or with other data access libraries.
+
+### Installation
+
+The latest stable version is available on [NuGet](https://www.nuget.org/packages/Microsoft.Data.Sqlite).
+
+```sh
+dotnet add package Microsoft.Data.Sqlite
+```
+
+Use the `--version` option to specify a [preview version](https://www.nuget.org/packages/Microsoft.Data.Sqlite/absoluteLatest) to install.
+
+### Basic usage
+
+This library implements the common [ADO.NET](https://docs.microsoft.com/dotnet/framework/data/adonet/) abstractions for connections, commands, data readers, and so on. For more information, see [Microsoft.Data.Sqlite](https://docs.microsoft.com/dotnet/standard/data/sqlite/) on Microsoft Docs.
+
+```cs
+using var connection = new SqliteConnection("Data Source=Blogs.db");
+connection.Open();
+
+using var command = connection.CreateCommand();
+command.CommandText = "SELECT Url FROM Blogs";
+
+using var reader = command.ExecuteReader();
+while (reader.Read())
+{
+ var url = reader.GetString(0);
+}
+```
+
+### Getting support
+
+If you have a specific question about using these projects, we encourage you to [ask it on Stack Overflow](https://stackoverflow.com/questions/tagged/microsoft.data.sqlite). If you encounter a bug or would like to request a feature, [submit an issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](.github/SUPPORT.md).
+
diff --git a/src/dotnet-ef/PACKAGE.md b/src/dotnet-ef/PACKAGE.md
new file mode 100644
index 00000000000..7582724711c
--- /dev/null
+++ b/src/dotnet-ef/PACKAGE.md
@@ -0,0 +1,38 @@
+The Entity Framework Core tools help with design-time development tasks. They're primarily used to manage Migrations and to scaffold a `DbContext` and entity types by reverse engineering the schema of a database.
+
+This package, `dotnet-ef` is for cross-platform command line tooling that can be used anywhere.
+
+## Usage
+
+Install the tool package using:
+
+```dotnetcli
+dotnet tool install --global dotnet-ef
+```
+
+The available commands are listed in the following table.
+
+| Command | Usage |
+|-------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
+| [dotnet ef --help](https://learn.microsoft.com/ef/core/cli/dotnet#common-options) | Displays information about Entity Framework commands. |
+| [dotnet ef database drop](https://learn.microsoft.com/ef/core/cli/dotnet#dotnet-ef-database-drop) | Drops the database. |
+| [dotnet ef database update](https://learn.microsoft.com/ef/core/cli/dotnet#dotnet-ef-database-update) | Updates the database to the last migration or to a specified migration |
+| [dotnet ef dbcontext info](https://learn.microsoft.com/ef/core/cli/dotnet#dotnet-ef-dbcontext-info) | Gets information about a `DbContext` type. |
+| [dotnet ef dbcontext list](https://learn.microsoft.com/ef/core/cli/dotnet#dotnet-ef-dbcontext-list) | Lists available `DbContext` types. |
+| [dotnet ef dbcontext optimize](https://learn.microsoft.com/ef/core/cli/dotnet#dotnet-ef-dbcontext-optimize) | Generates a compiled version of the model used by the `DbContext`. |
+| [dotnet ef dbcontext scaffold](https://learn.microsoft.com/ef/core/cli/dotnet#dotnet-ef-dbcontext-scaffold) | Generates a `DbContext` and entity type classes for a specified database. |
+| [dotnet ef dbcontext script](https://learn.microsoft.com/ef/core/cli/dotnet#dotnet-ef-dbcontext-script) | Generates a SQL script from the `DbContext`. Bypasses any migrations. |
+| [dotnet ef migrations add](https://learn.microsoft.com/ef/core/cli/dotnet#dotnet-ef-migrations-add) | Adds a new migration. |
+| [dotnet ef migrations bundle](https://learn.microsoft.com/ef/core/cli/dotnet#dotnet-ef-migrations-bundle) | Creates an executable to update the database. |
+| [dotnet ef migrations has-pending-model-changes](https://learn.microsoft.com/ef/core/cli/dotnet#dotnet-ef-migrations-has-pending-model-changes) | Checks if any changes have been made to the model since the last migration. |
+| [dotnet ef migrations list](https://learn.microsoft.com/ef/core/cli/dotnet#dotnet-ef-migrations-list) | Lists available migrations. |
+| [dotnet ef migrations remove](https://learn.microsoft.com/ef/core/cli/dotnet#dotnet-ef-migrations-remove) | Removes the last migration. |
+| [dotnet ef migrations script](https://learn.microsoft.com/ef/core/cli/dotnet#dotnet-ef-migrations-script) | Generates a SQL script from the migrations. |
+
+## Getting started with EF Core
+
+See [Getting started with EF Core](https://learn.microsoft.com/ef/core/get-started/overview/install) for more information about EF NuGet packages, including which to install when getting started.
+
+## Feedback
+
+If you encounter a bug or issues with this package,you can [open an Github issue](https://github.com/dotnet/efcore/issues/new/choose). For more details, see [getting support](https://github.com/dotnet/efcore/blob/main/.github/SUPPORT.md).
\ No newline at end of file
diff --git a/src/dotnet-ef/dotnet-ef.nuspec b/src/dotnet-ef/dotnet-ef.nuspec
index 2ceff381f6f..b7df82ccb63 100644
--- a/src/dotnet-ef/dotnet-ef.nuspec
+++ b/src/dotnet-ef/dotnet-ef.nuspec
@@ -5,10 +5,12 @@
+ docs\PACKAGE.md
$CommonFileElements$
+
diff --git a/test/EFCore.AspNet.Specification.Tests/PACKAGE.md b/test/EFCore.AspNet.Specification.Tests/PACKAGE.md
new file mode 100644
index 00000000000..fa068cd075b
--- /dev/null
+++ b/test/EFCore.AspNet.Specification.Tests/PACKAGE.md
@@ -0,0 +1,3 @@
+This package contains tests that can be implemented and then used in database provider repos to test EF Core against that provider.
+
+See [Writing a Database Provider](https://learn.microsoft.com/ef/core/providers/writing-a-provider) for more information.
diff --git a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs
index f79aef83b49..c942f6ee484 100644
--- a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs
+++ b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs
@@ -370,6 +370,25 @@ public class FooExtension
public T Bar { get; set; }
}
+ public class Parrot
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public TChild Child { get; set; }
+ }
+
+ public class Parrot
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public Beak Child { get; set; }
+ }
+
+ public class Beak
+ {
+ public string Name { get; set; }
+ }
+
#region Model
[ConditionalFact]
@@ -4648,6 +4667,139 @@ public virtual void Property_column_name_is_stored_in_snapshot_when_DefaultColum
var property = entityType.FindProperty("FooExtensionId");
Assert.NotNull(property);
Assert.Equal("FooExtensionId", property.GetColumnName());
+
+ Assert.Collection(
+ model.GetRelationalModel().Tables,
+ t =>
+ {
+
+ Assert.Equal("BarBase", t.Name);
+ Assert.Equal(["Id", "Discriminator", "FooExtensionId"], t.Columns.Select(t => t.Name));
+ },
+ t =>
+ {
+
+ Assert.Equal("FooExtension", t.Name);
+ Assert.Equal(["Id"], t.Columns.Select(t => t.Name));
+ });
+ });
+
+ [ConditionalFact]
+ public virtual void Generic_entity_type_with_owned_entities()
+ => Test(
+ modelBuilder => modelBuilder.Entity>().OwnsOne(e => e.Child),
+ AddBoilerPlate(
+ """
+ modelBuilder
+ .HasDefaultSchema("DefaultSchema")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+ SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+ modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Parrot", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Parrot", "DefaultSchema");
+ });
+
+ modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Parrot", b =>
+ {
+ b.OwnsOne("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Beak", "Child", b1 =>
+ {
+ b1.Property("ParrotId")
+ .HasColumnType("int");
+
+ b1.Property("Name")
+ .HasColumnType("nvarchar(max)");
+
+ b1.HasKey("ParrotId");
+
+ b1.ToTable("Parrot", "DefaultSchema");
+
+ b1.WithOwner()
+ .HasForeignKey("ParrotId");
+ });
+
+ b.Navigation("Child");
+ });
+"""),
+ model =>
+ {
+ var parentType = model.FindEntityType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Parrot");
+ Assert.NotNull(parentType);
+ Assert.NotNull(parentType.FindNavigation("Child")!.TargetEntityType);
+
+ var table = model.GetRelationalModel().Tables.Single();
+ Assert.Equal(["Id", "Child_Name", "Name"], table.Columns.Select(t => t.Name));
+ });
+
+ [ConditionalFact]
+ public virtual void Non_generic_entity_type_with_owned_entities()
+ => Test(
+ modelBuilder => modelBuilder.Entity().OwnsOne(e => e.Child),
+ AddBoilerPlate(
+ """
+ modelBuilder
+ .HasDefaultSchema("DefaultSchema")
+ .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+ SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+ modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Parrot", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("int");
+
+ SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"));
+
+ b.Property("Name")
+ .HasColumnType("nvarchar(max)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Parrot", "DefaultSchema");
+ });
+
+ modelBuilder.Entity("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Parrot", b =>
+ {
+ b.OwnsOne("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Beak", "Child", b1 =>
+ {
+ b1.Property("ParrotId")
+ .HasColumnType("int");
+
+ b1.Property("Name")
+ .HasColumnType("nvarchar(max)");
+
+ b1.HasKey("ParrotId");
+
+ b1.ToTable("Parrot", "DefaultSchema");
+
+ b1.WithOwner()
+ .HasForeignKey("ParrotId");
+ });
+
+ b.Navigation("Child");
+ });
+"""),
+ model =>
+ {
+ var parentType = model.FindEntityType("Microsoft.EntityFrameworkCore.Migrations.ModelSnapshotSqlServerTest+Parrot");
+ Assert.NotNull(parentType);
+ Assert.NotNull(parentType.FindNavigation("Child")!.TargetEntityType);
+
+ var table = model.GetRelationalModel().Tables.Single();
+ Assert.Equal(["Id", "Child_Name", "Name"], table.Columns.Select(t => t.Name));
});
[ConditionalFact]
diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs
index 8432c8ac498..82362c3c216 100644
--- a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs
+++ b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs
@@ -34659,6 +34659,111 @@ private IRelationalModel CreateRelationalModel()
microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("FlagsEnum2", flagsEnum2ColumnBase);
var idColumnBase = new ColumnBase("Id", "bigint", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase);
microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Id", idColumnBase);
+ var owned_DetailsColumnBase = new ColumnBase("Owned_Details", "varchar(64)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Details", owned_DetailsColumnBase);
+ var owned_NumberColumnBase = new ColumnBase("Owned_Number", "int", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase);
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Number", owned_NumberColumnBase);
+ var owned_Principal_AlternateIdColumnBase = new ColumnBase("Owned_Principal_AlternateId", "uniqueidentifier", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase);
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Principal_AlternateId", owned_Principal_AlternateIdColumnBase);
+ var owned_Principal_Enum1ColumnBase = new ColumnBase("Owned_Principal_Enum1", "int", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase);
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Principal_Enum1", owned_Principal_Enum1ColumnBase);
+ var owned_Principal_Enum2ColumnBase = new ColumnBase("Owned_Principal_Enum2", "int", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Principal_Enum2", owned_Principal_Enum2ColumnBase);
+ var owned_Principal_FlagsEnum1ColumnBase = new ColumnBase("Owned_Principal_FlagsEnum1", "int", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase);
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Principal_FlagsEnum1", owned_Principal_FlagsEnum1ColumnBase);
+ var owned_Principal_FlagsEnum2ColumnBase = new ColumnBase("Owned_Principal_FlagsEnum2", "int", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase);
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Principal_FlagsEnum2", owned_Principal_FlagsEnum2ColumnBase);
+ var owned_Principal_IdColumnBase = new ColumnBase("Owned_Principal_Id", "bigint", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Principal_Id", owned_Principal_IdColumnBase);
+ var owned_Principal_RefTypeArrayColumnBase = new ColumnBase("Owned_Principal_RefTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Principal_RefTypeArray", owned_Principal_RefTypeArrayColumnBase);
+ var owned_Principal_RefTypeEnumerableColumnBase = new ColumnBase("Owned_Principal_RefTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Principal_RefTypeEnumerable", owned_Principal_RefTypeEnumerableColumnBase);
+ var owned_Principal_RefTypeIListColumnBase = new ColumnBase("Owned_Principal_RefTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Principal_RefTypeIList", owned_Principal_RefTypeIListColumnBase);
+ var owned_Principal_RefTypeListColumnBase = new ColumnBase("Owned_Principal_RefTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Principal_RefTypeList", owned_Principal_RefTypeListColumnBase);
+ var owned_Principal_ValueTypeArrayColumnBase = new ColumnBase("Owned_Principal_ValueTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Principal_ValueTypeArray", owned_Principal_ValueTypeArrayColumnBase);
+ var owned_Principal_ValueTypeEnumerableColumnBase = new ColumnBase("Owned_Principal_ValueTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Principal_ValueTypeEnumerable", owned_Principal_ValueTypeEnumerableColumnBase);
+ var owned_Principal_ValueTypeIListColumnBase = new ColumnBase("Owned_Principal_ValueTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Principal_ValueTypeIList", owned_Principal_ValueTypeIListColumnBase);
+ var owned_Principal_ValueTypeListColumnBase = new ColumnBase("Owned_Principal_ValueTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_Principal_ValueTypeList", owned_Principal_ValueTypeListColumnBase);
+ var owned_RefTypeArrayColumnBase = new ColumnBase("Owned_RefTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_RefTypeArray", owned_RefTypeArrayColumnBase);
+ var owned_RefTypeEnumerableColumnBase = new ColumnBase("Owned_RefTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_RefTypeEnumerable", owned_RefTypeEnumerableColumnBase);
+ var owned_RefTypeIListColumnBase = new ColumnBase("Owned_RefTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_RefTypeIList", owned_RefTypeIListColumnBase);
+ var owned_RefTypeListColumnBase = new ColumnBase("Owned_RefTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_RefTypeList", owned_RefTypeListColumnBase);
+ var owned_ValueTypeArrayColumnBase = new ColumnBase("Owned_ValueTypeArray", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_ValueTypeArray", owned_ValueTypeArrayColumnBase);
+ var owned_ValueTypeEnumerableColumnBase = new ColumnBase("Owned_ValueTypeEnumerable", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_ValueTypeEnumerable", owned_ValueTypeEnumerableColumnBase);
+ var owned_ValueTypeIListColumnBase = new ColumnBase("Owned_ValueTypeIList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_ValueTypeIList", owned_ValueTypeIListColumnBase);
+ var owned_ValueTypeListColumnBase = new ColumnBase("Owned_ValueTypeList", "nvarchar(max)", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
+ {
+ IsNullable = true
+ };
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.Columns.Add("Owned_ValueTypeList", owned_ValueTypeListColumnBase);
var principalBaseIdColumnBase = new ColumnBase("PrincipalBaseId", "bigint", microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase)
{
IsNullable = true
@@ -35371,6 +35476,22 @@ private IRelationalModel CreateRelationalModel()
var ownedType = principalBase.FindComplexProperty("Owned")!.ComplexType;
+ var defaultTableMappings0 = new List>();
+ ownedType.SetRuntimeAnnotation("Relational:DefaultMappings", defaultTableMappings0);
+ var microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0 = new TableMappingBase(ownedType, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase, false);
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.AddTypeMapping(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0, false);
+ defaultTableMappings0.Add(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_DetailsColumnBase, ownedType.FindProperty("Details")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_NumberColumnBase, ownedType.FindProperty("Number")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_RefTypeArrayColumnBase, ownedType.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_RefTypeEnumerableColumnBase, ownedType.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_RefTypeIListColumnBase, ownedType.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_RefTypeListColumnBase, ownedType.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_ValueTypeArrayColumnBase, ownedType.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_ValueTypeEnumerableColumnBase, ownedType.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_ValueTypeIListColumnBase, ownedType.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_ValueTypeListColumnBase, ownedType.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
+
var tableMappings0 = new List();
ownedType.SetRuntimeAnnotation("Relational:TableMappings", tableMappings0);
var principalBaseTableMapping0 = new TableMapping(ownedType, principalBaseTable, true);
@@ -35389,6 +35510,26 @@ private IRelationalModel CreateRelationalModel()
var principalBase0 = ownedType.FindComplexProperty("Principal")!.ComplexType;
+ var defaultTableMappings1 = new List>();
+ principalBase0.SetRuntimeAnnotation("Relational:DefaultMappings", defaultTableMappings1);
+ var microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1 = new TableMappingBase(principalBase0, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase, false);
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.AddTypeMapping(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1, false);
+ defaultTableMappings1.Add(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_Principal_AlternateIdColumnBase, principalBase0.FindProperty("AlternateId")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_Principal_Enum1ColumnBase, principalBase0.FindProperty("Enum1")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_Principal_Enum2ColumnBase, principalBase0.FindProperty("Enum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_Principal_FlagsEnum1ColumnBase, principalBase0.FindProperty("FlagsEnum1")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_Principal_FlagsEnum2ColumnBase, principalBase0.FindProperty("FlagsEnum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_Principal_IdColumnBase, principalBase0.FindProperty("Id")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_Principal_RefTypeArrayColumnBase, principalBase0.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_Principal_RefTypeEnumerableColumnBase, principalBase0.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_Principal_RefTypeIListColumnBase, principalBase0.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_Principal_RefTypeListColumnBase, principalBase0.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_Principal_ValueTypeArrayColumnBase, principalBase0.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_Principal_ValueTypeEnumerableColumnBase, principalBase0.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_Principal_ValueTypeIListColumnBase, principalBase0.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+ RelationalModel.CreateColumnMapping((ColumnBase)owned_Principal_ValueTypeListColumnBase, principalBase0.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase1);
+
var tableMappings1 = new List();
principalBase0.SetRuntimeAnnotation("Relational:TableMappings", tableMappings1);
var principalBaseTableMapping1 = new TableMapping(principalBase0, principalBaseTable, true);
@@ -35411,26 +35552,26 @@ private IRelationalModel CreateRelationalModel()
var principalDerived = FindEntityType("Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest+PrincipalDerived>")!;
- var defaultTableMappings0 = new List>();
- principalDerived.SetRuntimeAnnotation("Relational:DefaultMappings", defaultTableMappings0);
- var microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0 = new TableMappingBase(principalDerived, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase, true);
- microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.AddTypeMapping(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0, false);
- defaultTableMappings0.Add(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)idColumnBase, principalDerived.FindProperty("Id")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)discriminatorColumnBase, principalDerived.FindProperty("Discriminator")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)enum1ColumnBase, principalDerived.FindProperty("Enum1")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)enum2ColumnBase, principalDerived.FindProperty("Enum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum1ColumnBase, principalDerived.FindProperty("FlagsEnum1")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum2ColumnBase, principalDerived.FindProperty("FlagsEnum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)principalBaseIdColumnBase, principalDerived.FindProperty("PrincipalBaseId")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)refTypeArrayColumnBase, principalDerived.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)refTypeEnumerableColumnBase, principalDerived.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)refTypeIListColumnBase, principalDerived.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)refTypeListColumnBase, principalDerived.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)valueTypeArrayColumnBase, principalDerived.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)valueTypeEnumerableColumnBase, principalDerived.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)valueTypeIListColumnBase, principalDerived.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
- RelationalModel.CreateColumnMapping((ColumnBase)valueTypeListColumnBase, principalDerived.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase0);
+ var defaultTableMappings2 = new List>();
+ principalDerived.SetRuntimeAnnotation("Relational:DefaultMappings", defaultTableMappings2);
+ var microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2 = new TableMappingBase(principalDerived, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase, true);
+ microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseTableBase.AddTypeMapping(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2, false);
+ defaultTableMappings2.Add(microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)idColumnBase, principalDerived.FindProperty("Id")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)discriminatorColumnBase, principalDerived.FindProperty("Discriminator")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)enum1ColumnBase, principalDerived.FindProperty("Enum1")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)enum2ColumnBase, principalDerived.FindProperty("Enum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum1ColumnBase, principalDerived.FindProperty("FlagsEnum1")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)flagsEnum2ColumnBase, principalDerived.FindProperty("FlagsEnum2")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)principalBaseIdColumnBase, principalDerived.FindProperty("PrincipalBaseId")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)refTypeArrayColumnBase, principalDerived.FindProperty("RefTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)refTypeEnumerableColumnBase, principalDerived.FindProperty("RefTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)refTypeIListColumnBase, principalDerived.FindProperty("RefTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)refTypeListColumnBase, principalDerived.FindProperty("RefTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)valueTypeArrayColumnBase, principalDerived.FindProperty("ValueTypeArray")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)valueTypeEnumerableColumnBase, principalDerived.FindProperty("ValueTypeEnumerable")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)valueTypeIListColumnBase, principalDerived.FindProperty("ValueTypeIList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
+ RelationalModel.CreateColumnMapping((ColumnBase)valueTypeListColumnBase, principalDerived.FindProperty("ValueTypeList")!, microsoftEntityFrameworkCoreScaffoldingInternalCSharpRuntimeModelCodeGeneratorTestPrincipalBaseMappingBase2);
var tableMappings2 = new List();
principalDerived.SetRuntimeAnnotation("Relational:TableMappings", tableMappings2);
diff --git a/test/EFCore.InMemory.FunctionalTests/PropertyValuesInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/PropertyValuesInMemoryTest.cs
index 9e06e8afced..dd2d0dd74e7 100644
--- a/test/EFCore.InMemory.FunctionalTests/PropertyValuesInMemoryTest.cs
+++ b/test/EFCore.InMemory.FunctionalTests/PropertyValuesInMemoryTest.cs
@@ -3,19 +3,47 @@
namespace Microsoft.EntityFrameworkCore;
-public class PropertyValuesInMemoryTest : PropertyValuesTestBase
+public class PropertyValuesInMemoryTest(PropertyValuesInMemoryTest.PropertyValuesInMemoryFixture fixture)
+ : PropertyValuesTestBase(fixture)
{
- public PropertyValuesInMemoryTest(PropertyValuesInMemoryFixture fixture)
- : base(fixture)
- {
- }
+ public override Task Complex_current_values_can_be_accessed_as_a_property_dictionary_using_IProperty()
+ => Assert.ThrowsAsync( // In-memory database cannot query complex types
+ () => base.Complex_current_values_can_be_accessed_as_a_property_dictionary_using_IProperty());
+
+ public override Task Complex_original_values_can_be_accessed_as_a_property_dictionary_using_IProperty()
+ => Assert.ThrowsAsync( // In-memory database cannot query complex types
+ () => base.Complex_original_values_can_be_accessed_as_a_property_dictionary_using_IProperty());
+
+ public override Task Complex_store_values_can_be_accessed_as_a_property_dictionary_using_IProperty()
+ => Assert.ThrowsAsync( // In-memory database cannot query complex types
+ () => base.Complex_store_values_can_be_accessed_as_a_property_dictionary_using_IProperty());
+
+ public override Task Complex_store_values_can_be_accessed_asynchronously_as_a_property_dictionary_using_IProperty()
+ => Assert.ThrowsAsync( // In-memory database cannot query complex types
+ () => base.Complex_store_values_can_be_accessed_asynchronously_as_a_property_dictionary_using_IProperty());
public class PropertyValuesInMemoryFixture : PropertyValuesFixtureBase
{
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
- => base.AddOptions(builder).EnableSensitiveDataLogging(false);
+ => base.AddOptions(builder)
+ .ConfigureWarnings(w => w.Ignore(CoreEventId.MappedComplexPropertyIgnoredWarning))
+ .EnableSensitiveDataLogging(false);
protected override ITestStoreFactory TestStoreFactory
=> InMemoryTestStoreFactory.Instance;
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
+ {
+ base.OnModelCreating(modelBuilder, context);
+
+ // In-memory database doesn't support complex type queries
+ modelBuilder.Entity(
+ b =>
+ {
+ b.Ignore(e => e.Culture);
+ b.Ignore(e => e.Milk);
+ });
+
+ }
}
}
diff --git a/test/EFCore.InMemory.FunctionalTests/Query/AdHocAdvancedMappingsQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/AdHocAdvancedMappingsQueryInMemoryTest.cs
new file mode 100644
index 00000000000..bcd988e7562
--- /dev/null
+++ b/test/EFCore.InMemory.FunctionalTests/Query/AdHocAdvancedMappingsQueryInMemoryTest.cs
@@ -0,0 +1,10 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Query;
+
+public class AdHocAdvancedMappingsQueryInMemoryTest : AdHocAdvancedMappingsQueryTestBase
+{
+ protected override ITestStoreFactory TestStoreFactory
+ => InMemoryTestStoreFactory.Instance;
+}
diff --git a/test/EFCore.Relational.Specification.Tests/Migrations/MigrationsInfrastructureTestBase.cs b/test/EFCore.Relational.Specification.Tests/Migrations/MigrationsInfrastructureTestBase.cs
index 0d1a226dcce..db77782f269 100644
--- a/test/EFCore.Relational.Specification.Tests/Migrations/MigrationsInfrastructureTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/Migrations/MigrationsInfrastructureTestBase.cs
@@ -72,7 +72,32 @@ public virtual void Can_apply_all_migrations()
x => Assert.Equal("00000000000001_Migration1", x.MigrationId),
x => Assert.Equal("00000000000002_Migration2", x.MigrationId),
x => Assert.Equal("00000000000003_Migration3", x.MigrationId),
- x => Assert.Equal("00000000000004_Migration4", x.MigrationId));
+ x => Assert.Equal("00000000000004_Migration4", x.MigrationId),
+ x => Assert.Equal("00000000000005_Migration5", x.MigrationId),
+ x => Assert.Equal("00000000000006_Migration6", x.MigrationId),
+ x => Assert.Equal("00000000000007_Migration7", x.MigrationId));
+ }
+
+ [ConditionalFact]
+ public virtual void Can_apply_range_of_migrations()
+ {
+ using var db = Fixture.CreateContext();
+ db.Database.EnsureDeleted();
+
+ GiveMeSomeTime(db);
+
+ var migrator = db.GetService();
+ migrator.Migrate("Migration6");
+
+ var history = db.GetService();
+ Assert.Collection(
+ history.GetAppliedMigrations(),
+ x => Assert.Equal("00000000000001_Migration1", x.MigrationId),
+ x => Assert.Equal("00000000000002_Migration2", x.MigrationId),
+ x => Assert.Equal("00000000000003_Migration3", x.MigrationId),
+ x => Assert.Equal("00000000000004_Migration4", x.MigrationId),
+ x => Assert.Equal("00000000000005_Migration5", x.MigrationId),
+ x => Assert.Equal("00000000000006_Migration6", x.MigrationId));
}
[ConditionalFact]
@@ -100,9 +125,8 @@ public virtual void Can_revert_all_migrations()
GiveMeSomeTime(db);
- db.Database.Migrate();
-
var migrator = db.GetService();
+ migrator.Migrate("Migration5");
migrator.Migrate(Migration.InitialDatabase);
var history = db.GetService();
@@ -117,15 +141,17 @@ public virtual void Can_revert_one_migrations()
GiveMeSomeTime(db);
- db.Database.Migrate();
-
var migrator = db.GetService();
- migrator.Migrate("Migration1");
+ migrator.Migrate("Migration5");
+ migrator.Migrate("Migration4");
var history = db.GetService();
Assert.Collection(
history.GetAppliedMigrations(),
- x => Assert.Equal("00000000000001_Migration1", x.MigrationId));
+ x => Assert.Equal("00000000000001_Migration1", x.MigrationId),
+ x => Assert.Equal("00000000000002_Migration2", x.MigrationId),
+ x => Assert.Equal("00000000000003_Migration3", x.MigrationId),
+ x => Assert.Equal("00000000000004_Migration4", x.MigrationId));
}
[ConditionalFact]
@@ -144,7 +170,10 @@ await history.GetAppliedMigrationsAsync(),
x => Assert.Equal("00000000000001_Migration1", x.MigrationId),
x => Assert.Equal("00000000000002_Migration2", x.MigrationId),
x => Assert.Equal("00000000000003_Migration3", x.MigrationId),
- x => Assert.Equal("00000000000004_Migration4", x.MigrationId));
+ x => Assert.Equal("00000000000004_Migration4", x.MigrationId),
+ x => Assert.Equal("00000000000005_Migration5", x.MigrationId),
+ x => Assert.Equal("00000000000006_Migration6", x.MigrationId),
+ x => Assert.Equal("00000000000007_Migration7", x.MigrationId));
}
[ConditionalFact]
@@ -357,6 +386,7 @@ public MigrationsContext(DbContextOptions options)
public class Foo
{
public int Id { get; set; }
+ public string Description { get; set; }
}
[DbContext(typeof(MigrationsContext))]
@@ -370,7 +400,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
migrationBuilder
.CreateTable(
name: "Table1",
- columns: x => new { Id = x.Column(), Foo = x.Column() })
+ columns: x => new { Id = x.Column(), Foo = x.Column(), Description = x.Column() })
.PrimaryKey(
name: "PK_Table1",
columns: x => x.Id);
@@ -452,4 +482,72 @@ protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
+
+ [DbContext(typeof(MigrationsContext))]
+ [Migration("00000000000005_Migration5")]
+ private class Migration5 : Migration
+ {
+ public const string TestValue = """
+ Value With
+
+ Empty Lines
+ """;
+
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.Sql($"INSERT INTO Table1 (Id, Bar, Description) VALUES (-1, ' ', '{TestValue}')");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+
+ }
+ }
+
+ [DbContext(typeof(MigrationsContext))]
+ [Migration("00000000000006_Migration6")]
+ private class Migration6 : Migration
+ {
+ public const string TestValue = """
+ GO
+ Value With
+
+ Empty Lines
+ """;
+
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.Sql($"INSERT INTO Table1 (Id, Bar, Description) VALUES (-2, ' ', '{TestValue}')");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+
+ }
+ }
+
+ [DbContext(typeof(MigrationsContext))]
+ [Migration("00000000000007_Migration7")]
+ private class Migration7 : Migration
+ {
+ public const string TestValue = """
+ GO
+ Value With
+
+ GO
+
+ Empty Lines
+ GO
+ """;
+
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.Sql($"INSERT INTO Table1 (Id, Bar, Description) VALUES (-3, ' ', '{TestValue}')");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+
+ }
+ }
}
diff --git a/test/EFCore.Relational.Specification.Tests/Migrations/MigrationsTestBase.cs b/test/EFCore.Relational.Specification.Tests/Migrations/MigrationsTestBase.cs
index 8af3b5cbc97..eef96d80c95 100644
--- a/test/EFCore.Relational.Specification.Tests/Migrations/MigrationsTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/Migrations/MigrationsTestBase.cs
@@ -1,6 +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.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
using Microsoft.EntityFrameworkCore.Scaffolding.Metadata;
@@ -1874,6 +1875,339 @@ await Test(
""");
}
+ [ConditionalFact]
+ public virtual Task Add_required_primitve_collection_to_existing_table()
+ => Test(
+ builder => builder.Entity(
+ "Customer", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.ToTable("Customers");
+ }),
+ builder => builder.Entity(
+ "Customer", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.Property>("Numbers").IsRequired();
+ e.ToTable("Customers");
+ }),
+ model =>
+ {
+ var customersTable = Assert.Single(model.Tables.Where(t => t.Name == "Customers"));
+
+ Assert.Collection(
+ customersTable.Columns,
+ c => Assert.Equal("Id", c.Name),
+ c => Assert.Equal("Name", c.Name),
+ c => Assert.Equal("Numbers", c.Name));
+ Assert.Same(
+ customersTable.Columns.Single(c => c.Name == "Id"),
+ Assert.Single(customersTable.PrimaryKey!.Columns));
+ });
+
+ [ConditionalFact]
+ public virtual Task Add_required_primitve_collection_with_custom_default_value_to_existing_table()
+ => Test(
+ builder => builder.Entity(
+ "Customer", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.ToTable("Customers");
+ }),
+ builder => builder.Entity(
+ "Customer", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.Property>("Numbers").IsRequired().HasDefaultValue(new List { 1, 2, 3 });
+ e.ToTable("Customers");
+ }),
+ model =>
+ {
+ var customersTable = Assert.Single(model.Tables.Where(t => t.Name == "Customers"));
+
+ Assert.Collection(
+ customersTable.Columns,
+ c => Assert.Equal("Id", c.Name),
+ c => Assert.Equal("Name", c.Name),
+ c => Assert.Equal("Numbers", c.Name));
+ Assert.Same(
+ customersTable.Columns.Single(c => c.Name == "Id"),
+ Assert.Single(customersTable.PrimaryKey!.Columns));
+ });
+
+ [ConditionalFact]
+ public abstract Task Add_required_primitve_collection_with_custom_default_value_sql_to_existing_table();
+
+ protected virtual Task Add_required_primitve_collection_with_custom_default_value_sql_to_existing_table_core(string defaultValueSql)
+ => Test(
+ builder => builder.Entity(
+ "Customer", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.ToTable("Customers");
+ }),
+ builder => builder.Entity(
+ "Customer", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.Property>("Numbers").IsRequired().HasDefaultValueSql(defaultValueSql);
+ e.ToTable("Customers");
+ }),
+ model =>
+ {
+ var customersTable = Assert.Single(model.Tables.Where(t => t.Name == "Customers"));
+
+ Assert.Collection(
+ customersTable.Columns,
+ c => Assert.Equal("Id", c.Name),
+ c => Assert.Equal("Name", c.Name),
+ c => Assert.Equal("Numbers", c.Name));
+ Assert.Same(
+ customersTable.Columns.Single(c => c.Name == "Id"),
+ Assert.Single(customersTable.PrimaryKey!.Columns));
+ });
+
+ [ConditionalFact(Skip = "issue #33038")]
+ public virtual Task Add_required_primitve_collection_with_custom_converter_to_existing_table()
+ => Test(
+ builder => builder.Entity(
+ "Customer", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.ToTable("Customers");
+ }),
+ builder => builder.Entity(
+ "Customer", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.Property>("Numbers").HasConversion(new ValueConverter, string>(
+ convertToProviderExpression: x => x != null && x.Count > 0 ? "some numbers" : "nothing",
+ convertFromProviderExpression: x => x == "nothing" ? new List { } : new List { 7, 8, 9 }))
+ .IsRequired();
+ e.ToTable("Customers");
+ }),
+ model =>
+ {
+ var customersTable = Assert.Single(model.Tables.Where(t => t.Name == "Customers"));
+
+ Assert.Collection(
+ customersTable.Columns,
+ c => Assert.Equal("Id", c.Name),
+ c => Assert.Equal("Name", c.Name),
+ c => Assert.Equal("Numbers", c.Name));
+ Assert.Same(
+ customersTable.Columns.Single(c => c.Name == "Id"),
+ Assert.Single(customersTable.PrimaryKey!.Columns));
+ });
+
+ [ConditionalFact]
+ public virtual Task Add_required_primitve_collection_with_custom_converter_and_custom_default_value_to_existing_table()
+ => Test(
+ builder => builder.Entity(
+ "Customer", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.ToTable("Customers");
+ }),
+ builder => builder.Entity(
+ "Customer", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.Property>("Numbers").HasConversion(new ValueConverter, string>(
+ convertToProviderExpression: x => x != null && x.Count > 0 ? "some numbers" : "nothing",
+ convertFromProviderExpression: x => x == "nothing" ? new List { } : new List { 7, 8, 9 }))
+ .HasDefaultValue(new List { 42 })
+ .IsRequired();
+ e.ToTable("Customers");
+ }),
+ model =>
+ {
+ var customersTable = Assert.Single(model.Tables.Where(t => t.Name == "Customers"));
+
+ Assert.Collection(
+ customersTable.Columns,
+ c => Assert.Equal("Id", c.Name),
+ c => Assert.Equal("Name", c.Name),
+ c => Assert.Equal("Numbers", c.Name));
+ Assert.Same(
+ customersTable.Columns.Single(c => c.Name == "Id"),
+ Assert.Single(customersTable.PrimaryKey!.Columns));
+ });
+
+ [ConditionalFact]
+ public virtual Task Add_optional_primitive_collection_to_existing_table()
+ => Test(
+ builder => builder.Entity(
+ "Customer", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.ToTable("Customers");
+ }),
+ builder => builder.Entity(
+ "Customer", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.Property>("Numbers");
+ e.ToTable("Customers");
+ }),
+ model =>
+ {
+ var customersTable = Assert.Single(model.Tables.Where(t => t.Name == "Customers"));
+
+ Assert.Collection(
+ customersTable.Columns,
+ c => Assert.Equal("Id", c.Name),
+ c => Assert.Equal("Name", c.Name),
+ c => Assert.Equal("Numbers", c.Name));
+ Assert.Same(
+ customersTable.Columns.Single(c => c.Name == "Id"),
+ Assert.Single(customersTable.PrimaryKey!.Columns));
+ });
+
+ [ConditionalFact]
+ public virtual Task Create_table_with_required_primitive_collection()
+ => Test(
+ builder => { },
+ builder => builder.Entity(
+ "Customer", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.Property>("Numbers").IsRequired();
+ e.ToTable("Customers");
+ }),
+ model =>
+ {
+ var customersTable = Assert.Single(model.Tables.Where(t => t.Name == "Customers"));
+
+ Assert.Collection(
+ customersTable.Columns,
+ c => Assert.Equal("Id", c.Name),
+ c => Assert.Equal("Name", c.Name),
+ c => Assert.Equal("Numbers", c.Name));
+ Assert.Same(
+ customersTable.Columns.Single(c => c.Name == "Id"),
+ Assert.Single(customersTable.PrimaryKey!.Columns));
+ });
+
+ [ConditionalFact]
+ public virtual Task Create_table_with_optional_primitive_collection()
+ => Test(
+ builder => { },
+ builder => builder.Entity(
+ "Customer", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.Property>("Numbers");
+ e.ToTable("Customers");
+ }),
+ model =>
+ {
+ var customersTable = Assert.Single(model.Tables.Where(t => t.Name == "Customers"));
+
+ Assert.Collection(
+ customersTable.Columns,
+ c => Assert.Equal("Id", c.Name),
+ c => Assert.Equal("Name", c.Name),
+ c => Assert.Equal("Numbers", c.Name));
+ Assert.Same(
+ customersTable.Columns.Single(c => c.Name == "Id"),
+ Assert.Single(customersTable.PrimaryKey!.Columns));
+ });
+
+ [ConditionalFact]
+ public virtual Task Create_table_with_complex_type_with_required_properties_on_derived_entity_in_TPH()
+ => Test(
+ builder => { },
+ builder =>
+ {
+ builder.Entity(
+ "Contact", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.HasKey("Id");
+ e.Property("Name");
+ e.ToTable("Contacts");
+ });
+ builder.Entity(
+ "Supplier", e =>
+ {
+ e.HasBaseType("Contact");
+ e.Property("Number");
+ e.ComplexProperty("MyComplex", ct =>
+ {
+ ct.ComplexProperty("MyNestedComplex").IsRequired();
+ });
+ });
+ },
+ model =>
+ {
+ var contactsTable = Assert.Single(model.Tables.Where(t => t.Name == "Contacts"));
+ Assert.Collection(
+ contactsTable.Columns,
+ c => Assert.Equal("Id", c.Name),
+ c => Assert.Equal("Discriminator", c.Name),
+ c => Assert.Equal("Name", c.Name),
+ c => Assert.Equal("Number", c.Name),
+ c =>
+ {
+ Assert.Equal("MyComplex_Prop", c.Name);
+ Assert.Equal(true, c.IsNullable);
+ },
+ c =>
+ {
+ Assert.Equal("MyComplex_MyNestedComplex_Bar", c.Name);
+ Assert.Equal(true, c.IsNullable);
+ },
+ c =>
+ {
+ Assert.Equal("MyComplex_MyNestedComplex_Foo", c.Name);
+ Assert.Equal(true, c.IsNullable);
+ });
+ });
+
+ protected class MyComplex
+ {
+ [Required]
+ public string Prop { get; set; }
+
+ [Required]
+ public MyNestedComplex Nested { get; set; }
+ }
+
+ public class MyNestedComplex
+ {
+ public int Foo { get; set; }
+ public DateTime Bar { get; set; }
+ }
+
protected class Person
{
public int Id { get; set; }
diff --git a/test/EFCore.Relational.Specification.Tests/PACKAGE.md b/test/EFCore.Relational.Specification.Tests/PACKAGE.md
new file mode 100644
index 00000000000..fa068cd075b
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/PACKAGE.md
@@ -0,0 +1,3 @@
+This package contains tests that can be implemented and then used in database provider repos to test EF Core against that provider.
+
+See [Writing a Database Provider](https://learn.microsoft.com/ef/core/providers/writing-a-provider) for more information.
diff --git a/test/EFCore.Relational.Specification.Tests/Query/AdHocAdvancedMappingsQueryRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/AdHocAdvancedMappingsQueryRelationalTestBase.cs
new file mode 100644
index 00000000000..d001b5488f2
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/Query/AdHocAdvancedMappingsQueryRelationalTestBase.cs
@@ -0,0 +1,230 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.EntityFrameworkCore.Query;
+
+public abstract class AdHocAdvancedMappingsQueryRelationalTestBase : AdHocAdvancedMappingsQueryTestBase
+{
+ protected TestSqlLoggerFactory TestSqlLoggerFactory
+ => (TestSqlLoggerFactory)ListLoggerFactory;
+
+ protected void ClearLog()
+ => TestSqlLoggerFactory.Clear();
+
+ protected void AssertSql(params string[] expected)
+ => TestSqlLoggerFactory.AssertBaseline(expected);
+
+ #region 32911
+
+ [ConditionalFact]
+ public virtual async Task Two_similar_complex_properties_projected_with_split_query1()
+ {
+ var contextFactory = await InitializeAsync(seed: c => c.Seed());
+
+ using var context = contextFactory.CreateContext();
+ var query = context.Offers
+ .Include(e => e.Variations)
+ .ThenInclude(v => v.Nested)
+ .AsSplitQuery()
+ .ToList();
+
+ var resultElement = query.Single();
+ foreach (var variation in resultElement.Variations)
+ {
+ Assert.NotEqual(variation.Payment.Brutto, variation.Nested.Payment.Brutto);
+ Assert.NotEqual(variation.Payment.Netto, variation.Nested.Payment.Netto);
+ }
+ }
+
+ [ConditionalFact]
+ public virtual async Task Two_similar_complex_properties_projected_with_split_query2()
+ {
+ var contextFactory = await InitializeAsync(seed: c => c.Seed());
+
+ using var context = contextFactory.CreateContext();
+ var query = context.Offers
+ .Include(e => e.Variations)
+ .ThenInclude(v => v.Nested)
+ .AsSplitQuery()
+ .Single(x => x.Id == 1);
+
+ foreach (var variation in query.Variations)
+ {
+ Assert.NotEqual(variation.Payment.Brutto, variation.Nested.Payment.Brutto);
+ Assert.NotEqual(variation.Payment.Netto, variation.Nested.Payment.Netto);
+ }
+ }
+
+ [ConditionalFact]
+ public virtual async Task Projecting_one_of_two_similar_complex_types_picks_the_correct_one()
+ {
+ var contextFactory = await InitializeAsync(seed: c => c.Seed());
+
+ using var context = contextFactory.CreateContext();
+
+ var query = context.Cs
+ .Where(x => x.B.AId.Value == 1)
+ .OrderBy(x => x.Id)
+ .Take(10)
+ .Select(x => new
+ {
+ x.B.A.Id,
+ x.B.Info.Created,
+ }).ToList();
+
+ Assert.Equal(new DateTime(2000, 1, 1), query[0].Created);
+ }
+
+ protected class Context32911(DbContextOptions options) : DbContext(options)
+ {
+ public DbSet Offers { get; set; }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever();
+ modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever();
+ modelBuilder.Entity().ComplexProperty(x => x.Payment, cpb =>
+ {
+ cpb.IsRequired();
+ cpb.Property(p => p.Netto).HasColumnName("payment_netto");
+ cpb.Property(p => p.Brutto).HasColumnName("payment_brutto");
+ });
+ modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever();
+ modelBuilder.Entity().ComplexProperty(x => x.Payment, cpb =>
+ {
+ cpb.IsRequired();
+ cpb.Property(p => p.Netto).HasColumnName("payment_netto");
+ cpb.Property(p => p.Brutto).HasColumnName("payment_brutto");
+ });
+ }
+
+ public void Seed()
+ {
+ var v1 = new Variation
+ {
+ Id = 1,
+ Payment = new Payment(1, 10),
+ Nested = new NestedEntity
+ {
+ Id = 1,
+ Payment = new Payment(10, 100)
+ }
+ };
+
+ var v2 = new Variation
+ {
+ Id = 2,
+ Payment = new Payment(2, 20),
+ Nested = new NestedEntity
+ {
+ Id = 2,
+ Payment = new Payment(20, 200)
+ }
+ };
+
+ var v3 = new Variation
+ {
+ Id = 3,
+ Payment = new Payment(3, 30),
+ Nested = new NestedEntity
+ {
+ Id = 3,
+ Payment = new Payment(30, 300)
+ }
+ };
+
+ Offers.Add(new Offer { Id = 1, Variations = new List { v1, v2, v3 } });
+
+ SaveChanges();
+ }
+
+ public abstract class EntityBase
+ {
+ public int Id { get; set; }
+ }
+
+ public class Offer : EntityBase
+ {
+ public ICollection Variations { get; set; }
+ }
+
+ public class Variation : EntityBase
+ {
+ public Payment Payment { get; set; } = new Payment(0, 0);
+
+ public NestedEntity Nested { get; set; }
+ }
+
+ public class NestedEntity : EntityBase
+ {
+ public Payment Payment { get; set; } = new Payment(0, 0);
+ }
+
+ public record Payment(decimal Netto, decimal Brutto);
+ }
+
+ protected class Context32911_2(DbContextOptions options) : DbContext(options)
+ {
+ public DbSet As { get; set; }
+ public DbSet Bs { get; set; }
+ public DbSet Cs { get; set; }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever();
+ modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever();
+ modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever();
+
+ modelBuilder.Entity(x => x.ComplexProperty(b => b.Info).IsRequired());
+ modelBuilder.Entity(x => x.ComplexProperty(c => c.Info).IsRequired());
+ }
+
+ public void Seed()
+ {
+ var c = new C
+ {
+ Id = 100,
+ Info = new Metadata { Created = new DateTime(2020, 10, 10) },
+ B = new B
+ {
+ Id = 10,
+ Info = new Metadata { Created = new DateTime(2000, 1, 1) },
+ A = new A { Id = 1 }
+ }
+ };
+
+ Cs.Add(c);
+ SaveChanges();
+ }
+
+ public class Metadata
+ {
+ public DateTime Created { get; set; }
+ }
+
+ public class A
+ {
+ public int Id { get; set; }
+ }
+
+ public class B
+ {
+ public int Id { get; set; }
+ public Metadata Info { get; set; }
+ public int? AId { get; set; }
+
+ public A A { get; set; }
+ }
+
+ public class C
+ {
+ public int Id { get; set; }
+ public Metadata Info { get; set; }
+ public int BId { get; set; }
+
+ public B B { get; set; }
+ }
+ }
+
+ #endregion
+}
diff --git a/test/EFCore.Specification.Tests/PACKAGE.md b/test/EFCore.Specification.Tests/PACKAGE.md
new file mode 100644
index 00000000000..fa068cd075b
--- /dev/null
+++ b/test/EFCore.Specification.Tests/PACKAGE.md
@@ -0,0 +1,3 @@
+This package contains tests that can be implemented and then used in database provider repos to test EF Core against that provider.
+
+See [Writing a Database Provider](https://learn.microsoft.com/ef/core/providers/writing-a-provider) for more information.
diff --git a/test/EFCore.Specification.Tests/PropertyValuesTestBase.cs b/test/EFCore.Specification.Tests/PropertyValuesTestBase.cs
index 078d9371725..13a78faf380 100644
--- a/test/EFCore.Specification.Tests/PropertyValuesTestBase.cs
+++ b/test/EFCore.Specification.Tests/PropertyValuesTestBase.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.
+#nullable enable
+
using System.ComponentModel.DataAnnotations.Schema;
using System.Globalization;
@@ -28,11 +30,11 @@ public virtual Task Scalar_original_values_can_be_accessed_as_a_property_diction
[ConditionalFact]
public virtual Task Scalar_store_values_can_be_accessed_as_a_property_dictionary()
- => TestPropertyValuesScalars(e => Task.FromResult(e.GetDatabaseValues()), expectOriginalValues: true);
+ => TestPropertyValuesScalars(e => Task.FromResult(e.GetDatabaseValues()!), expectOriginalValues: true);
[ConditionalFact]
public virtual Task Scalar_store_values_can_be_accessed_asynchronously_as_a_property_dictionary()
- => TestPropertyValuesScalars(e => e.GetDatabaseValuesAsync(), expectOriginalValues: true);
+ => TestPropertyValuesScalars(e => e.GetDatabaseValuesAsync()!, expectOriginalValues: true);
private async Task TestPropertyValuesScalars(
Func, Task> getPropertyValues,
@@ -78,11 +80,11 @@ public virtual Task Scalar_original_values_can_be_accessed_as_a_property_diction
[ConditionalFact]
public virtual Task Scalar_store_values_can_be_accessed_as_a_property_dictionary_using_IProperty()
- => TestPropertyValuesScalarsIProperty(e => Task.FromResult(e.GetDatabaseValues()), expectOriginalValues: true);
+ => TestPropertyValuesScalarsIProperty(e => Task.FromResult(e.GetDatabaseValues()!), expectOriginalValues: true);
[ConditionalFact]
public virtual Task Scalar_store_values_can_be_accessed_asynchronously_as_a_property_dictionary_using_IProperty()
- => TestPropertyValuesScalarsIProperty(e => e.GetDatabaseValuesAsync(), expectOriginalValues: true);
+ => TestPropertyValuesScalarsIProperty(e => e.GetDatabaseValuesAsync()!, expectOriginalValues: true);
private async Task TestPropertyValuesScalarsIProperty(
Func, Task> getPropertyValues,
@@ -129,11 +131,11 @@ public virtual Task Scalar_original_values_of_a_derived_object_can_be_accessed_a
[ConditionalFact]
public virtual Task Scalar_store_values_of_a_derived_object_can_be_accessed_as_a_property_dictionary()
- => TestPropertyValuesDerivedScalars(e => Task.FromResult(e.GetDatabaseValues()), expectOriginalValues: true);
+ => TestPropertyValuesDerivedScalars(e => Task.FromResult(e.GetDatabaseValues()!), expectOriginalValues: true);
[ConditionalFact]
public virtual Task Scalar_store_values_of_a_derived_object_can_be_accessed_asynchronously_as_a_property_dictionary()
- => TestPropertyValuesDerivedScalars(e => e.GetDatabaseValuesAsync(), expectOriginalValues: true);
+ => TestPropertyValuesDerivedScalars(e => e.GetDatabaseValuesAsync()!, expectOriginalValues: true);
private async Task TestPropertyValuesDerivedScalars(
Func, Task> getPropertyValues,
@@ -182,11 +184,11 @@ public virtual Task Scalar_original_values_can_be_accessed_as_a_non_generic_prop
[ConditionalFact]
public virtual Task Scalar_store_values_can_be_accessed_as_a_non_generic_property_dictionary()
- => TestNonGenericPropertyValuesScalars(e => Task.FromResult(e.GetDatabaseValues()), expectOriginalValues: true);
+ => TestNonGenericPropertyValuesScalars(e => Task.FromResult(e.GetDatabaseValues()!), expectOriginalValues: true);
[ConditionalFact]
public virtual Task Scalar_store_values_can_be_accessed_asynchronously_as_a_non_generic_property_dictionary()
- => TestNonGenericPropertyValuesScalars(e => e.GetDatabaseValuesAsync(), expectOriginalValues: true);
+ => TestNonGenericPropertyValuesScalars(e => e.GetDatabaseValuesAsync()!, expectOriginalValues: true);
private async Task TestNonGenericPropertyValuesScalars(
Func> getPropertyValues,
@@ -238,11 +240,11 @@ public virtual Task Scalar_original_values_can_be_accessed_as_a_non_generic_prop
[ConditionalFact]
public virtual Task Scalar_store_values_can_be_accessed_as_a_non_generic_property_dictionary_using_IProperty()
- => TestNonGenericPropertyValuesScalarsIProperty(e => Task.FromResult(e.GetDatabaseValues()), expectOriginalValues: true);
+ => TestNonGenericPropertyValuesScalarsIProperty(e => Task.FromResult(e.GetDatabaseValues()!), expectOriginalValues: true);
[ConditionalFact]
public virtual Task Scalar_store_values_can_be_accessed_asynchronously_as_a_non_generic_property_dictionary_using_IProperty()
- => TestNonGenericPropertyValuesScalarsIProperty(e => e.GetDatabaseValuesAsync(), expectOriginalValues: true);
+ => TestNonGenericPropertyValuesScalarsIProperty(e => e.GetDatabaseValuesAsync()!, expectOriginalValues: true);
private async Task TestNonGenericPropertyValuesScalarsIProperty(
Func> getPropertyValues,
@@ -291,11 +293,11 @@ public virtual Task Scalar_original_values_of_a_derived_object_can_be_accessed_a
[ConditionalFact]
public virtual Task Scalar_store_values_of_a_derived_object_can_be_accessed_as_a_non_generic_property_dictionary()
- => TestNonGenericPropertyValuesDerivedScalars(e => Task.FromResult(e.GetDatabaseValues()), expectOriginalValues: true);
+ => TestNonGenericPropertyValuesDerivedScalars(e => Task.FromResult(e.GetDatabaseValues()!), expectOriginalValues: true);
[ConditionalFact]
public virtual Task Scalar_store_values_of_a_derived_object_can_be_accessed_asynchronously_as_a_non_generic_property_dictionary()
- => TestNonGenericPropertyValuesDerivedScalars(e => e.GetDatabaseValuesAsync(), expectOriginalValues: true);
+ => TestNonGenericPropertyValuesDerivedScalars(e => e.GetDatabaseValuesAsync()!, expectOriginalValues: true);
private async Task TestNonGenericPropertyValuesDerivedScalars(
Func