El método SqlBulkCopy WriteToServer no escribe ningún dato cuando se usa datatable

c# sql sqlbulkcopy sql-server

Pregunta

Todo el siguiente procesamiento tiene lugar en la máquina local:

Tengo una base de datos de origen (en el servidor) y una base de datos de destino (máquina local). Tengo una lista de tablas que deseo copiar del origen al destino, es decir, servidor -> local.

Empiezo almacenando todos los datos del servidor en una matriz DataTable usando sentencias SELECT * simples y utilizando Adpter.Fill (myDataTable) y luego agregando myDataTable a la matriz DataTable.

Luego, localmente, ejecuto un script SQL que tengo en el disco para eliminar la base de datos local y volver a crearla. Obtuve la secuencia de comandos de SSMS usando [RightClick -> Tareas -> Generar secuencias de comandos]

Después de eliminar y volver a crear la base de datos local, uso SqlBulkCopy con la matriz DataTable anterior para copiar los datos del servidor en la base de datos local recién creada.


El problema es que todo funciona como se esperaba hasta que llego a la parte de SqlBulkCopy. No obtengo excepciones, ni mensajes, ni eventos bcp_SqlRowsCopied que se activen. Simplemente no se copian los datos ... ¿Qué está pasando aquí? Al menos esperaría algún tipo de error ...


Aquí está el código para la aplicación de la consola en su totalidad: Tenga en cuenta que no está listo para la producción ya que todavía no hay ningún tipo de manejo de errores.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Configuration;
using System.Data;
using System.IO;
using System.Diagnostics;

namespace TomboDBSync
{
    class Program
    {
        //Names of all the tables to copy from the server (the source) to our local db (the destination)
        public static string[] tables = new string[] {"br_Make_Model", "br_Model_Series", "br_Product_EngineCapacity", "br_Product_ProductAttributeDescription", "CompanyPassword", "dtproperties", "EngineCapacity", "Make", "Model", "PetrolType", "Product", "ProductAttribute", "ProductAttributeDescription", "ProductsImport", "ProductType", "Role", "SearchString", "Series", "Supplier", "Tally", "Transmission", "Users", "Year", "GRV"};


        static void Main(string[] args)
        {
            //Get Data from SourceDB
            DataTable[] dtTables = GetDataTables(tables);

            //Drop and Recreate Destination DB using SQL scripts
            DropAndRecreateDB();

            //Populate Destination with Data from SourceDB DataTables
            InsertDataFromDataTables(dtTables);
        }

        /// <summary>
        /// Takes all the data in the dtTables array which we got from the server (the source) and
        /// Bulk Copy it all into the local database (the destination)
        /// </summary>
        /// <param name="dtTables"></param>
        private static void InsertDataFromDataTables(DataTable[] dtTables)
        {
            foreach (DataTable dtTable in dtTables.ToList<DataTable>())
            {
                using (SqlBulkCopy bcp = new SqlBulkCopy(getLocalConnectionString(), SqlBulkCopyOptions.KeepIdentity & SqlBulkCopyOptions.KeepNulls))
                {
                    bcp.DestinationTableName = dtTable.TableName;

                    bcp.SqlRowsCopied += new SqlRowsCopiedEventHandler(bcp_SqlRowsCopied);
                    for (int colIndex = 0; colIndex < dtTable.Columns.Count; colIndex++)
                    {
                        bcp.ColumnMappings.Add(colIndex, colIndex);
                    }
                    bcp.WriteToServer(dtTable);

                }
             }                    
        }


        /// <summary>
        /// Row Copied eEvent handler for SqlBulkCopy
        /// </summary>
        static void bcp_SqlRowsCopied(object sender, SqlRowsCopiedEventArgs e)
        {
            Console.WriteLine("row written");
        }

        /// <summary>
        /// 1) Takes a list of tablenames.
        /// 2) Connects to the server (the source)
        /// 3) Does a SELECT * on all the tables and stick the results into DataTables
        /// </summary>
        /// <param name="tables"></param>
        /// <returns>Returns an array of DataTables with all the data from the server in them</returns>
        public static DataTable[] GetDataTables(string[] tables)
        {            
            //Query all the server tables and stick 'em into DataTables           
            DataTable[] dataTables = new DataTable[tables.Length];

            for (int tableIndex = 0; tableIndex < tables.Length; tableIndex++)
            {
                string qry = "SELECT * FROM " + tables[tableIndex] + ";";
                Console.Write(qry);
                DataTable dtTable = new DataTable();

                using (SqlConnection connection = new SqlConnection(getServerConnectionString()))
                {
                    if (connection.State != ConnectionState.Open) connection.Open();
                    using (SqlCommand cmd = new SqlCommand(qry, connection))
                    {
                        SqlDataAdapter adapter = new SqlDataAdapter();
                        adapter.SelectCommand = cmd;
                        adapter.Fill(dtTable);
                    }
                }
                dtTable.TableName = tables[tableIndex];
                dataTables[tableIndex] = dtTable;
                Console.WriteLine(" Rows: " + dtTable.Rows.Count);
            }
            return dataTables;
        }


        /// <summary>
        /// Parses and executes the script needed to drop and recreate the database
        /// </summary>
        private static void DropAndRecreateDB()
        {
            using (SqlConnection connection = new SqlConnection(getLocalConnectionString()))
            {
                string[] queries = getDropAndRecreateScript().Split(new string[] { "GO\r\n", "GO ", "GO\t" }, StringSplitOptions.RemoveEmptyEntries);
                foreach (string qry in queries)
                {
                    if (connection.State != ConnectionState.Open) connection.Open();
                    using (SqlCommand cmd = new SqlCommand(qry, connection))
                    {
                        cmd.ExecuteNonQuery();
                    }
                }
            }
        }

        /// <summary>
        /// Reads in the createdbscript.sql file from disk.
        /// It contains all the SQL statements needed to drop and recreate the database.
        /// </summary>
        /// <returns>SQL to drop and recreate the database</returns>

        public static string getDropAndRecreateScript()
        {
            string qry = "";
            StreamReader re = File.OpenText("createdbscript.sql");
            string input = null;
            while ((input = re.ReadLine()) != null)
            {
                qry += (" " + input + "\r\n"); 
            }
            Console.WriteLine(qry);
            re.Close();
            return qry;
        }

        public static string getServerConnectionString()
        {
            return ConfigurationManager.AppSettings["SOURCEDB"];
        }

        public static string getLocalConnectionString()
        {
            return ConfigurationManager.AppSettings["DESTINATIONDB"];
        }

    }
}

Respuesta aceptada

¡He intentado su código y con éxito copia las tablas para mí!

Para SqlRowsCopied evento SqlRowsCopied , debe configurar bcp.NotifyAfter a algún valor> 0.

En cuanto a por qué no estás viendo valores, no estoy exactamente seguro. Si el DB o las tablas no están allí, obtendrás una excepción (o, al menos, lo hice). Una diferencia en mi código es que comenté DropAndRecreateDB() y, cuando llegué a ese punto en el depurador, ejecuté un script de creación de archivos manualmente en SQL y verifiqué que las tablas estaban presentes.

Ya que su código de copia real funciona bien para mí como lo ha publicado, me gustaría verificar que sus cadenas de conexión sean lo que cree que son. Si pudiera publicar esa información, sería más fácil continuar rastreando.

Actualizar:

FWIW, aquí está mi script drop / create:

USE [master];
ALTER DATABASE MyTestDB2 SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
DROP DATABASE MyTestDB2;
GO
CREATE DATABASE MyTestDB2;
GO

USE [MyTestDB2];

CREATE TABLE [dbo].[tblPetTypes](
    [commonname] [nvarchar](50) NOT NULL,
    PRIMARY KEY CLUSTERED ([commonname])
)

CREATE TABLE [dbo].[tblPeople](
    [oid] [int] IDENTITY(1,1) NOT NULL,
    [firstname] [nvarchar](30) NOT NULL,
    [lastname] [nvarchar](30) NOT NULL,
    [phone] [nvarchar](30) NULL,
    PRIMARY KEY CLUSTERED ([oid])
)

CREATE TABLE [dbo].[tblPets](
    [oid] [int] IDENTITY(1,1) NOT NULL,
    [name] [nvarchar](50) NOT NULL,
    [pettype] [nvarchar](50) NULL,
    [ownerid] [int] NULL,
    PRIMARY KEY CLUSTERED ([oid])
) ON [PRIMARY]

... y copié de MyTestDB a MyTestDB2 en el mismo servidor.



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué