dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.79k stars 3.19k forks source link

Multiple Provider Migrations: Adding Cosmos Provider causes SQL Server migrations to fail #21401

Closed brickgw closed 3 years ago

brickgw commented 4 years ago

I have a project that uses two data providers with two different DbContexts. One for SQL Server, the other for Cosmos. The Cosmos implementation was added after the Sql Server implementation. Prior to adding Cosmos I was able to run code first migrations via the package manager console as expected. However, after adding a reference to Microsoft.EntityFrameworkCore.Cosmos (3.1.5) and implementing the models, DbContext and configuration I'm unable to run a migration for my SqlServer provider even when using the -Context flag in the Package Manager Console. A System.ArgumentNullException occurs after the build succeeds.

I'm aware that migrations are not supported for Cosmos but my expectation is that if I'm using the -Context flag to specify which context to use that the Cosmos provider would not be attempting to get resolved.

Steps to reproduce

Solution is comprised of the following projects: MyProject.Web - Startup Project MyProject.Data - Persistence Project

In a .NET Core 3.1 WebAPI Solution that has been using the SqlServer provider and code first migrations successfully add a reference to Microsoft.EntityFrameworkCore.Cosmos.

Create a new DbContext for the Cosmos data source in the same project where the SqlServer DbContext exists. Configure services in Startup to add the CosmosDbContext using the Cosmos provider.

Make a change to a SqlServer model that requires a migration.

Using Package Manager Console execute the command: Add-Migration 'update_some_model' -Context SqlServerDbContext

A System.ArgumentNullException will occur indicating that accountEndpoint argument was null when executing the DbContextOptionsBuilder configuration for the CosmosDbContext.

CosmosDbContext

public class GlobalAssessmentCosmosDbContext : DbContext
    {
        public DbSet<AssessmentDataModel> Assessments { get; set; }

        public GlobalAssessmentCosmosDbContext(DbContextOptions<GlobalAssessmentCosmosDbContext> options) : base(options)
        {

        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {

        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // Model Configuration ...          
        }
    }

Startup.Cs Service Configuration

public void ConfigureServices(IServiceCollection services)
        {
            services.AddOptions();

            services.Configure<AzureStorageConfig>(Configuration.GetSection("AzureStorageConfig"));

            services.SwaggerConfiguration();

            services.PersistenceConfiguration(Configuration);

            services.CorsConfiguration();

            services.AuthenticationConfiguration(Configuration);

            services.ApplicationConfiguration();
        }

PersistenceConfiguration Extension Method

public static IServiceCollection PersistenceConfiguration(this IServiceCollection services, IConfiguration config)
        {
            services.AddDbContext<GlobalAssessmentDbContext>(options =>
            {
                options.UseSqlServer(config.GetConnectionString("connectionString"));
                options.EnableSensitiveDataLogging();
            });

            services.AddDbContext<GlobalAssessmentCosmosDbContext>(options => options.UseCosmos(
                config["CosmosEndpoint"],
                config["CosmosKey"],
                config["CosmosDatabaseId"]));

            services.AddScoped<IRepository, Repository<GlobalAssessmentDbContext>>();
            services.AddScoped<IReadOnlyRepository, ReadOnlyRepository<GlobalAssessmentDbContext>>();
            services.AddScoped<IODataRepository, ODataRepository>();
            services.AddScoped<IDocumentRepository, DocumentRepository<GlobalAssessmentCosmosDbContext>>();
            services.AddSingleton<IBlobStorage>(serviceProvider =>
            {
                var blobStorage = new BlobStorage(serviceProvider.GetService<IOptions<AzureStorageConfig>>());
                blobStorage.Initialize().GetAwaiter().GetResult();
                return blobStorage;
            });

            return services;
        }

Got Exceptions? Include both the message and the stack trace

add-migration 'initial' -Context GlobalAssessmentDbContext -Verbose Using project 'Infastructure\GlobalAssessment.Data'. Using startup project 'Presentation\GlobalAssessment.Web'. Build started... Build succeeded. C:\Program Files\dotnet\dotnet.exe exec --depsfile C:\Users\gbricka\source\repos\GlobalAssessment\GlobalAssessment.Web\bin\Debug\netcoreapp3.1\GlobalAssessment.Web.deps.json --additionalprobingpath C:\Users\gbricka.nuget\packages --additionalprobingpath C:\Microsoft\Xamarin\NuGet --additionalprobingpath "C:\Program Files\dotnet\sdk\NuGetFallbackFolder" --runtimeconfig C:\Users\gbricka\source\repos\GlobalAssessment\GlobalAssessment.Web\bin\Debug\netcoreapp3.1\GlobalAssessment.Web.runtimeconfig.json C:\Users\gbricka.nuget\packages\microsoft.entityframeworkcore.tools\3.1.5\tools\netcoreapp2.0\any\ef.dll migrations add initial --json --context GlobalAssessmentDbContext --verbose --no-color --prefix-output --assembly C:\Users\gbricka\source\repos\GlobalAssessment\GlobalAssessment.Web\bin\Debug\netcoreapp3.1\GlobalAssessment.Data.dll --startup-assembly C:\Users\gbricka\source\repos\GlobalAssessment\GlobalAssessment.Web\bin\Debug\netcoreapp3.1\GlobalAssessment.Web.dll --project-dir C:\Users\gbricka\source\repos\GlobalAssessment\GlobalAssessment.Data\ --language C# --working-dir C:\Users\gbricka\source\repos\GlobalAssessment --root-namespace GlobalAssessment.Data Using assembly 'GlobalAssessment.Data'. Using startup assembly 'GlobalAssessment.Web'. Using application base 'C:\Users\gbricka\source\repos\GlobalAssessment\GlobalAssessment.Web\bin\Debug\netcoreapp3.1'. Using working directory 'C:\Users\gbricka\source\repos\GlobalAssessment\GlobalAssessment.Web'. Using root namespace 'GlobalAssessment.Data'. Using project directory 'C:\Users\gbricka\source\repos\GlobalAssessment\GlobalAssessment.Data\'. Finding DbContext classes... Finding IDesignTimeDbContextFactory implementations... Finding application service provider... Finding Microsoft.Extensions.Hosting service provider... Using environment 'Development'. Using application service provider from Microsoft.Extensions.Hosting. System.ArgumentNullException: Value cannot be null. (Parameter 'accountEndpoint') at Microsoft.EntityFrameworkCore.Utilities.Check.NotNull[T](T value, String parameterName) at Microsoft.EntityFrameworkCore.CosmosDbContextOptionsExtensions.UseCosmos(DbContextOptionsBuilder optionsBuilder, String accountEndpoint, String accountKey, String databaseName, Action1 cosmosOptionsAction) at GlobalAssessment.Web.StartupExtensions.DependencyInjectionExtensions.<>c__DisplayClass0_0.<PersistenceConfiguration>b__1(DbContextOptionsBuilder options) in C:\Users\gbricka\source\repos\GlobalAssessment\GlobalAssessment.Web\StartupExtensions\DependencyInjectionExtensions.cs:line 23 at Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.<>c__DisplayClass1_02.b0(IServiceProvider p, DbContextOptionsBuilder b) at Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.CreateDbContextOptions[TContext](IServiceProvider applicationServiceProvider, Action`2 optionsAction) at Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.<>cDisplayClass10_01.<AddCoreServices>b__0(IServiceProvider p) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider) at Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.<>c__101.b10_1(IServiceProvider p) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSite(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.b0(ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetServices[T](IServiceProvider provider) at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.FindContextTypes() at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.FindContextType(String name) at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType) at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType) at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType) at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>cDisplayClass0_0.<.ctor>b0() at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>cDisplayClass3_0`1.b0() at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action) Value cannot be null. (Parameter 'accountEndpoint') PM> add-migration 'initial' -Context GlobalAssessmentDbContext -Verbose

Further technical details

EF Core version: 3.1.5 Database provider: Microsoft.EntityFrameworkCore.SqlServer, Microsoft.EntityFrameworkCore.Cosmos Target framework: .NET Core 3.1 Operating system: Windows 10 Enterprise IDE: Visual Studio 2019 16.6.2

brickgw commented 4 years ago

It appears as though the appSettings.json is being used for migrations instead of appSettings.Local.json. I was able to get a migration to run by adding the cosmos configuration settings into appSettings.json as well as appSettings.Local.json. Is there a configuration setting that needs to be used to indicate which appSettings to use for migrations? When running the application I've not had connection issues with Cosmos so that leads me to believe that the debugger and migration tool resolve the settings differently.

brickgw commented 4 years ago

I see the issue has been tagged for 6.0.0. Is this something I can work on myself or would it be best to hold off to better align with future features/fixes?

bricelam commented 4 years ago

We made some improvements in this area for EF Core 5.0, can you verify that it's still an issue?

ajcvickers commented 3 years ago

Verified that this is now fixed.