entity-frameworkazure-devopsentity-framework-migrations

EF Migration bundle not packaging migrations


I'm sure I'll figure out what is wrong after posting this but here goes.

I'm trying to get an EF Migration bundle going in my devops pipeline so I can deploy updates/changes to my db with my CD pipeline.

Below is my yaml file. I've omitted variables as some are sensitive

stages:
- stage: Build
  displayName: Build stage
  jobs:
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)
    steps:
    - task: UseDotNet@2
      inputs:
        packageType: 'sdk'
        version: '8.x'
    - task: DotNetCoreCLI@2
      displayName: 'Install Dotnet EF Tool'
      inputs:
        command: custom
        custom: "tool"
        arguments: "install --global dotnet-ef"
    - task: DotNetCoreCLI@2
      displayName: EFBundle
      inputs:
        command: 'custom'
        custom: 'ef '
        arguments: 'migrations bundle --configuration Release --self-contained -r win-x64 -p "$(System.DefaultWorkingDirectory)\EntityFramework\EntityFramework.csproj" -o $(Build.ArtifactStagingDirectory)\SQL\bundle.exe --force'
    - task: PublishBuildArtifacts@1
      displayName: 'PublishArtifacts - SQL Bundle'
      inputs:
        pathToPublish: '$(Build.ArtifactStagingDirectory)/SQL'
        artifactName: 'SQL'
    - task: DotNetCoreCLI@2
      displayName: Restore
      inputs:
        command: 'restore'
        projects: |
          $(workingDirectory)\*.csproj
    - task: DotNetCoreCLI@2
      displayName: Build
      inputs:
        command: 'build'
        projects: |
          $(workingDirectory)\*.csproj
        arguments: '--output $(System.DefaultWorkingDirectory)\publish_output --configuration Release'
    - task: ArchiveFiles@2
      displayName: 'Archive files'
      inputs:
        rootFolderOrFile: '$(System.DefaultWorkingDirectory)\publish_output'
        includeRootFolder: false
        archiveType: zip
        archiveFile: $(Build.ArtifactStagingDirectory)\$(Build.BuildId).zip
        replaceExistingArchive: true
    - publish: $(Build.ArtifactStagingDirectory)\$(Build.BuildId).zip
      artifact: drop

- stage: DeployToDevelopment
  displayName: Deploy to Development Slot
  dependsOn: Build
  condition: succeeded()
  jobs:
  - deployment: DeployDevelopment
    displayName: Deploy to Development Environment
    environment: 'development'
    pool:
      vmImage: $(vmImageName)
    strategy:
      runOnce:
        deploy:
          steps:
          - task: AzureCLI@2
            displayName: 'Azure SQL Schema Deployment'
            env:
              MonoConnectionString: $(devConnectionString)
            inputs:
              scriptType: 'pscore'
              scriptLocation: 'inlineScript'
              azureSubscription: '$(azureSubscription)'
              inlineScript: |
                cd $(Pipeline.Workspace)/SQL
                ./bundle.exe --connection "$(devConnectionString)" --verbose
          - task: AzureFunctionApp@1
            displayName: 'Deploy to Development App'
            inputs:
              azureSubscription: '$(azureSubscription)'
              appType: 'functionApp'
              appName: $(devFunctionAppName)
              package: '$(Pipeline.Workspace)\drop\$(Build.BuildId).zip'

- stage: DeployToProduction
  displayName: Deploy to Production Slot
  dependsOn: DeployToDevelopment
  condition: succeeded()
  jobs:
  - deployment: DeployProduction
    displayName: Deploy to Production Slot
    environment: 'production'  # Make sure this environment has manual approvals configured in Azure DevOps
    pool:
      vmImage: $(vmImageName)
    strategy:
      runOnce:
        deploy:
          steps:
          - task: AzureCLI@2
            displayName: 'Run EF Migrations Bundle for Production'
            env:
              MonoConnectionString: $(connectionString)
            inputs:
              scriptType: 'pscore'
              scriptLocation: 'inlineScript'
              azureSubscription: '$(azureSubscription)'
              inlineScript: |
                cd $(Pipeline.Workspace)/SQL
                ./bundle.exe AddedEmailToInvitationTable --verbose
          - task: AzureFunctionApp@1
            displayName: 'Deploy to Production Slot'
            inputs:
              azureSubscription: '$(azureSubscription)'
              appType: 'functionApp'
              appName: $(functionAppName)
              package: '$(Pipeline.Workspace)\drop\$(Build.BuildId).zip'

As you can see I'm running the typical dotnet ef migrations bundle command with the --force flag so it overwrites any bundle already present (which there shouldn't be anyway). My EF migrations are contained inside a project called EntityFramework. Here is a screen shot of that in my IDE.

Migrations in the left and db context setup on the right
As you can see, I've already applied migrations and have deployed them to my dev db via my IDE by simply calling database update (that works as expected).

The problem I face is once my production deploy pipeline runs, I always get a message saying the database is already up to date. I thought at first perhaps my prod stage was looking at my dev db, but that's not the case as you can clearly see in this output that it created an __EFMigrations table


      Executed DbCommand (36ms) [Parameters=[], CommandType='Text', CommandTimeout='1800']
      SELECT OBJECT_ID(N'[__EFMigrationsHistory]');
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (21ms) [Parameters=[], CommandType='Text', CommandTimeout='1800']
      SELECT 1
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (27ms) [Parameters=[], CommandType='Text', CommandTimeout='1800']
      CREATE TABLE [__EFMigrationsHistory] (
          [MigrationId] nvarchar(150) NOT NULL,
          [ProductVersion] nvarchar(32) NOT NULL,
          CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId])
      );
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (20ms) [Parameters=[], CommandType='Text', CommandTimeout='1800']
      SELECT 1
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (22ms) [Parameters=[], CommandType='Text', CommandTimeout='1800']
      SELECT OBJECT_ID(N'[__EFMigrationsHistory]');
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (29ms) [Parameters=[], CommandType='Text', CommandTimeout='1800']
      SELECT [MigrationId], [ProductVersion]
      FROM [__EFMigrationsHistory]
      ORDER BY [MigrationId];
info: Microsoft.EntityFrameworkCore.Migrations[20405]
      No migrations were applied. The database is already up to date.

So to troubleshoot the issue I added a specific migration to my ./bundle.exe command. If you look at my yaml above you will see it called AddedEmailToInvitationTable. When I run that I get an error saying that there is no migration with that name; and hence I've come to the conclusion that for some reason, my dotnet ef migrations bundle command is some how not seeing my actual migrations`

System.InvalidOperationException: The migration 'AddedEmailToInvitationTable' was not found.

How can I make sure the bundle command explicitly incorporates my migrations folder?OR perhaps, is there anything obvious I am doing wrong?


Solution

  • Yup, as I mentioned in my question, I would figure out the answer once I posted. It's a strange one to say the least.

    Adding .MigrationsAssembly("EntityFramework") to my AddDbContext call seemed to magically tell the EF Migrations where to find my migrations even though the migration assembly is the same exact assembly I was running the commands on hehe.

    Here is the total code

    builder.Services.AddDbContext<MonoContext>(a =>
        a.UseSqlServer(dbConn, opts =>
                opts.CommandTimeout((int)TimeSpan.FromMinutes(30).TotalSeconds)
                    .MigrationsAssembly("EntityFramework"))
            .EnableDetailedErrors()
            .EnableSensitiveDataLogging());
    

    I figured it out by trying to run dotnet ef migrations list locally on my project. No matter what I did it would not list any migrations. So I then google searched "no migrations found ef core" and found other people who had a similar issue outside of the context of ef bundles.