Programação Por Equipe DevBras

Capa do artigo: Conector MySQL no Visual Studio: "funciona na minha máquina" é versão não anotada

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

  1. Descubra o que o projeto usa hoje. Procure no .csproj uma Reference para MySql.Data sem caminho de pacote, e no app.config uma seção DbProviderFactories. Rode dotnet list package para ver o que vem de NuGet.
  2. Escolha um conector distribuído por NuGet. As duas opções comuns são MySql.Data, da Oracle, e MySqlConnector, de código aberto com licença MIT. A troca entre eles exige ajustar o namespace (MySqlConnector no segundo).
  3. Fixe a versão no PackageReference. Versão exata, revisada quando alguém decide atualizar, nunca "a mais nova que tiver".
  4. Ative o arquivo de lock. Com RestorePackagesWithLockFile, o restore gera packages.lock.json, que vai para o Git. No CI, dotnet restore --locked-mode falha se alguém mudar dependência sem atualizar o lock.
  5. Registre o provider no projeto, se você usa DbProviderFactories: no código, em .NET moderno; no app.config, em .NET Framework.
  6. Trave o SDK com global.json, para todo mundo compilar com a mesma linha do .NET.
  7. 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.config com 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_password para 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 PackageReference com versão exata.
  • packages.lock.json está 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?

Avalie este artigo

Referência: https://dev.mysql.com/doc/visual-studio/en/visual-studio-install.html