ExportAsync: una sección entera a CSV con una llamada
Nueva función del SDK .NET de Dinaup: exporta una sección completa o solo lo que cambió a un CSV comprimido con URL firmada. Siete veces más rápido que paginar.
ExportAsync está disponible en el paquete Dinaup desde la versión 10.15.0.50. Pides una sección por su id y recibes un CSV comprimido con gzip, con una URL firmada, generado en tu servidor con los permisos de tu clave API. Sirve para volcar la sección entera o solo lo tocado desde la última vez, y puedes elegir qué columnas viajan.
Es la forma más rápida de sacar una sección de Dinaup: a los ritmos medidos, siete veces más rápido que recorrerla como informe con LoadAllRowsAsync, y sin acumular las filas en memoria.
Exportar y leer
using Dinaup;
using static DemoUp.MyDinaup.SectionsD;
var export = await client.ExportAsync(RecambiosD._SectionIDGUID);
if (export.State != ExportStateE.Ready)
throw new Exception($"Export {export.State}: {export.Message}");
var fichero = await client.DownloadExportAsync(export);
foreach (var fila in fichero.Rows)
{
var precio = fila[RecambiosD.RecambiosES.ImportePrecioVenta];
var borrado = fila[RecambiosD.RecambiosES.Eliminado] == "1";
}ExportAsync devuelve un ExportDTO:
| Propiedad | Qué es |
|---|---|
State | Ready, Working, Failed o Busy |
Url | URL firmada del CSV. Vale una hora (UrlSeconds dice cuánto le queda). Vacía si no había filas |
Rows / Bytes | Filas del fichero sin cabecera, y su tamaño comprimido |
FromUtc / NextFromUtc | El rango de fechaia aplicado. NextFromUtc es el corte para la siguiente llamada |
ExportId | Identificador del trabajo, para recogerlo después |
Message | Motivo del fallo. Vacío si todo fue bien |
DownloadExportAsync baja el fichero, lo descomprime y lo parsea como lo escribe Postgres: comas, comillas dobladas dentro de un campo y saltos de línea dentro de un texto citado. Las columnas se leen por nombre de campo, con las constantes de SectionsD. Pedir una columna que no está en el fichero lanza una excepción que lista las que hay.
Rows es perezoso: las filas se interpretan según las recorres, de una en una. Para acceso por posición en un fichero pequeño, Read(fila, columna) lo materializa entero la primera vez.
Incremental
Con fromUtc salen solo las filas cuyo fechaia sea igual o posterior. El corte de arriba lo pone el servidor al empezar y vuelve en NextFromUtc: es el fromUtc de la siguiente llamada. Encadena siempre con ese valor, nunca con tu reloj, o perderás las filas escritas mientras se generaba el fichero.
var completo = await client.ExportAsync(RecambiosD._SectionIDGUID);
var corte = completo.NextFromUtc;
// Cada vuelta: solo lo tocado desde el corte anterior.
var cambios = await client.ExportAsync(RecambiosD._SectionIDGUID, corte);
corte = cambios.NextFromUtc;
if (cambios.Rows == 0) return; // Ready sin URL: no cambió nadaLas bajas vienen dentro. Borrar en Dinaup es lógico: la fila sigue con eliminado a 1 y ese cambio mueve su fechaia, así que sale en el incremental como una modificación más. El CSV nunca filtra por eliminado.
Columnas
El tercer parámetro es la lista de columnas. El fichero sale con esas y en ese orden. Una columna que no se puede exportar es un error del servidor con la lista de las que no valen, no un CSV al que le falta algo en silencio.
var columnas = new[]
{
RecambiosD.RecambiosES.ID,
RecambiosD.RecambiosES.Eliminado,
RecambiosD.RecambiosES.FechaIndiceActividad_UTC,
RecambiosD.RecambiosES.TextoPrincipal,
RecambiosD.RecambiosES.ImportePrecioVenta,
};
var export = await client.ExportAsync(RecambiosD._SectionIDGUID, columnas);Pedir menos columnas es el ahorro más barato: casi todo el tiempo de un export es descarga.
Exports que tardan más de dos minutos
ExportAsync espera hasta dos minutos. Si el fichero no está, devuelve el ExportDTO en Working con su ExportId. El servidor guarda el resultado 30 minutos; lo recoges con ExportStateAsync.
var export = await client.ExportAsync(RecambiosD._SectionIDGUID);
while (export.State == ExportStateE.Working)
{
await Task.Delay(TimeSpan.FromSeconds(5));
export = await client.ExportStateAsync(export.ExportId);
}Solo corre un export a la vez por empresa: retiene una conexión a tu base de datos mientras dura. Si hay otro en marcha, el tuyo espera su turno dentro de esos dos minutos.
Rendimiento
Medido en frío sobre nuestro tenant de pruebas, en una sección de recambios con 95 columnas exportables. El SDK en un portátil, el servidor en su centro de datos.
| Vía | Ritmo | Por fila |
|---|---|---|
Informe con LoadAllRowsAsync, páginas de 2.000 | 1.600 filas/s | 1,5 KB retenidos en memoria |
| Export completo, 95 columnas | 11.600 filas/s | 400 bytes comprimidos |
| Export con 51 columnas | 15.000 filas/s | 284 bytes comprimidos |
Del tiempo de un export completo, un 29 % es el servidor escribiendo el CSV, un 57 % la descarga y un 14 % recorrerlo. Con 51 columnas el fichero pesa un 29 % menos y el total baja un 23 %. El tiempo que verás tú depende sobre todo de tu ancho de banda de bajada.
Antes de publicarlo cotejamos celda a celda el export contra el informe sobre las mismas filas: millones de celdas en dos secciones, cero diferencias.
Límites
- No viajan nunca, aunque la clave tenga permiso para verlos: contraseñas, campos confidenciales y campos eliminados u obsoletos de la sección.
- Las listas de una sección no van dentro. Exporta la sección de lista aparte y cruza por
idrelacionlistador. - Un valor nulo y una cadena vacía llegan igual, como
"". - No hay filtros salvo el corte de fecha, ni columnas calculadas, ni relaciones resueltas: cada campo sale tal cual está en la base. Para eso está un informe con
LoadAllRowsAsync. - La cabecera lleva el nombre de campo de la sección, no el título de la columna de un informe. Al migrar un consumidor de informe a export, comprueba cada columna contra su constante.
No hace falta regenerar tu MyDinaup: ExportAsync, ExportStateAsync y DownloadExportAsync viven en el cliente.
