bcquality/microsoft/knowledge/data-modeling/pictures-must-use-media-not-blob.md
Michael Dieringer 67962727f6 Fix ten focused correctness items plus sample links from Jesper's 2026-09-15 re-review
Six carried-over threads:
- api-page-least-privilege-write-access fixtures: added the mandatory
  EntityName/EntitySetName properties (AL0485).
- pages-must-not-contain-business-logic fixtures: Sales Line has no
  "Total Amount" field; replaced with the real "Line Amount" (field 103).
- test-feature-scenario-tags.good.al and test-one-when-per-test.good.al:
  CreatePriceHeader leaves a price list in Draft status, which price
  calculation ignores. Added Validate(Status, Active) + Modify before the
  sales line that depends on it. Verified Status field/enum against
  PriceListHeader.Table.al and PriceStatus.Enum.al in the BCApps clone.
- exposed-objects-must-be-in-a-permission-set.md: a published codeunit is
  a SOAP endpoint (SOAP is deprecated), not OData - Page/Query are the
  OData object types. Corrected and pointed new integrations at API
  pages/queries instead.
- al-error-handling-review.md: the log-writes-must-survive-rollback cue
  selected on Session.StartSession, which only appears in the compliant
  fix, never in the anti-pattern - the bad fixture could never be
  worklisted. Recued on the actual risk shape (log insert around a
  failed TryFunction/GetLastError* path, then raise/propagate), with
  StartSession as an explicit compliant discriminator instead.
- page-design-must-match-bc-page-type-conventions.md: the enum value is
  NavigatePage, not Navigate; noted the type list is a selected subset,
  not an exhaustive PageType catalogue (PromptDialog, ConfigurationDialog,
  UserControlHost, XmlPort also exist, out of this article's scope).

Four new correctness gaps:
- release-must-update-app-version.md: "the version is the only identity"
  was backwards - id is the app's stable identity, version identifies a
  release/code-state of it.
- defensive-vs-offensive-code-must-match-blast-radius.good.al: the "low
  blast radius" example had no else branch, so a failed Customer.Get()
  left the field at its prior/default value instead of the explicit
  chosen fallback the article claims to demonstrate. Added the else.
- bcpt-scenarios-must-be-app-specific.good.al: InitTest and both measured
  StartScenario/EndScenario sections were empty/comment-only, so the
  "app-specific" fixture measured no actual work. Filled in a real,
  self-contained header+line creation path.
- upgrade-tag-logic-must-not-nest-deeply.good.al: the flattened version
  dropped both safety conditions the bad fixture had (Discount % = 0,
  nonblank posting group), silently changing behavior instead of just
  removing nesting. Extracted the guarded update into a helper with both
  conditions preserved as early exits.

Also converted this PR's remaining plain-backtick "See sample:" sample
references (16 articles) to the READ-convention markdown-link form,
matching the fix already made on #156/#158.

Rebased onto upstream/main (conflicts in al-ui-review.md, al-style-review.md,
al-upgrade-review.md against merged upstream PRs - all additive, both
sides' worklist cues retained).
2026-09-21 22:44:17 +02:00

2.1 KiB

bc-version domain keywords technologies countries application-area
all
data-modeling
blob
media
mediaset
picture-field
image-field
table-design
al
w1
all

Pictures must be stored in a Media/MediaSet field, not BLOB

Description

BLOB is still a valid AL field type for arbitrary binary data, but it is not the right choice for storing pictures or images. The current recommendation is the Media field type for a single image, or MediaSet when a record needs several independent images (e.g. multiple product photos) — MediaSet is a collection of separately-imported media objects, each with its own identity; it does not generate resized variants or thumbnails on its own, and displaying more than one item still requires custom page handling. Media/MediaSet integrate with the platform's picture control and media repository, which a plain BLOB field does not — but any derived preview or thumbnail image still has to be generated explicitly and stored in its own field, regardless of which type holds the source image.

BLOB remains the correct choice for genuinely arbitrary binary payloads that are not images and don't benefit from the media pipeline (e.g. a raw file attachment blob unrelated to picture rendering).

Best Practice

Use Media for a single image, or MediaSet for multiple independent images, for any field that holds a picture.

See sample: pictures-must-use-media-not-blob.good.al.

Anti Pattern

A BLOB field named "Picture" compiles and stores the image bytes, but it misses the picture control integration and media repository that a Media/MediaSet field provides for free — the anti pattern is choosing BLOB for image storage out of habit rather than recognizing that the field is holding a picture, not generic binary data. A related anti pattern: assuming MediaSet gives automatic image variants or thumbnails because it sounds like a collection with derived versions — it is only a collection of independently-imported media objects.

See sample: pictures-must-use-media-not-blob.bad.al.