c# - sqlbulkcopycolumnmapping - sqlbulkcopyoptions
Lograr que SqlBulkCopy respete los nombres de columna (7)
Aquí hay una solución para solucionar este ''error''.
El valor predeterminado es asignar por ordinal / posición.
En mi caso, estaba cargando desde una hoja de cálculo con columnas en orden aleatorio. Aquí hay una solución rápida (la tabla es mi tabla de datos que está "fuera de orden ordinal" y bulkCopy es el objeto SqLBulkCopy)
foreach (DataColumn col in table.Columns)
{
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
Como puede ver, solo lo estoy obligando a reordenar por nombre, a pesar de que los nombres son IDÉNTICOS.
Estoy en el proceso de convertir algunas rutinas de informes basadas en procedimientos almacenados para ejecutar en C #. La idea general es utilizar todas las maravillas de C # / .NET Framework y luego enviar los resultados a la base de datos. Todo ha ido bien, excepto por un problema que encontré ayer y resolví hoy.
Para realizar la importación, estoy creando mediante programación un DataTable y utilizando SqlBulkCopy para mover los resultados al servidor.
p.ej
DataTable detail = new DataTable();
detail.Columns.Add(new DataColumn("Stock", Type.GetType("System.Decimal")));
detail.Columns.Add(new DataColumn("Receipts", Type.GetType("System.Decimal")));
detail.Columns.Add(new DataColumn("Orders", Type.GetType("System.Decimal")));
La tabla de importación tenía los campos en el siguiente orden.
...
Stock decimal(18,4),
Orders decimal(18,4),
Receipts decimal(18,4)
...
Esencialmente, el valor de Recibos fue en Órdenes, y viceversa.
Evidentemente, esto se debe a que SqlBulkCopy no parece estar cumpliendo con el nombre de la columna que le he dicho que rellene, sino que va por la posición ordinal.
Este es un problema para mí ya que usamos la comparación de SQL de Redgate. Cuando se empujan los cambios de una base de datos a otra, la posición ordinal de las columnas no se mantiene necesariamente. (p. ej., si inserta una nueva columna como tercer campo ordinal, la Comparación de SQL simplemente la agregará a la tabla en una sincronización, lo que la convertirá en la última columna).
Esto ensucia seriamente con mi solución. Ahora, estas son solo tablas de ''importación'', así que si es necesario, puedo eliminarlas y volver a crearlas como parte de cualquier script de migración con las posiciones ordinales intactas. Aunque prefiero no hacer eso.
¿Hay alguna forma de obligar a SqlBulkCopy a respetar los nombres de columna que le indica que complete?
De http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.columnmappings.aspx :
Sin embargo, si los recuentos de columnas difieren, o las posiciones ordinales no son consistentes, debe usar ColumnMappings para asegurarse de que los datos se copien en las columnas correctas.
Para obtener un código de muestra, consulte "Asignación de columnas" en http://www.sqlteam.com/article/use-sqlbulkcopy-to-quickly-load-data-from-your-client-to-sql-server
Existen sobrecargas que le permiten especificar la posición ordinal o el nombre de la columna de origen que se asignará a la columna de destino.
Echa un vistazo a SqlBulkCopyColumnMappingCollection.Add y establece la propiedad http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.columnmappings.aspx .
Hay una opción en la comparación de SQL para forzar orden de columnas en las opciones del proyecto. Esto generará un script que reordena las columnas. Puede que esté equivocado, pero creo que esta operación puede requerir que la tabla se reconstruya, así que tenga cuidado, ya que esto podría afectar el rendimiento. El script, sin embargo, conservará los datos de la tabla.
Puede que me esté faltando algo, pero ¿por qué no podría cambiar el orden de sus columnas? Normalmente no construyo mi DataTable
a mano. Yo uso el método FillSchema de SqlDataAdapter
, usando una consulta "select * from targetTable"
. Así que siempre estoy seguro de que mi DataTable
ajusta a la tabla de destino.
Una forma sencilla de implementar la asignación de columnas en una copia masiva, asegúrese de que los nombres de las columnas de datos sean exactamente los mismos que en la tabla en la que desea volcar todos los datos
using (SqlBulkCopy bulkcopy = new SqlBulkCopy(dc.ConnectionString))
{
bulkcopy.BulkCopyTimeout = 150;
// column mapping defined
dt.Columns.Cast<DataColumn>().ToList().ForEach(f =>
{
SqlBulkCopyColumnMapping bccm = new SqlBulkCopyColumnMapping();
bccm.DestinationColumn = f.ColumnName;
bccm.SourceColumn = f.ColumnName;
bulkcopy.ColumnMappings.Add(bccm);
});
// column mapping defined
bulkcopy.DestinationTableName = outputTable;
bulkcopy.WriteToServer(dt);
}
Aquí hay una Gist con un método de extensión para ello .
Ejemplo:
bulkCopy
.WithColumnMappings(table.Columns)
.WriteToServer(table);
Esto borra las asignaciones de columna existentes, luego agrega una asignación para cada columna que se pasa.