Hola devs, si están aprendiendo C# o incluso si ya lo dominan siempre les va a ayudar un montón que hagan herramientas por su cuenta, así aprenderás a aplicar los conocimientos que has aprendido, y de esa forma se aprende mucho mejor.
En esta ocasión, tuve la tarea de insertar muchos datos de un excel a tablas en SQL Server y aunque SSMS tiene su propia herramienta para eso, yo lo que quería era generar los scripts de inserción.
Características
La aplicación emplea Dapper para acceder a la estructura de las tablas y poder generar el insert con las columnas adecuadas y evitar también tomar en cuenta a aquellas columnas que son autoincrementales.
Puede trabajar con varias tablas y utiliza un archivo json para que le pases la configuración adecuada haciéndolo más versátil 🐿️.
Show me the code
Así luce la aplicación:
Me decidí hacerlo como consola para que el desarrollo sea rápido y aquí te traigo el código:
Consta de dos archivos básicamente, el config.json y la clase Program.cs
{
"ExcelPath": "InputOutputFiles/Datos.xlsx",
"OutputSqlPath": "InputOutputFiles/InsertScripts.sql",
"ConnectionString": "Server=.;Database=db;User Id=user;Password=*****;trust server certificate=true",
"TablasDestino": [ "tabla1", "tabla2"]
}
La clase Program luce así:
using Microsoft.Data.SqlClient;
using Dapper;
using ExcelDataReader;
using System.Data;
using System.Text.Json;
class Program
{
static void Main()
{
string configPath = Path.Combine(Directory.GetCurrentDirectory(), "config.json");
if (!File.Exists(configPath))
{
Console.WriteLine("Error: No se encontró config.json");
return;
}
var config = JsonSerializer.Deserialize<Dictionary<string, object>>(File.ReadAllText(configPath))!;
string excelPath = Path.Combine(Directory.GetCurrentDirectory(), config["ExcelPath"].ToString()!);
string outputSqlPath = Path.Combine(Directory.GetCurrentDirectory(), config["OutputSqlPath"].ToString()!);
string connectionString = config["ConnectionString"].ToString()!;
string[] tablasDestino = JsonSerializer.Deserialize<string[]>(config["TablasDestino"].ToString()!)!;
//string excelPath = Path.Combine(Directory.GetCurrentDirectory(), "Excel", "Datos.xlsx");
//string outputSqlPath = Path.Combine(Directory.GetCurrentDirectory(), "Excel", "InsertScripts.sql");
//string connectionString =
// "Server=.;Database=db;User Id=sa;Password=*****;trust server certificate=true";
////Lista de tablas a procesar, cada tabla debe tener una hoja en Datos.xlsx con el mismo nombre conteniendo los datos
//string[] tablasDestino = ["tabla1", "tabla2"];
Console.WriteLine($"***** Brave Scripter *****");
Console.WriteLine($"==========================");
Console.WriteLine($"Directorio actual: {Directory.GetCurrentDirectory()}");
Console.WriteLine($"Leyendo archivo: {excelPath}");
Console.WriteLine($"Se generarán scripts para las siguientes tablas: {string.Join(",", tablasDestino)}");
if (ConfirmarContinuacion())
{
if (File.Exists(excelPath))
{
try
{
var columnasPorTabla = ObtenerColumnasSql(connectionString, tablasDestino);
var scriptsGenerales = new List<string>();
using var stream = File.Open(excelPath, FileMode.Open, FileAccess.Read);
using var reader = ExcelReaderFactory.CreateReader(stream);
var dataSet = reader.AsDataSet(new ExcelDataSetConfiguration()
{
ConfigureDataTable = (_) => new ExcelDataTableConfiguration() { UseHeaderRow = true }
});
foreach (var tabla in tablasDestino)
{
var hoja = dataSet.Tables[tabla]; // Buscar hoja con el mismo nombre de la tabla
if (hoja != null)
{
var (columnasValidas, columnaAutoIncremental) = columnasPorTabla[tabla];
var datosExcel = LeerExcel(hoja, columnasValidas);
var scripts = GenerarInsertScripts(tabla, datosExcel, columnaAutoIncremental);
scriptsGenerales.AddRange(scripts);
}
else
{
Console.WriteLine($"No se encontró la hoja '{tabla}' en el Excel.");
}
}
File.WriteAllLines(outputSqlPath, scriptsGenerales);
Console.WriteLine($"Scripts generados en: {outputSqlPath}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
else
{
Console.WriteLine($"No se encontró el archivo en: {excelPath}");
}
}
Console.WriteLine("Pulse una tecla para salir...");
Console.ReadKey();
}
static Dictionary<string, (List<string> columnas, string? columnaAutoIncremental)> ObtenerColumnasSql(string connectionString, string[] tablas)
{
using var connection = new SqlConnection(connectionString);
connection.Open();
var resultado = new Dictionary<string, (List<string>, string?)>();
foreach (var tabla in tablas)
{
var columnas = connection.Query<(string ColumnName, int IsIdentity)>(
@"SELECT COLUMN_NAME AS ColumnName, COLUMNPROPERTY(OBJECT_ID(@tabla), COLUMN_NAME, 'IsIdentity') AS IsIdentity
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @tabla",
new { tabla }).ToList();
string? columnaAutoIncremental = columnas.FirstOrDefault(c => c.IsIdentity == 1).ColumnName;
var columnasValidas = columnas.Where(c => c.IsIdentity == 0).Select(c => c.ColumnName).ToList();
resultado[tabla] = (columnasValidas, columnaAutoIncremental);
}
return resultado;
}
static List<Dictionary<string, object>> LeerExcel(System.Data.DataTable hoja, List<string> columnasValidas)
{
var resultado = new List<Dictionary<string, object>>();
var columnasExcel = new List<string>();
foreach (DataColumn col in hoja.Columns)
{
string columnName = col.ColumnName.Trim();
if (columnasValidas.Contains(columnName))
columnasExcel.Add(columnName);
}
foreach (DataRow row in hoja.Rows)
{
var fila = new Dictionary<string, object>();
foreach (var columna in columnasExcel)
{
fila[columna] = row[columna] != DBNull.Value ? row[columna] : null;
}
resultado.Add(fila);
}
return resultado;
}
static List<string> GenerarInsertScripts(string tabla, List<Dictionary<string, object>> datos, string? columnaAutoIncremental)
{
var scripts = new List<string>();
foreach (var fila in datos)
{
var columnas = fila.Keys.Where(k => k != columnaAutoIncremental);
var valores = columnas.Select(col => fila[col] is string ? $"'{fila[col]}'" : fila[col]?.ToString() ?? "NULL");
scripts.Add($"INSERT INTO {tabla} ({string.Join(", ", columnas)}) VALUES ({string.Join(", ", valores)});");
}
return scripts;
}
public static bool ConfirmarContinuacion(string mensaje = "¿Desea continuar? (y/n): ")
{
Console.Write(mensaje);
while (true)
{
var tecla = Console.ReadKey(true).Key;
Console.WriteLine();
if (tecla == ConsoleKey.Y) return true;
if (tecla == ConsoleKey.N) return false;
Console.Write("Entrada no válida. Por favor, presione Y o N: ");
}
}
}
Puse todo en la clase Program.cs para que tengas un panorama completo de la aplicación, en futuros commits voy a mejorar eso.
Aquí te paso el repositorio, de paso me sigues y le das estrella 🐿️✌🏼
https://github.com/GeaSmart/BraveScripter
Si esta entrada te ha gustado, compártela tigre! 🙌
Fuente de la imagen de portada: Foto de Jakub Żerdzicki en Unsplash