Entendendo o erro

O erro “Communication with the underlying transaction manager has failed” ocorre quando o serviço Microsoft Distributed Transaction Coordinator (MSDTC) em uma máquina não consegue se comunicar com o serviço MSDTC em outra máquina para coordenar uma transação distribuída.

O erro completo e a exceção interna geralmente se parecem com isso:

System.Transactions.TransactionManagerCommunicationException:
Communication with the underlying transaction manager has failed.

Inner Exception:
The MSDTC transaction manager was unable to pull the transaction from the
source transaction manager due to communication problems. Possible causes are:
a firewall is present and it doesn't have an exception for the MSDTC process,
the two machines cannot find each other by their NetBIOS names, or the support
for network transactions is not enabled for one of the two transaction managers.
(Exception from HRESULT: 0x8004D02B)

Isso ocorre comumente quando:

  • Uma aplicação .NET usa TransactionScope entre múltiplas instâncias do SQL Server
  • Um procedimento armazenado usa servidores vinculados com BEGIN DISTRIBUTED TRANSACTION
  • Serviços WCF usam bindings transacionais entre máquinas

Quando as transações distribuídas são acionadas

Você pode não perceber que sua aplicação usa transações distribuídas. Elas são escaladas automaticamente nestes cenários:

// This creates a distributed transaction if conn1 and conn2 point to different servers
using (TransactionScope scope = new TransactionScope())
{
    using (SqlConnection conn1 = new SqlConnection("Server=Server1;Database=DB1;..."))
    using (SqlConnection conn2 = new SqlConnection("Server=Server2;Database=DB2;..."))
    {
        conn1.Open();
        // Execute work on Server1

        conn2.Open();  // This escalates to a distributed transaction
        // Execute work on Server2
    }
    scope.Complete();
}

Mesmo abrir duas conexões com o mesmo servidor pode escalar para o MSDTC em alguns casos (antes do .NET 4.0 com SQL Server 2008).

Correção passo a passo

Passo 1: Configurar as definições de segurança do MSDTC

Isso deve ser feito em ambas as máquinas envolvidas na transação.

  1. Abra os Serviços de Componentes executando dcomcnfg
  2. Navegue até: Serviços de Componentes > Computadores > Meu Computador > Coordenador de Transações Distribuídas > DTC Local
  3. Clique com o botão direito em DTC Local e selecione Propriedades
  4. Vá para a guia Segurança
  5. Configure as seguintes definições:
DefiniçãoValor
Acesso DTC de RedeMarcado
Permitir Clientes RemotosMarcado
Permitir EntradaMarcado
Permitir SaídaMarcado
Autenticação Mútua NecessáriaVeja nota abaixo
Nenhuma Autenticação NecessáriaSelecione se as máquinas NÃO estão no mesmo domínio
Habilitar Transações XAMarcado (se usar XA)
Habilitar Transações SNA LU 6.2Deixar desmarcado a menos que necessário

Nota sobre autenticação: Se ambas as máquinas estão no mesmo domínio do Active Directory, use Autenticação Mútua Necessária. Se estão em domínios ou grupos de trabalho diferentes, use Nenhuma Autenticação Necessária.

  1. Clique em OK e confirme que deseja reiniciar o serviço MSDTC

Passo 2: Configurar o Firewall do Windows

A exceção interna menciona especificamente o firewall. No Windows 7, Windows Server 2008 e versões posteriores, você deve habilitar explicitamente a exceção de firewall do MSDTC, mesmo que ache que já o fez.

Usando o Firewall do Windows com Segurança Avançada

# Enable the predefined MSDTC firewall rules (do this on BOTH machines)
Enable-NetFirewallRule -DisplayGroup "Distributed Transaction Coordinator"

Ou manualmente pela interface gráfica:

  1. Abra o Firewall do Windows com Segurança Avançada (wf.msc)
  2. Clique em Regras de Entrada
  3. Encontre as regras no grupo Coordenador de Transações Distribuídas
  4. Habilite todas (clique direito > Habilitar Regra):
    • Coordenador de Transações Distribuídas (RPC)
    • Coordenador de Transações Distribuídas (RPC-EPMAP)
    • Coordenador de Transações Distribuídas (TCP-In)

Repita para as Regras de Saída também.

Configurar uma porta MSDTC fixa (recomendado para firewalls)

Por padrão, o MSDTC usa portas RPC dinâmicas, o que dificulta a configuração do firewall. Você pode atribuir uma porta fixa:

  1. Abra os Serviços de Componentes (dcomcnfg)
  2. Navegue até Meu Computador > Coordenador de Transações Distribuídas > DTC Local > Propriedades
  3. Não há interface gráfica para a configuração da porta; use o registro:
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSDTC]
"ServerTcpPort"=dword:00001388

Isso configura o MSDTC para usar a porta TCP 5000 (0x1388). Em seguida, crie uma regra de firewall específica:

New-NetFirewallRule -DisplayName "MSDTC Fixed Port" -Direction Inbound -Protocol TCP -LocalPort 5000 -Action Allow
New-NetFirewallRule -DisplayName "MSDTC RPC" -Direction Inbound -Protocol TCP -LocalPort 135 -Action Allow

Após alterar a porta, reinicie o serviço MSDTC:

Restart-Service -Name MSDTC

Passo 3: Verificar a resolução de nomes NetBIOS

As duas máquinas devem ser capazes de resolver uma à outra pelo nome NetBIOS, não apenas por endereço IP ou FQDN. O MSDTC usa o nome NetBIOS internamente.

Teste a partir de cada máquina:

:: From the client, test resolution of the server
ping SERVER1
nbtstat -a SERVER1

:: From the server, test resolution of the client
ping CLIENT1
nbtstat -a CLIENT1

Se a resolução de nomes falhar, adicione entradas ao arquivo hosts em ambas as máquinas:

# C:\Windows\System32\drivers\etc\hosts
10.0.1.10    SERVER1
10.0.1.20    CLIENT1

Ou melhor, certifique-se de que o WINS ou DNS com os registros apropriados esteja configurado.

Passo 4: Verificar se o serviço MSDTC está em execução

# Check MSDTC service status on both machines
Get-Service -Name MSDTC | Select-Object Name, Status, StartType

# Ensure it is set to start automatically
Set-Service -Name MSDTC -StartupType Automatic
Start-Service -Name MSDTC

Passo 5: Testar a comunicação do MSDTC

Use a ferramenta DTCPing da Microsoft para testar a conectividade do MSDTC entre duas máquinas.

  1. Baixe o DTCPing da Microsoft
  2. Execute dtcping.exe em ambas as máquinas simultaneamente
  3. Em cada máquina, insira o nome NetBIOS da outra máquina
  4. Clique em Ping — um teste bem-sucedido mostra “successfully completed” tanto para os testes RPC quanto de transação

Alternativamente, teste pelo PowerShell:

# Test RPC endpoint mapper connectivity
Test-NetConnection -ComputerName SERVER1 -Port 135

# Test the MSDTC fixed port (if configured)
Test-NetConnection -ComputerName SERVER1 -Port 5000

Transações distribuídas com SQL Server

Cenário: Consultas com servidor vinculado

Quando o SQL Server executa uma consulta através de um servidor vinculado, o MSDTC está envolvido:

-- This triggers a distributed transaction
BEGIN DISTRIBUTED TRANSACTION
    INSERT INTO LocalDB.dbo.Orders (OrderID, CustomerName)
    SELECT OrderID, CustomerName FROM [RemoteServer].RemoteDB.dbo.Orders
    WHERE OrderDate > '2024-01-01';
COMMIT TRANSACTION

Certifique-se de que o MSDTC esteja configurado em ambas as máquinas do SQL Server.

Cenário: .NET TransactionScope

using (var scope = new TransactionScope())
{
    using (var conn = new SqlConnection(connectionString1))
    {
        conn.Open();
        using (var cmd = conn.CreateCommand())
        {
            cmd.CommandText = "INSERT INTO Table1 (Col1) VALUES ('Value1')";
            cmd.ExecuteNonQuery();
        }
    }

    using (var conn = new SqlConnection(connectionString2))
    {
        conn.Open();  // Escalates to distributed transaction
        using (var cmd = conn.CreateCommand())
        {
            cmd.CommandText = "INSERT INTO Table2 (Col1) VALUES ('Value2')";
            cmd.ExecuteNonQuery();
        }
    }

    scope.Complete();
}

Evitando transações distribuídas desnecessárias

Se ambas as conexões apontam para a mesma instância do SQL Server, você pode evitar a escalação para o MSDTC reutilizando uma única conexão:

using (var scope = new TransactionScope())
using (var conn = new SqlConnection(connectionString))
{
    conn.Open();

    using (var cmd1 = conn.CreateCommand())
    {
        cmd1.CommandText = "INSERT INTO DB1.dbo.Table1 (Col1) VALUES ('Value1')";
        cmd1.ExecuteNonQuery();
    }

    using (var cmd2 = conn.CreateCommand())
    {
        cmd2.CommandText = "INSERT INTO DB2.dbo.Table2 (Col1) VALUES ('Value2')";
        cmd2.ExecuteNonQuery();
    }

    scope.Complete();
}

Lista de verificação para solução de problemas

Percorra esta lista de verificação em ambas as máquinas:

  1. O serviço MSDTC está em execução e configurado para início automático
  2. As configurações de segurança dos Serviços de Componentes estão corretas (Acesso DTC de Rede habilitado, Permitir Entrada/Saída)
  3. O Firewall do Windows tem a exceção do Coordenador de Transações Distribuídas habilitada para entrada e saída
  4. A resolução de nomes NetBIOS funciona em ambas as direções
  5. O mesmo nível de autenticação MSDTC em ambas as máquinas (Autenticação Mútua, Autenticação de Chamador de Entrada ou Sem Autenticação)
  6. A porta RPC 135 está acessível entre as máquinas
  7. O teste DTCPing é bem-sucedido em ambas as direções
  8. O antivírus não está bloqueando o tráfego do MSDTC ou RPC
  9. O SQL Server tem o acesso remoto habilitado (sp_configure 'remote access', 1)
  10. Ambientes em cluster: Se o SQL Server está em cluster, configure o MSDTC como um recurso em cluster, não apenas nos nós individuais

Armadilhas comuns

O Firewall do Windows “parece” aberto mas não está

No Windows 7 e Server 2008+, o firewall pode parecer ter as exceções do MSDTC habilitadas, mas as regras podem estar aplicadas ao perfil de rede errado (Domínio vs Privado vs Público). Verifique:

Get-NetFirewallRule -DisplayGroup "Distributed Transaction Coordinator" |
    Select-Object Name, Enabled, Profile, Direction

Certifique-se de que as regras estejam habilitadas para o perfil correto.

Diferentes instâncias do MSDTC em um cluster

Em um Cluster de Failover do Windows Server, cada instância em cluster do SQL Server deve ter seu próprio recurso MSDTC. O MSDTC local no nó pode não ser o que está processando as transações para a instância do SQL em cluster.

Corrupção do arquivo de log do MSDTC

Se o arquivo de log do MSDTC estiver corrompido, o serviço pode iniciar mas falhar ao processar transações:

:: Reset the MSDTC log
msdtc -resetlog

Resumo

O erro “Communication with the underlying transaction manager has failed” é um problema de configuração de firewall e MSDTC. Para corrigi-lo, abra os Serviços de Componentes em ambas as máquinas e habilite o Acesso DTC de Rede com Permitir Entrada e Permitir Saída. Em seguida, habilite a exceção do Coordenador de Transações Distribuídas no Firewall do Windows em ambas as máquinas. Verifique se as máquinas podem resolver uma à outra pelo nome NetBIOS e use o DTCPing para validar a comunicação MSDTC de ponta a ponta.