Data Access Patterns¶
There is no ORM. All data access goes through ADO.NET, in two coexisting styles, both of which build SQL as strings. Knowing both styles — and their shared weakness — is essential for reading and safely changing the code.
The gateway: HAKDAC (DataAccessControl)¶
The global HAKDAC (FrameWork.SmartSoft.General.DataAccessControl) is the primary data
gateway. Its surface:
| Method | Does |
|---|---|
GetConnectionString(server, db, uid, pwd) |
Builds the SQL-auth connection string. |
DataTable(ConnStr, SQL) |
Runs a query → DataTable. CommandTimeout = 0. On error returns Nothing, sets ErrMessage. |
DataFound(ConnStr, Table, Condition) |
SELECT count(*) … WHERE … > 0 existence check. |
GetDataTable(ConnStr, Table, Condition, Result) |
Single scalar via SqlDataReader. |
NonQuery(...) |
INSERT/UPDATE/DELETE; overloads for string, StringBuilder, SqlCommand (stored proc), or a list of statements. Logs failures to REF_ERRORLOG. |
Scalar / DataSet / DataReader |
Other read shapes. |
TableExist / ColumnExist / ProcedureExist |
Schema metadata checks. |
flowchart LR
Code["Form / Class code"] -->|"SQL string"| HAKDAC["HAKDAC.DataTable / NonQuery"]
HAKDAC -->|"SqlConnection · SqlCommand · SqlDataAdapter"| DB[("SQL Server")]
HAKDAC -->|"on failure"| LOG[("REF_ERRORLOG")]
style HAKDAC fill:#e8eaf6,stroke:#3f51b5
style DB fill:#e0f2f1,stroke:#00897b
Style 1 — Inline StringBuilder SQL¶
The most common pattern. SQL is concatenated with &, session/user values interpolated
directly, then run through HAKDAC or a raw SqlConnection/SqlCommand/SqlDataReader.
' Illustrative — the shape you will see everywhere
Dim sb As New StringBuilder
sb.Append("SELECT * FROM tbMaster_Barang ")
sb.Append("WHERE PRD_KODESUP = '" & pSupplier & "' ")
sb.Append(" AND PRD_AKTIF = 'Y' ")
sb.Append("ORDER BY PRD_NAMA")
Dim dt As DataTable = HAKDAC.DataTable(ConnStr, sb.ToString)
Style 2 — Typed data-access classes (BrowseData pattern)¶
The FrameWork provides ~380 typed classes (tbMaster_*, tbTr_*, tbHist_*, tv_*,
REF_*) under DataAccessControl. Each wraps one table/view. You set properties, then
call a verb:
' Illustrative
Dim oBarang As New SmartSoft.Inventory.FrameWork.DataAccess.tbMaster_Barang
oBarang.ConnStr = ConnStr
oBarang.Criteria = "PRD_KODESUP = '" & pSupplier & "'"
oBarang.OrderBy = "PRD_NAMA"
Dim dt As DataTable = oBarang.BrowseData()
| Verb | Purpose |
|---|---|
BrowseData() |
Query rows matching Criteria / OrderBy. |
GetDataByPrimaryKey() |
Load one record by its key. |
SaveData() / UpdateData() / DeleteData() |
Persist changes. |
AuditTrail() |
Write an audit entry (where wired up). |
These classes are organised into product bundles:
FrameWork.SmartSoft.DataAccess— ~129 shared classes.SmartSoft.Inventory.FrameWork.DataAccess— ~183 inventory classes (tbMaster_Barang,tv_BackOffice_D, …).SmartSoft.POS.FrameWork.DataAccess— ~48 POS classes.SmartSoft.Service.FrameWork.DataAccess— ~29 service classes.
The typed classes still build string SQL
The BrowseData classes look safer, but internally they also concatenate Criteria
into the SQL string. They are a convenience wrapper, not a parameterization layer.
The shared weakness: no parameterization¶
SQL injection exposure is system-wide
Neither style parameterizes queries. Session and user-entered values are
interpolated straight into SQL text. The only parameterized paths are the handful of
stored-procedure calls (NonQuery(SqlCommand) with CommandType.StoredProcedure) and
CekTable's @NamaFile. Everywhere else, a value containing a quote can break — or
rewrite — the query.
This is the established pattern, not an isolated bug. Treat every string-built query as a potential injection point, and see Known Issues for the bigger picture.
Conventions & gotchas¶
| Convention | Detail |
|---|---|
CommandTimeout = 0 |
No query timeout — used deliberately on long recompute/report queries. |
| Errors swallowed | DataTable(...) returns Nothing on failure; callers often Try/Catch → Nothing. Check for Nothing, don't assume a DataTable. |
REF_ERRORLOG |
NonQuery failures are logged here (minimal escaping only). |
Reuse HAKDAC |
Prefer the global gateway over opening ad-hoc SqlConnections where a helper exists. |
| Oracle mirror | MyLib.DACOracle mirrors the SQL helper for Oracle, but SQL Server is the deployed engine. |
How to add a data call (following the grain)¶
- Reading a list? Prefer the typed
BrowseDataclass for the table if one exists; otherwise inlineStringBuilder+HAKDAC.DataTable. - Writing? Use the transaction-class pattern (build an
ArrayListof statements, run as oneHAKDAC.NonQuerybatch) so the writes stay together — see Transactions & Posting. - Escape or validate any interpolated value you cannot avoid concatenating. Better
still, use a stored proc via
NonQuery(SqlCommand)for new sensitive writes. - Check for
Nothingafter everyDataTable(...)call.