MySqlPoolManager.GetPool 报错An item with the same key has already been added.

报错:

System.ArgumentException: An item with the same key has already been added. Key: database=timsdb;server=127.0.0.1;port=3306;user id=admin;password=123456;characterset=utf8;allowuservariables=True;defaultcommandtimeout=600;sslmode=Disabled
at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
at MySql.Data.MySqlClient.MySqlPoolManager.GetPool(MySqlConnectionStringBuilder settings)
at MySql.Data.MySqlClient.MySqlConnection.Open()

环境

数据库版本:Mysql Ver 8.0.30 for Win64 on x86_64 (MySQL Community Server - GPL)
mysql.data.dll 引用版本 8.0.31

代码:

var providerName="MySql.Data.MySqlClient";
var _connectionString="database=timsdb;server=127.0.0.1;port=3306;user id=admin;password=123456;characterset=utf8;allowuservariables=True;defaultcommandtimeout=600;sslmode=Disabled";
          DbProviderFactory _dbProviderFactory = DbProviderFactories.GetFactory(providerName);
       Database     _database = new Database(_connectionString, _dbProviderFactory);
          DbConnection  _dbConnection = _dbProviderFactory.CreateConnection();
        _dbConnection.ConnectionString = _connectionString;

while(true)
{
           if (_dbConnection == null || IsDisposed)
            {
                _dbTransaction = null;
                _dbConnection = _dbProviderFactory.CreateConnection();
                _dbConnection.ConnectionString = _connectionString;
            }

            if (_dbConnection.State == ConnectionState.Open)
            {
                continue;
            }
          _dbConnection.Open();
}

排查思路

1、连接过多导致?报错不一致
2、连接关闭过于频繁?

这个错误消息表明,你已经在字典中添加了与键 database=timsdb;server=127.0.0.1;port=3306;user id=admin;password=123456;characterset=utf8;allo 相同的项,并且该键值对已存在。在 MySql.Data.MySqlClient.MySqlPoolManager.GetPool 函数中,你需要确保连接字符串是唯一的。你可以尝试给字符串添加一个随机数,以保证连接字符串的唯一性,如下所示:

var connectionString = "database=timsdb;server=127.0.0.1;port=3306;user id=admin;password=123456;characterset=utf8;allowuservariables=True;defaultcommandtimeout=600;sslmode=Disabled;" + Guid.NewGuid().ToString();

这可能是因为一个具有相同键的项目已经被添加。请检查您的代码,看看是否有重复添加重复key的情况。

你如果看源码就会发现实际上是因为MySqlPoolManager.GetPool会复用连接,调用了 Pools.Add(text, value);这是一个静态的Dictionary<string, MySqlPool>变量,但Pools本身并非线程安全的,在当前连接未初始化时,多线程情况下,就会引发这个异常。


     private static readonly Dictionary<string, MySqlPool> Pools;
        static MySqlPoolManager()
        {
            Pools = new Dictionary<string, MySqlPool>();
            ClearingPools = new List<MySqlPool>();
            maxConnectionIdleTime = 180;
            timer = new Timer(new TimerCallback(CleanIdleConnections), null, maxConnectionIdleTime * 1000 + 8000, maxConnectionIdleTime * 1000);
            AppDomain.CurrentDomain.ProcessExit += new EventHandler(UnloadAppDomain);
            AppDomain.CurrentDomain.DomainUnload += new EventHandler(UnloadAppDomain);
            AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly())!.Unloading += new Action<AssemblyLoadContext>(UnloadAssemblyLoadContext);
        }

        public static async Task<MySqlPool> GetPoolAsync(MySqlConnectionStringBuilder settings, bool execAsync, CancellationToken cancellationToken)
        {
            string text = GetKey(settings);
            SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
            semaphoreSlim.Wait(CancellationToken.None);
            Pools.TryGetValue(text, out var value);
            if (value == null)
            {
                value = await MySqlPool.CreateMySqlPoolAsync(settings, execAsync, cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
                Pools.Add(text, value);
            }
            else
            {
                value.Settings = settings;
            }

            semaphoreSlim.Release();
            return value;
        }

请问你解决了吗