mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-08-06 17:36:53 +01:00
Complete AL review knowledge readiness (#108)
* Complete AL review knowledge readiness Fill telemetry and Query coverage, strengthen thin review domains, correct audited content defects, and add deterministic cheap-model evaluation and reference-integrity safeguards. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9825b012-e653-496a-9310-c1f4b6f8ac27 * Generalize review fixture discovery Derive smoke cases from the leaf, domain, and paired-sample conventions so new leaves require no scoring-contract changes. Keep only exceptional selection/context overrides and fail when retrieval metadata cannot rank the selected article. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9825b012-e653-496a-9310-c1f4b6f8ac27 * Preserve published field IDs in sample Keep the existing Email and Contact Email field IDs unchanged, clarify that the sample represents an independent baseline, and use a local breaking-change rule for the generic smoke evaluation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9825b012-e653-496a-9310-c1f4b6f8ac27 * Clarify published field identity rules State explicitly that a published field keeps its ID, name, and type while a replacement is added as a separate field under an unused ID. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9825b012-e653-496a-9310-c1f4b6f8ac27 * Align field obsoletion sample baselines Use Email field ID 3 as the shared baseline so the bad example demonstrates a same-ID rename while the good example retains the original field and adds a separate replacement. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9825b012-e653-496a-9310-c1f4b6f8ac27 --------- Co-authored-by: Jesper Schulz-Wedde <jesper.schulzwedde@microsoft.com>
This commit is contained in:
parent
ae04938c03
commit
186d8a1314
105 changed files with 2229 additions and 212 deletions
|
|
@ -0,0 +1,26 @@
|
|||
table 50441 "Source Media Bad"
|
||||
{
|
||||
fields
|
||||
{
|
||||
field(1; Code; Code[20]) { }
|
||||
field(10; Pictures; MediaSet) { }
|
||||
}
|
||||
}
|
||||
|
||||
table 50442 "Target Media Bad"
|
||||
{
|
||||
fields
|
||||
{
|
||||
field(1; Code; Code[20]) { }
|
||||
field(20; Pictures; MediaSet) { }
|
||||
}
|
||||
}
|
||||
|
||||
codeunit 50443 "Share Media Bad"
|
||||
{
|
||||
procedure CopyPictures(Source: Record "Source Media Bad"; var Target: Record "Target Media Bad")
|
||||
begin
|
||||
Target.Pictures := Source.Pictures;
|
||||
Target.Modify(true);
|
||||
end;
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
table 50438 "Source Media Good"
|
||||
{
|
||||
fields
|
||||
{
|
||||
field(1; Code; Code[20]) { }
|
||||
field(10; Pictures; MediaSet) { }
|
||||
}
|
||||
}
|
||||
|
||||
table 50439 "Target Media Good"
|
||||
{
|
||||
fields
|
||||
{
|
||||
field(1; Code; Code[20]) { }
|
||||
field(20; Pictures; MediaSet) { }
|
||||
}
|
||||
}
|
||||
|
||||
codeunit 50440 "Share Media Good"
|
||||
{
|
||||
procedure CopyPictures(Source: Record "Source Media Good"; var Target: Record "Target Media Good")
|
||||
var
|
||||
Index: Integer;
|
||||
begin
|
||||
for Index := 1 to Source.Pictures.Count() do
|
||||
Target.Pictures.Insert(Source.Pictures.Item(Index));
|
||||
Target.Modify(true);
|
||||
end;
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: data-modeling
|
||||
keywords: [mediaset, media, insert, field-assignment, tenant-media, delete-integrity, sharing]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Share MediaSet items with Insert instead of field assignment
|
||||
|
||||
## Description
|
||||
|
||||
`Media` and `MediaSet` fields store IDs that reference tenant media system tables. When a record is deleted, the runtime looks for other references only in the same table and field index; it does not scan every table. Directly assigning a media-set field between different table types copies the ID without registering a separate media-set reference, so deleting one record can remove media that the other record still appears to reference.
|
||||
|
||||
## Best Practice
|
||||
|
||||
When sharing media between different tables, iterate the source `MediaSet` and call `Target.MediaSetField.Insert(Source.MediaSetField.Item(Index))`, then modify the target record. Direct field assignment is safe only when source and target are the same record subtype and use the same field ID. This concern is about reference/delete integrity, not the separate performance cost of `ModifyAll` on tables with media fields.
|
||||
|
||||
See sample: `share-mediaset-items-with-insert-not-field-assignment.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
`Target.Picture := Source.Picture;` where the two variables refer to different table types or different media-field IDs. The code copies an opaque ID, but the platform does not know that two independent fields now share the media object.
|
||||
|
||||
See sample: `share-mediaset-items-with-insert-not-field-assignment.bad.al`.
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
enum 50434 "Relation Type Bad"
|
||||
{
|
||||
Extensible = true;
|
||||
|
||||
value(0; Customer) { }
|
||||
}
|
||||
|
||||
table 50435 "Related Entity Bad"
|
||||
{
|
||||
fields
|
||||
{
|
||||
field(1; Type; Enum "Relation Type Bad") { }
|
||||
field(2; "Related No."; Code[20])
|
||||
{
|
||||
// This unconditional relation wins before extension branches run.
|
||||
TableRelation = Customer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enumextension 50436 "Relation Type Bad Ext" extends "Relation Type Bad"
|
||||
{
|
||||
value(10; Resource) { }
|
||||
}
|
||||
|
||||
tableextension 50437 "Related Entity Bad Ext" extends "Related Entity Bad"
|
||||
{
|
||||
fields
|
||||
{
|
||||
modify("Related No.")
|
||||
{
|
||||
TableRelation = if (Type = const(Resource)) Resource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
enum 50430 "Relation Type Good"
|
||||
{
|
||||
Extensible = true;
|
||||
|
||||
value(0; Customer) { }
|
||||
value(1; Item) { }
|
||||
}
|
||||
|
||||
table 50431 "Related Entity Good"
|
||||
{
|
||||
fields
|
||||
{
|
||||
field(1; Type; Enum "Relation Type Good") { }
|
||||
field(2; "Related No."; Code[20])
|
||||
{
|
||||
TableRelation =
|
||||
if (Type = const(Customer)) Customer
|
||||
else if (Type = const(Item)) Item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enumextension 50432 "Relation Type Resource" extends "Relation Type Good"
|
||||
{
|
||||
value(10; Resource) { }
|
||||
}
|
||||
|
||||
tableextension 50433 "Related Entity Resource" extends "Related Entity Good"
|
||||
{
|
||||
fields
|
||||
{
|
||||
modify("Related No.")
|
||||
{
|
||||
TableRelation = if (Type = const(Resource)) Resource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: data-modeling
|
||||
keywords: [tablerelation, tableextension, enumextension, additive, top-down, unconditional-relation]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Design TableRelation branches for additive top-down extension
|
||||
|
||||
## Description
|
||||
|
||||
A `tableextension` can add to an existing `TableRelation`, but the combined relation is evaluated top-down after the original value. The first unconditional relation wins. An extension branch appended after an unconditional base relation is therefore unreachable, even though the extension compiles and appears to describe the new enum value correctly.
|
||||
|
||||
## Best Practice
|
||||
|
||||
When a relation is designed to follow an extensible enum, express the base cases as conditional branches and leave no unconditional catch-all ahead of future extension branches. An enum extension can then append a condition for its new value. When extending a field you do not own, inspect the original `TableRelation`; do not claim that an appended condition overrides an unconditional relation.
|
||||
|
||||
See sample: `table-relation-extensions-are-additive-and-top-down.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
A base field has an unconditional `TableRelation = Customer;` and a `tableextension` adds `if (Type = const(Resource)) Resource`. The original unconditional branch always wins, so the new enum value still validates and looks up against Customer. The concern is evaluation order, not `ValidateTableRelation`; free-form input is covered separately by security guidance.
|
||||
|
||||
See sample: `table-relation-extensions-are-additive-and-top-down.bad.al`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue