Dinaup 2027 ya está llegando: agentes de IA, stock por almacén y todo más rápido. Descubre las novedades →
DinaupBlog
← Volver al blog

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.

Equipo Dinaup8 de septiembre de 20265 min de lectura

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:

PropiedadQué es
StateReady, Working, Failed o Busy
UrlURL firmada del CSV. Vale una hora (UrlSeconds dice cuánto le queda). Vacía si no había filas
Rows / BytesFilas del fichero sin cabecera, y su tamaño comprimido
FromUtc / NextFromUtcEl rango de fechaia aplicado. NextFromUtc es el corte para la siguiente llamada
ExportIdIdentificador del trabajo, para recogerlo después
MessageMotivo 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ó nada

Las 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íaRitmoPor fila
Informe con LoadAllRowsAsync, páginas de 2.0001.600 filas/s1,5 KB retenidos en memoria
Export completo, 95 columnas11.600 filas/s400 bytes comprimidos
Export con 51 columnas15.000 filas/s284 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.

Siguiente paso

Sigue leyendo