- data-modeling: tablerelation-field-length-must-match-related-field (exact length for unconditional relations, at least the longest target when all relations are conditional; no compile-time diagnostic) - testing: recordref-open-temp-parameter-defeats-real-table-checks (a temp-opened RecordRef is empty, so existence/uniqueness checks on it never see persisted rows; cross-links use-generateguid article) - Wire both into the review leaves and register the fixture pairs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
4.2 KiB
| bc-version | domain | keywords | technologies | countries | application-area | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
testing |
|
|
|
|
RecordRef.Open with Temp = true cannot check the real table
Description
RecordRef.Open(No: Integer [, Temp: Boolean] [, CompanyName: Text]) always takes a real table number. When Temp is true, though, the RecordRef refers to a temporary instance of that table. That instance starts empty and never contains the persisted rows. Microsoft Learn's example opens table 27 temporarily and notes that Find('-') "returns false" because "there are no records in a temporary table". So IsEmpty, Find*, Get or Count on a temp-opened RecordRef only sees rows the same code inserted into it. These calls can't answer "does this value already exist in the table?"
The defect is easy to miss at the call site. Temp is a positional, unnamed Boolean, the table number is genuine, and the code compiles without a diagnostic. The usual shape is a generate-until-unused loop: the code sets a range on a field and repeats until RecRef.IsEmpty(). That loop exits on its first pass and returns a value that may already exist. The defect then shows up later as a duplicate-key error or a wrong lookup, often only once the table holds data.
The shape occurs wherever RecordRef is used. This article sits in the testing domain because generic test-library helpers, which work on any table number, are where it typically appears. For how specific LibraryUtility helpers behave, including GenerateRandomCode's temporary open, see use-generateguid-for-unique-test-fixture-values.md. That article owns guidance on which helper to call.
Best Practice
When the code that follows has to see persisted rows, open with Temp = false or omit the parameter. Learn's first example omits it so the table "will not be open as temporary table".
Temp = true is correct when the RecordRef is deliberately a scratch instance that never answers existence questions about the real table, for example:
- A field-validation sandbox: insert a temporary row, then
Validatea field on it. - Parsing a table view with
SetViewand readingGetFilter. - Reading table metadata such as
SystemIdNo.
See sample: recordref-open-temp-parameter-defeats-real-table-checks.good.al.
Anti Pattern
RecRef.Open(<table>, true[, ...]) followed, on the same RecordRef and with no Insert into it, by IsEmpty, Find, FindFirst, FindSet, Get or Count whose result decides whether a value already exists in the table. The classic case is a uniqueness loop ending until RecRef.IsEmpty().
See sample: recordref-open-temp-parameter-defeats-real-table-checks.bad.al.
Source
- RecordRef.Open method: the syntax, Example 1 (parameters omitted, not temporary) and Example 2 (temporary open is empty,
Find('-')returns false). - BCApps
src/Layers/W1/Tests/ApplicationTestLibrary/LibraryUtility.Codeunit.al:GenerateRandomCodeopens withtrueat line 288 and loopsuntil RecRef.IsEmpty().GenerateRandomCodeWithLengthopens withfalseat line 309. - Legitimate
Temp = trueuses in BCApps W1:src/Layers/W1/BaseApp/Inventory/Item/ItemTempl.Table.alline 1255: temporaryIteminsert plusValidate.src/Apps/W1/Quality Management/app/src/Utilities/QltyFilterHelpers.Codeunit.alline 942:SetViewplusGetFilter.src/Layers/W1/BaseApp/Integration/SynchEngine/IntegrationRecordSynch.Codeunit.alline 221:SystemIdNo.