ADO.NET – Bulk Copy


O Microsoft SQL Server fornece um utilitário de linha de comando bastante popular, chamado de bcp. Basicamente, este utilitário tem a finalidadade de copiar grandes volumes de dados entre duas bases de dados SQL Server. Abaixo é mostrado a sua sintaxe:

bcp {[[database_name.][owner].]{table_name | view_name} | "query"}
    {in | out | queryout | format} data_file
    [-m max_errors] [-f format_file] [-e err_file]
    [-F first_row] [-L last_row] [-b batch_size]
    [-n] [-c] [-w] [-N] [-V (60 | 65 | 70)] [-6] 
    [-q] [-C code_page] [-t field_term] [-r row_term]
    [-i input_file] [-o output_file] [-a packet_size]
    [-S server_name[instance_name]] [-U login_id] [-P password]
    [-T] [-v] [-R] [-k] [-E] [-h "hint [,...n]"]

O ADO.NET 2.0, com estas várias inovações, incluiu também uma classe chamada SqlBulkCopy, que provê uma funcionalidade similar ao utilitário bcp, com a vantagem de ser agora gerenciado pelo .NET Framework e ter uma boa performance quanto ao utilitário de linha de comando. Mas a principal vantagem, é que essa “cópia em massa” de dados não precisa necessariamente ser entre base de dados SQL Server. Você pode ter os dados carregados em um objeto DataTable, um Array de objetos do tipo DataRow ou até mesmo de um DataReader e, com isso, você passa esses containers de dados para o objeto SqlBulkCopy, para que o mesmo possa fazer o seu trabalho, copiando os dados para a base de dados de destino. Para um exemplo bem simples, vamos analisar o código abaixo:

using System.Data.SqlClient;
//...
string connString = GetConnStringFromConfigFile("ConnString");

using (SqlConnection connSource = new SqlConnection(connString)) {
    SqlDataReader dr = null;
    try
    {
        SqlConnection connDest = new SqlConnection(connString);
        SqlCommand cmd = new SqlCommand("SELECT * FROM Tabela", connSource);
        connSource.Open();
        dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);

        using (SqlBulkCopy copy = new SqlBulkCopy(connString)) {
            copy.DestinationTableName = "Tabela1";
            copy.WriteToServer(dr);
        }
    }
    finally
    {
        if (dr != null) dr.Close();
    }
}

Para exemplificar, criamos dois objetos do tipo SqlConnection, “connSource” e “connDest”, que são responsáveis pela conexão com a base de dados que será a fonte dos dados e a qual será o destino dos dados, respectivamente. Utilizamos um DataReader, para recuperarmos de forma rápida os dados e, em seguida, criamos o objeto SqlBulkCopy, informando em seu construtor a string de conexão com a base de dados de destino. O construtor deste objeto é sobrecarregado, podendo passar ao mesmo ao invés da string de conexão, um objeto SqlConnection da base de dados de destino, mas neste é obrigado que você controle a abertura e fechamento da conexão, o que não é necessário quando se passa a string de conexão, pois o processo que é manual na outra situação, aqui já estará encapsulado.

Através da propriedade DestinationTableName, informamos ao objeto SqlBulkCopy a tabela destino que receberá os dados. Quando as tabelas de origem e destino de dados são idênticas, ou seja, tem o mesmo número de colunas e também a mesma posição das colunas, o mapeamento entre as mesmas não é necessário. Entretanto, se a quantidade de colunas é diferente ou a ordem das colunas não são equivalentes, o mapeamento é necessário e, para isso, o objeto SqlBulkCopy fornece uma propriedade chamada ColumnMappings, a qual armazena uma coleção de itens do tipo SqlBulkCopyColumnMapping. Abaixo é mostrado um exemplo reduzido de como realizar esse mapeamento:

using (SqlBulkCopy copy = new SqlBulkCopy(connString)) {
    copy.DestinationTableName = "Tabela1";

    SqlBulkCopyColumnMapping colunaNome = new SqlBulkCopyColumnMapping("Nome", "NomeProduto")
    copy.ColumnMappings.Add(colunaNome);

    copy.WriteToServer(dr);
}

Além destas configurações, ainda temos três propriedades interessantes dentro do objeto SqlBulkCopy: BatchSize, BulkCopyTimeout e NotifyAfter. A primeira destas (BatchSize) é onde definimos através de um número inteiro, a quantidade de linhas que irá conter em cada batch (bloco). Se o valor informado para essa propriedade for 0 (zero), ela será executada em um simples batch. Já a propriedade BulkCopyTimeout é onde definimos, também através de um número inteiro, o número de segundos que a operação irá aguardar para ser completada, antes de dar timeout.

Por fim, temos ainda a propriedade NotifyAfter, onde definimos um número inteiro positivo que representará o número de linhas que serão processadas, antes de gerar um evento de notificação chamado SqlRowsCopied. Esta propriedade, em conjunto com o evento SqlRowsCopied (representado pelo delegate SqlRowsCopiedEventHandler), é utilizada geralmente para manipularmos objetos/controles da interface da aplicação, como por exemplo, exibir ao usuário um demostrativo do progresso da execução da cópia dos dados. Abaixo é mostrado um exemplo de como utilizá-la:

using (SqlBulkCopy copy = new SqlBulkCopy(connString))
{
    copy.DestinationTableName = "Tabela1";
    copy.NotifyAfter = 50;
    copy.SqlRowsCopied += new SqlRowsCopiedEventHandler(this.Notificacao);

    copy.WriteToServer(dr);
}

//...

private void Notificacao(object sender, SqlRowsCopiedEventArgs e)
{
    MessageBox.Show("Quantidade de Linhas copiadas: " +
        e.RowsCopied.ToString());
}

ADONET20.zip (118.08 kb)

Anúncios

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s