Restructure for Docsy theme
This commit is contained in:
parent
f4a1ebc077
commit
cceb0c945e
376 changed files with 963 additions and 186 deletions
197
content/docs/BestPractices/SubscriberCodeunits/index.md
Normal file
197
content/docs/BestPractices/SubscriberCodeunits/index.md
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
---
|
||||
title: "Subscriber Codeunits"
|
||||
tags: ["Performance"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by waldo, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
In general, subscribers have to be put in codeunits. There are a few performance considerations that you should keep in the back of your minds, when designing such a codeunit.
|
||||
|
||||
- Keep the codeunit as small as possible
|
||||
- Work with a single instance codeunit
|
||||
- only subscribe when necessary
|
||||
- Avoid generic OnInsert/OnModify/OnDelete
|
||||
|
||||
Let's discuss all points
|
||||
|
||||
## Keep the codeunit as small as possible
|
||||
Every time a subscriber gets called, a new instance of the codeunit is being loaded in memory, which takes memory and processing power. The smaller the codeunit, the less memory, and the faster it is.
|
||||
|
||||
Therefore, it's suggested to split the subscribers by functionality and avoid putting business logic in the actual codeunit. Tip: put all business logic in an "[Method Codeunit](https://alguidelines.dev/bcpatterns/generic-method-pattern/)".
|
||||
|
||||
Examples:
|
||||
- if you app does things on Sales and Purchase, create a Sales-subs codeunit, and a Purchase-subs.
|
||||
- if you have multiple functionalities in your app (let's call'm modules), create a subs-codeunit per module, and only add the subscribers in there that are necessary for that module.
|
||||
|
||||
### Bad code
|
||||
```AL
|
||||
codeunit 2037325 "Setup Subs"
|
||||
{
|
||||
SingleInstance = true;
|
||||
|
||||
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
|
||||
local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
|
||||
var
|
||||
AppId: ModuleInfo;
|
||||
NameListLbl: Label 'Linked Texts Framework - List', Locked = true;
|
||||
DescriptionListLbl: Label 'Edit linked texts', Locked = true;
|
||||
KeyWordListLbl: Label 'LT,Distri,Technical,Functional,Reports', Locked = true;
|
||||
NameReportLbl: Label 'Linked Texts Framework - Reports', Locked = true;
|
||||
DescriptionReportLbl: Label 'View linked texts reports', Locked = true;
|
||||
KeyWordReportLbl: Label 'LT,Distri,Technical,Functional,Reports', Locked = true;
|
||||
begin
|
||||
navapp.GetCurrentModuleInfo(AppId);
|
||||
Sender.Insert(NameListLbl, DescriptionListLbl, KeyWordListLbl, page::"LTE Linked Text List", AppId.Id(), "Manual Setup Category"::General);
|
||||
Sender.Insert(NameReportLbl, DescriptionReportLbl, KeyWordReportLbl, page::"LTE Linked Texts Reports", AppId.Id(), "Manual Setup Category"::General);
|
||||
end;
|
||||
|
||||
[EventSubscriber(ObjectType::Codeunit, codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
|
||||
local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
|
||||
var
|
||||
AppId: ModuleInfo;
|
||||
NameLayoutLbl: Label 'Report Helper - Layout', Locked = true;
|
||||
DescriptionLayoutLbl: Label 'Set up or update report layout list', Locked = true;
|
||||
KeyWordLayoutLbl: Label 'RH,Distri,Technical,Functional,Reports,Layout', Locked = true;
|
||||
NameCaptionsLbl: Label 'Report Helper - Captions', Locked = true;
|
||||
DescriptionCaptionsLbl: Label 'Set up or update captions list', Locked = true;
|
||||
KeyWordCaptionsLbl: Label 'RH,Distri,Technical,Functional,Reports,Captions', Locked = true;
|
||||
NameFunctionsLbl: Label 'Report Helper - Functions', Locked = true;
|
||||
DescriptionFunctionsLbl: Label 'Set up or disable functions', Locked = true;
|
||||
KeyWordFunctionsLbl: Label 'RH,Distri,Technical,Functional,Reports,Functions', Locked = true;
|
||||
NameDFCLbl: Label 'Report Helper - Default Footer', Locked = true;
|
||||
DescriptionDFCLbl: Label 'Set up or update default footer', Locked = true;
|
||||
KeyWordDFCLbl: Label 'RH,Distri,Technical,Functional,Reports,Default,Footer', Locked = true;
|
||||
begin
|
||||
navapp.GetCurrentModuleInfo(AppId);
|
||||
Sender.Insert(NameLayoutLbl, DescriptionLayoutLbl, KeyWordLayoutLbl, page::"RHE Report Layout List", AppId.Id(), "Manual Setup Category"::General);
|
||||
Sender.Insert(NameCaptionsLbl, DescriptionCaptionsLbl, KeyWordCaptionsLbl, page::"RHE Captions", AppId.Id(), "Manual Setup Category"::General);
|
||||
Sender.Insert(NameFunctionsLbl, DescriptionFunctionsLbl, KeyWordFunctionsLbl, page::"RHE Functions", AppId.Id(), "Manual Setup Category"::General);
|
||||
Sender.Insert(NameDFCLbl, DescriptionDFCLbl, KeyWordDFCLbl, page::"RHE Default Footer Card", AppId.Id(), "Manual Setup Category"::General);
|
||||
end;
|
||||
}
|
||||
```
|
||||
### Good code
|
||||
|
||||
Split into 2 codeunits, and move the business logic out.
|
||||
|
||||
```AL
|
||||
codeunit 2037325 "LTE Setup Subs"
|
||||
{
|
||||
SingleInstance = true;
|
||||
|
||||
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
|
||||
local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
|
||||
var
|
||||
RegisterLTEManualSetup: codeunit "Register LTE Manual Setup";
|
||||
begin
|
||||
RegisterLTEManualSetup.RegisterLTEManualSetup();
|
||||
end;
|
||||
}
|
||||
|
||||
codeunit 2037324 "RHE Setup Subs"
|
||||
{
|
||||
SingleInstance = true;
|
||||
|
||||
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
|
||||
local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
|
||||
var
|
||||
RegisterRHEManualSetup: codeunit "Register RHE Manual Setup";
|
||||
begin
|
||||
RegisterRHEManualSetup.RegisterRHEManualSetup();
|
||||
end;
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Work with a single instance codeunit
|
||||
|
||||
To avoid the extra "loading of the content" while a subscriber is being executed, use Single Instance codeunit for subscribers. Do take into account, of course, that it would share the state across the entire session.
|
||||
|
||||
### Bad code
|
||||
```AL
|
||||
codeunit 2037324 "RHE Setup Subs"
|
||||
{
|
||||
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
|
||||
local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
|
||||
var
|
||||
RegisterRHEManualSetup: codeunit "Register RHE Manual Setup";
|
||||
begin
|
||||
RegisterRHEManualSetup.RegisterRHEManualSetup();
|
||||
end;
|
||||
}
|
||||
```
|
||||
### Good code
|
||||
```AL
|
||||
codeunit 2037324 "RHE Setup Subs"
|
||||
{
|
||||
SingleInstance = true;
|
||||
|
||||
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
|
||||
local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
|
||||
var
|
||||
RegisterRHEManualSetup: codeunit "Register RHE Manual Setup";
|
||||
begin
|
||||
RegisterRHEManualSetup.RegisterRHEManualSetup();
|
||||
end;
|
||||
}
|
||||
```
|
||||
|
||||
## only subscribe when necessary
|
||||
|
||||
If possible, only execute the subscriber when really necessary by using Manual Binding.
|
||||
|
||||
### Bad code
|
||||
```AL
|
||||
//subscriber - code should actually only run when Color=Red.
|
||||
[EventSubscriber(ObjectType::Table, Database::"Just Some Table WLD", 'OnAfterValidateEvent', 'Message 2', false, false)]
|
||||
local procedure JustDoSomthing(var Rec: Record "Just Some Table WLD"; var xRec: Record "Just Some Table WLD")
|
||||
begin
|
||||
if Rec.color <> 'RED' then
|
||||
exit; //only execute when necessary
|
||||
|
||||
...
|
||||
end;
|
||||
|
||||
//business logic
|
||||
if JustSomeTable.FindSet() then
|
||||
repeat
|
||||
JustSomeTable.Validate("Message 2", format(Random(1000)));
|
||||
until JustSomeTable.Next() < 1;
|
||||
```
|
||||
### Good code
|
||||
```AL
|
||||
if JustSomeTable.FindSet() then
|
||||
repeat
|
||||
if JustSomeTable.Color = 'RED' then
|
||||
BindSubscription(DemoSubs);
|
||||
|
||||
JustSomeTable.Validate("Message 2", format(Random(1000)));
|
||||
|
||||
if JustSomeTable.Color = 'RED' then
|
||||
UnbindSubscription(DemoSubs);
|
||||
until JustSomeTable.Next() < 1;
|
||||
```
|
||||
|
||||
## Avoid OnInsert/OnModify/OnDelete
|
||||
The reason for this is, that it breaks the batch-calls:
|
||||
- Any "OnInsert" subscriber breaks the bulk inserts, simply because it needs to perform an operation after every record that was inserted
|
||||
- Any "OnModify" subscriber slows down the "ModifyAll", simply because it needs to perform an operation after every record that was modified. I fact: 1 SQL call is turned into a loop of SQL calls.
|
||||
- Any "OnDelete" subscriber slows down the "DeleteAll", simply because it needs to perform an operation after every record that was deleted. I fact: 1 SQL call is turned into a loop of SQL calls.
|
||||
|
||||
Avoid subscribers to these events.
|
||||
|
||||
## [Discussions](https://github.com/microsoft/alguidelines/discussions/92)
|
||||
|
||||
You can discuss this guidelines [here](https://github.com/microsoft/alguidelines/discussions/92).
|
||||
|
||||
You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
|
||||
|
||||
If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
|
||||
|
||||
## References
|
||||
|
||||
The [Generic Method Pattern](https://alguidelines.dev/bcpatterns/generic-method-pattern/)
|
||||
Loading…
Add table
Add a link
Reference in a new issue