Pomelo.entityframeworkcore.mysql: An item with the same key has already been added. Key: Microsoft.EntityFrameworkCore.Infrastructure.Internal.MySqlOptionsExtension

Created on 2 Sep 2017  路  11Comments  路  Source: PomeloFoundation/Pomelo.EntityFrameworkCore.MySql

The issue

I am not able to create a migration to update my mysql database. It throws an exception.

Exception message:
Stack trace: 

info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0]
      User profile is available. Using 'C:\Users\nicho\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
System.ArgumentException: An item with the same key has already been added. Key: Microsoft.EntityFrameworkCore.Infrastructure.Internal.MySqlOptionsExtension
   at System.ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(Object key)
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector)
   at Microsoft.EntityFrameworkCore.DbContextOptions`1.WithExtension[TExtension](TExtension extension)
   at Microsoft.EntityFrameworkCore.DbContextOptionsBuilder.Microsoft.EntityFrameworkCore.Infrastructure.IDbContextOptionsBuilderInfrastructure.AddOrUpdateExtension[TExtension](TExtension extension
)
   at Microsoft.EntityFrameworkCore.MySqlDbContextOptionsExtensions.ConfigureWarnings(DbContextOptionsBuilder optionsBuilder)
   at Microsoft.EntityFrameworkCore.MySqlDbContextOptionsExtensions.UseMySql(DbContextOptionsBuilder optionsBuilder, String connectionString, Action`1 mySqlOptionsAction)
ContextOptionsBuilder optionsBuilder, String connectionString, Action`1 mySqlOpsBuilder optionsBuilder) in E:\Repositories\LegionTD\LegionTDServerReborn\LegionTDServerReborn\Context\LegionTdContext
tionsAction)
   at LegionTDServerReborn.Models.LegionTdContext.OnConfiguring(DbContextOption
sBuilder optionsBuilder) in E:\Repositories\LegionTD\LegionTDServerReborn\Legiorastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
nTDServerReborn\Context\LegionTdContext.cs:line 39                             e[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()    ontext(Func`1 factory)
   at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infontext(String contextType)
rastructure.IInfrastructure<System.IServiceProvider>.get_Instance()            ration(String name, String outputDir, String contextType)
   at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetServictring name, String outputDir, String contextType)
e[TService](IInfrastructure`1 accessor)                                        DisplayClass0_1.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateC_DisplayClass3_0`1.<Execute>b__0()
ontext(Func`1 factory)                                                         ute(Action action)
   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.<>c__DisplayClass0_1.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
An item with the same key has already been added. Key: Microsoft.EntityFrameworkCore.Infrastructure.Internal.MySqlOptionsExtension
PS E:\Repositories\LegionTD\LegionTDServerReborn\LegionTDServerReborn>

Further technical details

MySQL version: 5.7.19
Operating system: MySQL-Server Ubuntu 16.04; Asp.Net-Server Windows 10
Pomelo.EntityFrameworkCore.MySql version: 2.0.0-rtm-10061:

closed-fixed type-bug

Most helpful comment

FWIW, DbContextOptionsBuilder class has IsConfigured property for this situation.

All 11 comments

@Roofkiller At least, you have to provide us the reproduce(your old models and old migration csharp scripts, new models. the smallest repro is preferred) which required in issue template. Only the stack trace will not be helpful to resolve your issue.

@caleblloyd Does 10061 contains your legacy migration supports?

Sorry I can reproduce the error with a new project:

  1. dotnet new web
  2. Setting up .csproj file:
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Folder Include="wwwroot\" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
    <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.0.0-rtm-10061" />
  </ItemGroup>
  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
    <DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="2.0.0" />
    <DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.0" />
  </ItemGroup>

</Project>
  1. Adding a simple context
public class TestContext : DbContext {
    public static string ConnectionString {get; set;}

    public class User {
        public string Name {get; set;}
    }

    public DbSet<User> Users {get; set;}

    public TestContext() {}

    public TestContext(DbContextOptions<TestContext> options)
        :base (options) {}

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            optionsBuilder.UseMySql(ConnectionString);
        }
}
  1. Updating the Startup class
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(env.ContentRootPath)
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                .AddEnvironmentVariables();
            Configuration = builder.Build();

        }

        public IConfigurationRoot Configuration { get; }
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddMvc();

            services.AddSingleton<IConfiguration>(Configuration);

            services.AddEntityFrameworkSqlite();
            TestContext.ConnectionString = Configuration.GetConnectionString("MySQLConnection");
            services.AddDbContext<TestContext>(
                options => options.UseMySql(Configuration.GetConnectionString("MySQLConnection")));
            services.AddResponseCompression();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();
            app.UseMvc(routes =>
            {
                routes.MapRoute("default", "{controller=test}/");
            });

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }
        }
    }
  1. dotnet ef migrations add init

I have a appsettings.json file which contains my mysql connection string.
And then the error occurs.

Your exception does not seem related to our library, rather it seems related to Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager. There's not much we can do to debug that, we don't use it.

info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0]
      User profile is available. Using 'C:\Users\nicho\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
System.ArgumentException: An item with the same key has already been added. Key: Microsoft.EntityFrameworkCore.Infrastructure.Internal.MySqlOptionsExtension
   at System.ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(Object key)

My best recommendation is to take a look at how our Functional tests are setup and try to mirror that. The Program.cs, Startup.cs, and AppDb.cs can all be found here:

https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/tree/master/test/EFCore.MySql.FunctionalTests

Removing the parameterless constructor of my DbContext solved the problem.

In my case, removing the parameterless constructor didn't work. I think my problem was was specifying the connection string twice.

// In ConfigureServices:
services.AddDbContext<MyDbContext>(
    options => options.UseMySql(MyDbContext.CONNECTION_STRING, mysqlOptions =>
    {
        mysqlOptions.ServerVersion(new Version(5, 7, 23), ServerType.MySql);
        mysqlOptions.DisableBackslashEscaping();
    }
));
// And in MyDbContext
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    base.OnConfiguring(optionsBuilder);
    optionsBuilder.UseLazyLoadingProxies(true);
    optionsBuilder.UseMySql(CONNECTION_STRING, x => { x.ServerVersion(new System.Version(5, 7, 23), ServerType.MySql); });
}

So I just changed it to:

services.AddDbContext<MyDbContext>();

And then everything worked.

FWIW, DbContextOptionsBuilder class has IsConfigured property for this situation.

When you add-migration or Update-database for the FIRST time you have to only use either constructro for DbContextOptions or use OnConfiguration() method. or you can use IsConfigured in the OnConfiguration() method.

I have multi tenant application and i have to change the connection run time based on the tenant database.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    var connectionString = FetchAsPerTenant();

    // I am getting the same error about key already exists. 
    return contextOptionsBuilder.UseMySql(connectionString);
}

@n-gao , @yukozh and @caleblloyd and @EliasCK and @mguinness

Please let me know for any solution of this. I am having .Net core 3.1 project.

@Prince269090 Do you want to set the connection string before using the DbContext or do you need to change the connection string of a DbContext that is already being used?

You might want to share your project or at least your full context initialization logic (including AddDbContext(), the DbContext constructors etc.) with us.

It is likely, that you are currently trying to initialize the context twice.

I have created a sample project where i am reconfiguring the connection string using MySql. find the github repository here.

Statup.cs
Context.cs

In same code above i have added the Sql Server commented code which works fine if i replace MySql with SQL Server.

Below is my actual project code Startup.cs where i have to register the context with default tenant, say 'tenant1' connection string.

"DefaultConnection": "Server=localhost;Database=ToDo_Tenant1;User Id=root;Password=Admin@123;",

```
services.AddDbContext(options =>
databaseTypeInstance.GetContextBuilder(options, connectionOptions.Value.DefaultConnection))
.AddUnitOfWork();


Now, Below is the code which is runtime executed based on the tenant we get from the header in api and i have to re-initialize the DbContext based on the tenant. Let's in this case 'Tenant2.

"DefaultConnection": "Server=localhost;Database=**ToDo_Tenant2**;User Id=root;Password=Admin@123;",


protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionString = FetchAsPerTenant();

// I am getting the same error about key already exists. 
return contextOptionsBuilder.UseMySql(connectionString);

}

```

I agree with your point that i am re-initializing the DbContext and don't won't add twice.

This code works fine with SqlServer and PostgreSql where my same method actually updates the connection to new connection.

Please help on same!

@AndriySvyryd , @caleblloyd , @toralux and @yukozh
I have found and fixed the issue. Please check the PR

Was this page helpful?
0 / 5 - 0 ratings

Related issues

matthewjcooper picture matthewjcooper  路  4Comments

SharmaHarsh7 picture SharmaHarsh7  路  4Comments

ptsneves picture ptsneves  路  3Comments

IonRobu picture IonRobu  路  3Comments

zuosc picture zuosc  路  3Comments