C# 连接 MySQL 报错:Unable to connect to any of the specified MySQL hosts

此文章发布于 58 个月前,部分信息可能已经过时,请自行斟酌确认。

问题描述

C# 使用 MySQL.Data.dll 连接 MySQL 数据库,报错如下:

异常信息

Unable to connect to any of the specified MySQL hosts.

内部异常:序列包含一个以上的匹配元素

20200320113600.png

测试环境:

  • win10 1909
  • Mysql.Data.dll 8.0.18
  • .Net Framework 4.7

两个现象:

1、奇怪的是微酷家里电脑报错,同样的代码办公室电脑不报错正常连接。
2、异常出现在数据库服务器地址配置为域名的情况下,如果改成 IP 则不会报错。

解决过程

断点调试

通过 dnSpy 断点调试 Mysql.Data.dll 发现:序列包含一个以上的匹配元素这个错误出现在以下代码段中:

命名空间:MySql.Data.Common.StreamCreator

private static Stream GetTcpStream(MySqlConnectionStringBuilder settings)
{
    Task<IPAddress[]> hostAddressesAsync = Dns.GetHostAddressesAsync(settings.Server);
    hostAddressesAsync.Wait();
    if (hostAddressesAsync.Result == null || hostAddressesAsync.Result.Length == 0)
    {
        throw new ArgumentException(Resources.InvalidHostNameOrAddress);
    }
    //下面这行代码异常了,发现 hostAddressesAsync.Result 中有两个相同的IP地址。
    IPAddress ipaddress = hostAddressesAsync.Result.SingleOrDefault((IPAddress c) => c.AddressFamily == AddressFamily.InterNetwork);
    ...
}

20200320_110654.png

异常原因:

调试发现家里电脑经过域名解析后得到两个相同的 IP 地址,而为何会查到两个重复的 IP 地址呢,不去追了,重点是如上代码中 LinqSingleOrDefault 方法当匹配的元素有多个时会抛出 InvalidOperationException 异常,即:序列包含一个以上的匹配元素

解决方案

经过上面调试确定是在通过域名获取 IP 地址时因为返回了多个 IP 而抛出异常

官方有个讨论贴:https://bugs.mysql.com/bug.php?id=97448

贴中有 3 点值得关注:

  1. 8.0.17 没有问题,升级到 8.0.188.0.19 均会出现此问题,所以降级到 8.0.17 是个方案。
  2. 官方开发人员说 8.0.20 会修改这个问题,多个 IP 会返回第一个。不过目前(2020/03/20)8.0.20 未发布。
  3. 有人提可更换为 MySqlConnector 0.61.0,它不仅具有相同的 api 并且性能提升不少。

另外:

微酷的方案是在自己项目代码中将主机名解析为 IP 后取第 1 个替换到数据库连接字符串的 server 中。

C# 版:

var ips = System.Net.Dns.GetHostAddresses("数据库服务器域名www.weiku.co");
var ip = ips.FirstOrDefault(x => x.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);

string connString = $"Server={ip};Port=3306;Database=weiku;Uid=weiku;Pwd=123";
/// <summary>
/// 获取主机名解析后的第一个外网 IP
/// 用于应对 MySQL.Data 8.0.18 对于一个域名解析到多个 IP 抛出异常问题。
/// 详情:https://weiku.co/article/559/
/// </summary>
/// <param name="hostName">主机名,如:weiku.co</param>
/// <returns>第一个外网 IP</returns>
public static string GetDNSIP(string hostName)
{
    try
    {
        var ip = System.Net.Dns.GetHostAddresses(hostName)
            .FirstOrDefault(x => x.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
        return ip?.ToString();
    }
    catch (Exception ex)
    {
        throw new Exception("提供的主机地址解析 IP 出错。" + ex.Message);
    }
}

相关资料

  1. MySQL 官方 Bug 贴:https://bugs.mysql.com/bug.php?id=97448
  2. https://mysqlconnector.net/
最后修改:2020 年 03 月 20 日 11 : 56 AM
如果觉得我的文章对你有用,请随意赞赏

发表评论