Conector MySQL no Visual Studio: "funciona na minha máquina" é versão não anotada
Troque o conector instalado por wizard por um pacote NuGet com versão travada, registre o provider no código e documente servidor e conector para qualquer máquina conectar.
O sistema de estoque compila na máquina do Rafael e conecta no MySQL de primeira. Na máquina da Júlia, recém-formatada, o mesmo código, no mesmo commit, para em Unable to find the requested .Net Framework Data Provider. It may not be installed. Na do estagiário, a mensagem é outra: Authentication method 'caching_sha2_password' not supported by any of the available plugins. Três máquinas, três resultados, nenhuma linha de código diferente.
A resposta curta: o conector faz parte do projeto, não da máquina. Referencie o conector como pacote NuGet com versão fixa no .csproj, registre o provider no código ou no arquivo de configuração do projeto (não no machine.config que um instalador alterou), trave as versões com arquivo de lock e escreva no README qual versão do servidor MySQL e qual plugin de autenticação o projeto espera. Uma máquina nova deve conectar só seguindo o README.
Por que o mesmo código conecta numa máquina e não na outra
O tutorial clássico manda instalar o "MySQL for Visual Studio" e o Connector/NET pelo instalador. O instalador coloca a DLL num lugar do sistema e registra o provider no machine.config do .NET Framework. O projeto passa a depender de algo que não está no repositório: se o projeto usa DbProviderFactories.GetFactory("MySql.Data.MySqlClient") e a máquina não rodou o instalador, vem a primeira mensagem de erro.
A segunda mensagem é de versão. O MySQL 8 adotou caching_sha2_password como plugin de autenticação padrão, e conectores antigos não sabem falar com ele. A máquina do estagiário tinha um instalador velho baixado de um link antigo da wiki. No MySQL 8.4, o plugin antigo mysql_native_password vem desativado por padrão, então "voltar o usuário para o plugin antigo" deixou de ser um contorno simples.
Há ainda a integração com o Visual Studio, que adiciona o MySQL ao Server Explorer e aos designers. Ela é ferramenta de quem desenvolve, não dependência de execução. Se o projeto só roda com ela instalada, há algo do designer gerado que precisa sair.
Como travar o conector no projeto
- Descubra o que o projeto usa hoje. Procure no
.csprojumaReferenceparaMySql.Datasem caminho de pacote, e noapp.configuma seçãoDbProviderFactories. Rodedotnet list packagepara ver o que vem de NuGet. - Escolha um conector distribuído por NuGet. As duas opções comuns são
MySql.Data, da Oracle, eMySqlConnector, de código aberto com licença MIT. A troca entre eles exige ajustar o namespace (MySqlConnectorno segundo). - Fixe a versão no
PackageReference. Versão exata, revisada quando alguém decide atualizar, nunca "a mais nova que tiver". - Ative o arquivo de lock. Com
RestorePackagesWithLockFile, o restore gerapackages.lock.json, que vai para o Git. No CI,dotnet restore --locked-modefalha se alguém mudar dependência sem atualizar o lock. - Registre o provider no projeto, se você usa
DbProviderFactories: no código, em .NET moderno; noapp.config, em .NET Framework. - Trave o SDK com
global.json, para todo mundo compilar com a mesma linha do .NET. - Escreva o README com versão do servidor, plugin de autenticação, versão do conector e o comando de teste.
Exemplo: o projeto de estoque depois do ajuste
O .csproj, com o conector fixo e o lock ativado. O número da versão é exemplo; use a que o seu time validou:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MySqlConnector" Version="2.4.0" />
</ItemGroup>
</Project>
O global.json na raiz do repositório:
{
"sdk": {
"version": "10.0.100",
"rollForward": "latestFeature"
}
}
Se o código usa a fábrica genérica de providers, registre-a no início do programa em vez de depender do machine.config:
using System.Data.Common;
using MySqlConnector;
DbProviderFactories.RegisterFactory("MySqlConnector", MySqlConnectorFactory.Instance);
Em projetos .NET Framework que continuam com MySql.Data, a mesma ideia vai no app.config do projeto, que viaja com o repositório:
<system.data>
<DbProviderFactories>
<remove invariant="MySql.Data.MySqlClient" />
<add name="MySQL Data Provider"
invariant="MySql.Data.MySqlClient"
description=".Net Framework Data Provider for MySQL"
type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data" />
</DbProviderFactories>
</system.data>
E um teste de fumaça que qualquer pessoa roda depois de clonar. A string de conexão vem de variável de ambiente, nunca do código versionado:
using MySqlConnector;
var cs = Environment.GetEnvironmentVariable("ESTOQUE_DB")
?? throw new InvalidOperationException("Defina a variável ESTOQUE_DB.");
await using var conn = new MySqlConnection(cs);
await conn.OpenAsync();
await using var cmd = new MySqlCommand("SELECT VERSION()", conn);
Console.WriteLine("Servidor MySQL: " + await cmd.ExecuteScalarAsync());
Console.WriteLine("Conector: " + typeof(MySqlConnection).Assembly.GetName().Version);
O README que evita a próxima tarde perdida
## Banco de dados
- Servidor: MySQL 8.4 (LTS), plugin de autenticação caching_sha2_password
- Conector: MySqlConnector, versão fixada no .csproj (não instale conector pelo instalador)
- SDK: definido em global.json
- Teste: defina ESTOQUE_DB e rode "dotnet run --project tools/TesteConexao"
- Saída esperada: "Servidor MySQL: 8.4.x" e a versão do conector
Com isso, a Júlia clona, define a variável, roda o teste e vê a versão do servidor. Se falhar, a saída diz se o problema é de rede, de credencial ou de versão, e não "o provider não está instalado". Para o caso de o banco estar em outra máquina, siga MySQL em outro servidor; para guardar a string de conexão sem vazar senha, veja por que a connection string não se troca na tela.
Erros comuns
- Depender do conector instalado pelo wizard. Cada máquina tem uma versão, e a nova não tem nenhuma; o erro aparece como "provider não instalado".
- Versão flutuante no pacote. Um restore em dia diferente traz outra versão e o comportamento muda sem commit.
- Misturar provider antigo no
app.configcom pacote novo. A aplicação carrega uma DLL e a configuração aponta para outra, e o erro de carga de tipo parece bug de código. - Rebaixar o usuário para
mysql_native_passwordpara fazer o conector antigo funcionar. Você troca um problema de versão por um de segurança e por um que volta na próxima atualização do servidor. - Connection string com senha no repositório. O teste funciona para todos e a senha do banco vai junto para qualquer clone.
- README que não cita versões. A pessoa nova repete a descoberta de três dias atrás.
Checklist
- O conector vem de
PackageReferencecom versão exata. packages.lock.jsonestá no Git e o CI restaura em modo travado.- Nenhum passo do projeto depende de instalador de conector ou do
machine.config. - O SDK está fixado em
global.json. - O README cita versão do servidor, plugin de autenticação e conector.
- Existe um teste de conexão que imprime a versão do servidor e do conector.
- A string de conexão vem de variável de ambiente ou de segredo local.
Formate mentalmente uma máquina: seguindo só o README do seu projeto, ela conecta no banco sem instalar nada além do SDK?
Referência: https://dev.mysql.com/doc/visual-studio/en/visual-studio-install.html