101 lines
4.4 KiB
Markdown
101 lines
4.4 KiB
Markdown
# Reglas obligatorias de mapeo de resultados
|
|
|
|
Estas reglas complementan `AGENTS.md` y `AGENTS_STORED_PROCEDURES.md`.
|
|
|
|
## Extensión estándar
|
|
|
|
- Todo resultado obtenido con `IDataReader` debe convertirse mediante `AppCode/Utilities/DataReaderExtensions.cs`.
|
|
- No repetir bloques manuales como `Propiedad = Convert.ToInt32(reader["Columna"])` en repositorios o servicios.
|
|
- Resultados admitidos:
|
|
|
|
```csharp
|
|
T item = reader.ToObject<T>();
|
|
List<T> items = reader.ToList<T>();
|
|
DataTable table = reader.ToDataTable();
|
|
JArray json = reader.ToJsonArray();
|
|
```
|
|
|
|
- `ToObject<T>()` devuelve `null` cuando el procedimiento no devuelve filas.
|
|
- `ToList<T>()` devuelve una lista vacía cuando no hay filas; no devuelve `null`.
|
|
- `ToDataTable()` devuelve un `DataTable` vacío cuando no hay filas.
|
|
- `ToJsonArray()` devuelve un `JArray` vacío cuando no hay filas.
|
|
|
|
## Operaciones asíncronas
|
|
|
|
- Para código nuevo, preferir las versiones asíncronas cuando el proveedor exponga `DbCommand` y `DbDataReader`.
|
|
- Métodos disponibles: `ToObjectAsync<T>()`, `ToListAsync<T>()`, `ToDataTableAsync()` y `ToJsonArrayAsync()`.
|
|
- Los métodos asíncronos deben recibir y propagar un `CancellationToken` cuando la llamada superior disponga de uno.
|
|
- Abrir el lector con `ExecuteReaderAsync`; no envolver operaciones síncronas de base de datos en `Task.Run`.
|
|
- Usar `ConfigureAwait(false)` dentro de infraestructura, repositorios y servicios que no necesiten el contexto de WebForms.
|
|
- Cerrar o desechar el lector antes de leer `@ErrorMostrar`, `@ErrorTipo` y `@Return`.
|
|
- Una página WebForms que espere directamente debe declarar `Async="true"` y usar `RegisterAsyncTask` o un evento asíncrono compatible.
|
|
- Mantener los métodos síncronos únicamente para flujos existentes que todavía no se hayan migrado de extremo a extremo.
|
|
|
|
Ejemplo:
|
|
|
|
```csharp
|
|
List<UsuarioModel> usuarios;
|
|
|
|
using (DbDataReader reader = await command.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false))
|
|
{
|
|
usuarios = await reader.ToListAsync<UsuarioModel>(cancellationToken).ConfigureAwait(false);
|
|
}
|
|
|
|
StoredProcedureCommand.EnsureSuccess(command);
|
|
return usuarios;
|
|
```
|
|
|
|
## Convención de columnas y propiedades
|
|
|
|
- El mapeo se realiza por nombre de columna y propiedad sin distinguir mayúsculas/minúsculas.
|
|
- Los procedimientos deben usar alias para que las columnas coincidan con el modelo.
|
|
- Cuando no sea posible usar el mismo nombre, decorar la propiedad con `[Column("NombreColumna")]`.
|
|
- Las columnas adicionales se ignoran.
|
|
- Las propiedades sin columna conservan su valor predeterminado.
|
|
- `DBNull` se convierte en `null` para propiedades anulables o de referencia; para propiedades no anulables conserva el valor predeterminado.
|
|
- Se admiten tipos anulables, enumeraciones, `Guid`, `TimeSpan`, `DateTimeOffset`, booleanos y conversiones primitivas.
|
|
- Una conversión incompatible debe producir una excepción para que `DbExecutor` la registre; no ocultar errores de contrato entre el PA y el modelo.
|
|
|
|
## Uso correcto con procedimientos almacenados
|
|
|
|
- El repositorio crea y configura el comando mediante `StoredProcedureCommand`.
|
|
- El repositorio abre el lector dentro de `using`.
|
|
- Debe cerrar o desechar el lector antes de llamar `StoredProcedureCommand.EnsureSuccess(command)`, porque los parámetros OUTPUT se reciben al finalizar el lector.
|
|
- La extensión sólo mapea datos; no abre conexiones, no crea comandos, no captura excepciones y no decide mensajes para el usuario.
|
|
- Páginas ASPX, controles y master pages no deben manejar `IDataReader` directamente.
|
|
|
|
Ejemplo para un objeto:
|
|
|
|
```csharp
|
|
UserModel user;
|
|
|
|
using (IDataReader reader = command.ExecuteReader())
|
|
{
|
|
user = reader.ToObject<UserModel>();
|
|
}
|
|
|
|
StoredProcedureCommand.EnsureSuccess(command);
|
|
return user;
|
|
```
|
|
|
|
Ejemplo para una lista:
|
|
|
|
```csharp
|
|
List<MenuItemModel> items;
|
|
|
|
using (IDataReader reader = command.ExecuteReader())
|
|
{
|
|
items = reader.ToList<MenuItemModel>();
|
|
}
|
|
|
|
StoredProcedureCommand.EnsureSuccess(command);
|
|
return items;
|
|
```
|
|
|
|
## Elección del tipo de resultado
|
|
|
|
- Preferir modelos tipados para reglas de negocio, seguridad, usuarios, permisos, menús y módulos nuevos.
|
|
- Usar `DataTable` sólo para compatibilidad con componentes WebForms o migración de código heredado que realmente lo requiera.
|
|
- Usar `JArray` únicamente cuando el contrato de salida sea JSON dinámico y no exista un modelo estable.
|
|
- No convertir primero a `DataTable` para después crear objetos o JSON; mapear directamente desde `IDataReader`.
|