Utilidades

Debouncer, ExpirableValue, RateLimiter, TokenBucket, SlidingCounter, SerialQueue, colas en segundo plano, conversiones STR, INT y ToSQL, formateadores y extensiones.

El paquete Dinaup incluye un módulo de utilidades con primitivas de concurrencia y limitación, conversiones de tipos, formateadores y métodos de extensión. Todo está en el namespace Dinaup: no hay nada que instalar aparte.

using Dinaup;

Primitivas de concurrencia y limitación

Debouncer

Retrasa una acción asíncrona y cancela las llamadas anteriores si llega otra dentro de la ventana. Para operaciones caras disparadas por eventos rápidos (teclas, desplazamiento, redibujados). Implementa IDisposable.

var debouncer = new Debouncer(async () => await GuardarBorradorAsync(), 500);

// Cada pulsación reinicia el contador; solo se guarda 500 ms tras la última
debouncer.Debounce();

ExpirableValue y ExpirableValueAsync

Envuelven un valor con un tiempo de vida (expirationInSeconds). Al leerlo, si ha caducado, llaman a la función de refresco.

// Asíncrono: refresca desde una fábrica cada 60 s
var token = new ExpirableValueAsync<string>(async () => await PedirTokenAsync(), 60);
string actual = await token.GetValueAsync();

// Síncrono, con actualizador o con valor inicial
var config = new ExpirableValue<int>(() => LeerConfig(), 30);
int n = config.GetValue();

var cache = new ExpirableValue<string>("valor inicial", 30);
cache.SetValue("otro valor");   // reinicia la caducidad

RateLimiter

Limita intentos por clave. TryAttempt(key) incrementa el contador de esa clave y devuelve false si supera el máximo. Se reinicia sola al vencer clearInterval; NextClear dice cuándo.

var limiter = new RateLimiter(maxAttempts: 5, clearInterval: TimeSpan.FromMinutes(1));

if (limiter.TryAttempt(userEmail) == false)
    throw new Exception("Demasiados intentos, prueba en un minuto.");

TokenBucket

Cubo de tokens clásico. Se rellena a refillPerSecond tokens por segundo. TryConsume(count = 1) devuelve true si hay tokens y false si no.

var bucket = new TokenBucket(maxTokens: 100, refillPerSecond: 10);

if (bucket.TryConsume())
    await LlamarApiExternaAsync();

SlidingCounter

Limitador por ventana deslizante. TryAdd() devuelve true mientras el número de eventos dentro de la ventana no supere el máximo; descarta sola los eventos antiguos.

var counter = new SlidingCounter(maxEvents: 1000, window: TimeSpan.FromSeconds(10));

if (counter.TryAdd() == false)
    return; // pico de actividad: descarta o encola

SerialQueue

Cola FIFO segura entre hilos para ejecutar tareas de una en una. Cada Enqueue devuelve un Task que completa cuando el trabajo termina. Admite Action, Func<T>, Func<Task>, Func<Task<T>> y Action con CancellationToken.

var queue = new SerialQueue();

// Se ejecutan en orden, nunca a la vez
await queue.Enqueue(async () => await EscribirEnArchivoAsync(a));
var total = await queue.Enqueue(() => Calcular());

IBackgroundTaskQueue y QueuedHostedService

Cola de trabajo asíncrono con descripción por tarea. Enqueue(task, description) añade trabajo; DequeueAsync(cancellationToken) espera hasta que hay una tarea. QueuedHostedService es el BackgroundService que las consume: regístralo junto a la cola.

builder.Services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
builder.Services.AddHostedService<QueuedHostedService>();

// En un servicio
queue.Enqueue(async ct => await ProcesarPedidoAsync(pedidoId, ct), "procesar-pedido");

int pendientes = queue.Count();
var resumen = queue.ResumenCola();   // descripción -> cuántas pendientes

Suscribible

Suscribible<T> envuelve un valor y ejecuta una acción cada vez que cambia: new Suscribible<int>(0, s => Console.WriteLine(s.Value)).

Conversiones de tipos

Métodos de extensión para convertir entre tipos sin TryParse a mano ni excepciones inesperadas.

FamiliaConvierte aEjemplos de uso
STRstringprecio.STR(), id.STR(), fecha.STR(). Formatea decimales y fechas en el formato que espera Dinaup y tolera nulos. estado.STR(LenguajeE.Spanish) traduce un enum con MultiLanguageDescriptionAttribute.
INTinttexto.INT(), texto.INT(0) (valor si falla), texto.INT(min, max, siFalla) (con límites).
ToDate y familiaDateTime, DateOnly, TimeOnlys.ToDate(), s.ToDateOnly(), variantes _Nullable y de zona horaria.
ToSQL y familiaLiteral SQLnombre.ToSQL(), fecha.ToSQL_Date(), importe.ToSQL_Decimal(), para concatenar en sentencias.
ToSHA1Hash hexadecimalToSHA1(bytes), ToSHA1(texto), ToSHA1(stream).
int cantidad = Request["cantidad"].INT(0);          // 0 si no es número
string fecha = DateTime.UtcNow.ToSQL_Date();          // literal para SQL
string hash  = Dinaup.extensions.ToSHA1(bytes);       // SHA-1 en hexadecimal

ToSQL produce literales para concatenar, no parámetros. Para valores que llegan del usuario, usa consultas parametrizadas.

Formateadores

MóduloQué hace
FormatHelpers.FormatDate(fecha) y FormatRelativeTime(fechaUtc)Fecha corta y tiempo relativo (hace 3 min).
FormatHelpers.FormatDurationFromSeconds(segundos), FormatMillisecondsReadable, FormatSeconds, FormatMinutesReadableDuraciones cortas legibles. Las variantes Html y Html20 devuelven marcado.
FormatHelpers.FormatHoursReadable, FormatDaysReadable, FormatMonthsReadable, FormatYearsReadableDuraciones largas legibles.
FormatHelpers.FormatSizeBytes(bytes), FormatSizeMegabytes, FormatSizeGigabytes, FormatGrams(gramos)Tamaños de archivo y pesos con su unidad.
FormatHelpers.FormatByRolePn(role, valor)Formatea un valor según el rol del campo Dinaup (RoleFieldE): moneda, porcentaje, peso.
FormatHelpers.TryParseMagicDateTime(texto, ref fecha)Interpreta fechas escritas de varias formas.
TimeZoneHelper.GetTimeZone(zoneId), ToUtcTime(fechaLocal, zoneId), ToLocalTime_PN(fechaUtc, zoneId), GetLocalDateTime(zoneId), GetLocalDateOnly(zoneId)Conversión entre UTC y una zona horaria IANA o Windows.
Utilities.Pricing.CalculatePrice(quantity, unitPrice, fixedDiscount, percentDiscount, hasTaxesIncluded, taxPercent) y ApplyDiscounts(price, fixedDiscount, percentDiscount)Precio de línea con descuento fijo, descuento porcentual e impuestos incluidos o no. Devuelve un CalculatedPrice.
Utilities.MentionParser.GetAllMentions(html)Los GUIDs mencionados en un comentario HTML.

Para formatear según la cultura y la zona horaria del usuario en una app web, usa CultureService. Ver SDK .NET.

Familias de extensiones

El módulo añade cientos de métodos de extensión. Estas son las familias que más se usan.

Vacío y nulos

  • IsEmpty(), IsNotEmpty(), HasContent(): sobre string, List, Dictionary, IEnumerable, Guid y fechas anulables.
  • IsNull(), IsNotNull(), NotEmpty(): sobre Guid y object.
  • OrEmpty(): devuelve una secuencia vacía si el origen es null, para iterar sin comprobar.
  • IfIsEmpty(valor): valor por defecto para números y fechas vacíos.
if (report.Rows.IsNotEmpty()) { /* hay filas */ }
foreach (var x in lista.OrEmpty()) { /* nunca NullReferenceException */ }

Predicados de texto

  • IsEmail(), IsGUID(), IsNumeric(), IsInteger(), IsJson(), IsDateOnly().
  • ContainsWord(), ContainsIgnoreCase(), EqualsIgnoreCase(), StartsWithIgnoreCase(), EndsWithIgnoreCase().
  • MatchAllWords(), MatchPercentage(), ContainsWebSearchMode(): búsqueda por palabras al estilo buscador.

LikeM

Atajo para preguntar si un valor es igual a alguno de una lista. Hay sobrecargas para int, decimal, Guid, char y string.

if (estado.LikeM(1, 2, 5)) { /* 1, 2 o 5 */ }
if (letra.LikeMIgnoreCase("A", "B", "C")) { /* a, b o c */ }

Guardas de argumentos

Lanzan una excepción descriptiva si el argumento no cumple.

  • ThrowIf_IsNull(), ThrowIf_IsEmpty(): sobre string, colecciones, Guid, números y fechas.
  • ThrowIf_IsNotGuid(), ThrowIf_IsNotEmail(), ThrowIf_IsNotUrl(), ThrowIf_IsZero(), ThrowIf_HasUpperCase().
email.ThrowIf_IsNotEmail(nameof(email));
sectionId.ThrowIf_IsEmpty(nameof(sectionId));

Colecciones

  • GetM(clave) sobre diccionarios: devuelve el valor o el valor por defecto si la clave no existe, sin lanzar. GetM(clave, defecto) fija ese defecto.
  • HasElements(), HasContent(): presencia sobre arrays, listas y diccionarios.
  • GetRandom(), MoveItemUp(), MoveItemDown(), AddRange() sobre HashSet.

Transformación y numéricas

  • Texto: RemoveAccents(), RemoveSpecialCharacters(), UppercaseFirst(), ParseBetween(inicio, fin), Trim_PN() (Trim que tolera nulos), FormatJson().
  • Numéricas: Between(min, max), IsMultipleOf(), RoundMoney(decimales), Half(), HasDecimalPlaces().

Las extensiones de petición HTTP (GetUserIP, ReadGet) están en ASP.NET Core.

En esta página