You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: NeoReports-Decisoes.md
+14Lines changed: 14 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -203,3 +203,17 @@ Removido da v1 vs. Cap. 16: `IRetryPolicy`, `IExceptionClassifier`, `IAuthProvid
203
203
**Decisão.**`ReportBuilder<TRow>` é genérico **apenas** sobre o tipo de linha final `TRow`. O mapeamento de um tipo de origem diferente é expresso por overloads `From<TSource>(IBatchSource<TSource>, Func<TSource,TRow>)` / `From<TSource>(IStreamingSource<TSource>, Func<TSource,TRow>)`, que adaptam a source via `MappingBatchSource`/`MappingStreamingSource`.
204
204
205
205
**Por quê.** Um passo `Map<TOut>` que troca o tipo do builder quebraria o padrão de registro `AddReport<TRow>("nome", Action<ReportBuilder<TRow>>)` (a lambda continuaria num builder de outro tipo enquanto o registro buildaria o original). O overload de `From` entrega a mesma capacidade ("Map para um tipo de saída" da spec) sem essa armadilha e sem segundo parâmetro genérico no builder. Colunas são declaradas com `.Column(v => v.X, "Header")` (infere `ColumnType` do tipo do membro) ou `Columns(Col(...))`.
- Entrada fluente: `Source.Sql(connectionString, sql).Keyset<T,TKey>(v => v.Id, pageSize: 1000)`. O primeiro parâmetro é a **connection string** na v1; resolução por **nome de conexão** (config/DI, como o `"vendas-db"` da spec) fica pós-MVP.
213
+
- A query é responsabilidade do autor e **deve** expor um parâmetro `@cursor` na coluna-chave e ordenar por ela — padrão recomendado `WHERE (@cursor IS NULL OR Id > @cursor) ORDER BY Id`. Primeira página manda `@cursor = NULL`.
214
+
-**Conexão aberta/fechada por página** (D2/D3). O **cursor** é a última chave da página serializada como `string?` (`BatchResult.NextCursor`); `HasMore` só é verdadeiro quando a página encheu (`Count == pageSize`) e há última chave.
215
+
-**Bind de parâmetros defensivo:** só são adicionados ao comando os parâmetros que a query realmente referencia (varredura do texto por `@nome`), evitando "too many parameters"; parâmetros de execução (run-time) sobrepõem os estáticos sem duplicar.
216
+
-**Materialização** (`RecordMaterializer<T>`): preferir o construtor mais longo do POCO (records posicionais), casando parâmetros↔colunas por nome (case-insensitive); fallback para construtor vazio + propriedades setáveis. Ordinais de coluna mapeados por nome.
217
+
- O `Schema` declarado pela source é um placeholder mínimo — a projeção da pipeline usa as colunas do builder (D1), não o schema da source.
218
+
219
+
**Por quê.** Keyset com `@cursor` opaco e conexão-por-página atende CA-2 (lê todas as páginas em ordem, sem pular/repetir) e mantém memória constante, deixando checkpoint/multi-worker viáveis depois sem rework. Connection-by-name é açúcar de configuração que não muda o contrato — corta da v1.
-**Aceite:** CA-2, CA-4, CA-7. Report de referência roda fim-a-fim em CSV+Local. SQL testado com Testcontainers.
27
+
-[x]`Source.Sql(...).Keyset(key, pageSize)` — `IBatchSource<T>`, conexão por página, cursor `string?`, parâmetros parametrizados (bind automático só do que a query referencia).
28
+
-[x]`Format.Csv(...)` — writer não-genérico (delimitador, encoding, cabeçalho via `DisplayName`, formatação por cultura/format, escaping RFC 4180, CRLF, UTF-8 sem BOM).
0 commit comments