UNPKG

bc-code-intelligence-mcp

Version:

BC Code Intelligence MCP Server - Complete Specialist Bundle with AI-driven expert consultation, seamless handoffs, and context-preserving workflows

427 lines (354 loc) 12.1 kB
# Type-Safe Operations Sample ## Overview This sample demonstrates type-safe operation patterns in AL including generic procedures, interface-based type safety, and compile-time type validation techniques. ## Generic Type-Safe Procedures ```al codeunit 50400 "Type Safe Operations" { procedure SafeGetRecord<T>(RecordNo: Code[20]; var TargetRecord: Record T): Boolean begin TargetRecord.SetRange("No.", RecordNo); exit(TargetRecord.FindFirst()); end; procedure SafeUpdateField<T>(var TargetRecord: Record T; FieldNo: Integer; NewValue: Variant): Boolean var RecordRef: RecordRef; FieldRef: FieldRef; begin RecordRef.GetTable(TargetRecord); if not RecordRef.FieldExist(FieldNo) then exit(false); FieldRef := RecordRef.Field(FieldNo); if not ValidateFieldType(FieldRef, NewValue) then exit(false); FieldRef.Value := NewValue; RecordRef.SetTable(TargetRecord); exit(TargetRecord.Modify(true)); end; local procedure ValidateFieldType(FieldRef: FieldRef; NewValue: Variant): Boolean begin case FieldRef.Type of FieldType::Code: exit(NewValue.IsCode); FieldType::Text: exit(NewValue.IsText); FieldType::Integer: exit(NewValue.IsInteger); FieldType::Decimal: exit(NewValue.IsDecimal); FieldType::Boolean: exit(NewValue.IsBoolean); FieldType::Date: exit(NewValue.IsDate); FieldType::DateTime: exit(NewValue.IsDateTime); else exit(false); end; end; } ``` ## Interface-Based Type Safety ```al interface "Type Safe Validator" { procedure Validate(Value: Variant): Boolean; procedure GetValidationMessage(): Text; } codeunit 50401 "Customer Validator" implements "Type Safe Validator" { procedure Validate(Value: Variant): Boolean var Customer: Record Customer; begin if not Value.IsRecord then exit(false); Customer := Value; exit(ValidateCustomer(Customer)); end; procedure GetValidationMessage(): Text begin exit('Customer validation failed'); end; local procedure ValidateCustomer(Customer: Record Customer): Boolean begin if Customer."No." = '' then exit(false); if Customer.Name = '' then exit(false); if Customer.Blocked <> Customer.Blocked::" " then exit(false); exit(true); end; } codeunit 50402 "Item Validator" implements "Type Safe Validator" { procedure Validate(Value: Variant): Boolean var Item: Record Item; begin if not Value.IsRecord then exit(false); Item := Value; exit(ValidateItem(Item)); end; procedure GetValidationMessage(): Text begin exit('Item validation failed'); end; local procedure ValidateItem(Item: Record Item): Boolean begin if Item."No." = '' then exit(false); if Item.Description = '' then exit(false); if Item.Type = Item.Type::" " then exit(false); exit(true); end; } ``` ## Type-Safe Data Processing ```al codeunit 50403 "Type Safe Data Processor" { procedure ProcessTypedData<T>(var DataRecord: Record T; Processor: Interface "Data Processor"): Boolean var RecordVariant: Variant; begin RecordVariant := DataRecord; if not Processor.CanProcess(RecordVariant) then exit(false); if not Processor.Process(RecordVariant) then exit(false); DataRecord := RecordVariant; exit(true); end; procedure BatchProcessRecords<T>(var RecordSet: Record T; Processor: Interface "Data Processor"): Integer var ProcessedCount: Integer; CurrentRecord: Record T; begin RecordSet.FindSet(); repeat CurrentRecord := RecordSet; if ProcessTypedData(CurrentRecord, Processor) then ProcessedCount += 1; until RecordSet.Next() = 0; exit(ProcessedCount); end; } interface "Data Processor" { procedure CanProcess(Data: Variant): Boolean; procedure Process(var Data: Variant): Boolean; procedure GetProcessorName(): Text; } ``` ## Type-Safe Configuration Pattern ```al codeunit 50404 "Type Safe Config Manager" { procedure GetConfigValue<T>(ConfigKey: Code[50]): T var ConfigRecord: Record "Type Safe Configuration"; ResultValue: T; ConfigVariant: Variant; begin if not ConfigRecord.Get(ConfigKey) then Error('Configuration key %1 not found', ConfigKey); ConfigVariant := ConfigRecord.Value; if not TryConvertToType<T>(ConfigVariant, ResultValue) then Error('Cannot convert configuration value to requested type for key %1', ConfigKey); exit(ResultValue); end; procedure SetConfigValue<T>(ConfigKey: Code[50]; Value: T): Boolean var ConfigRecord: Record "Type Safe Configuration"; ValueVariant: Variant; begin ValueVariant := Value; if ConfigRecord.Get(ConfigKey) then ConfigRecord.Modify() else begin ConfigRecord.Init(); ConfigRecord."Config Key" := ConfigKey; ConfigRecord.Insert(); end; ConfigRecord.Value := ValueVariant; ConfigRecord."Data Type" := GetVariantType(ValueVariant); exit(ConfigRecord.Modify(true)); end; [TryFunction] local procedure TryConvertToType<T>(SourceVariant: Variant; var TargetValue: T) var TempVariant: Variant; begin TempVariant := SourceVariant; TargetValue := TempVariant; end; local procedure GetVariantType(Value: Variant): Enum "Configuration Data Type" begin case true of Value.IsBoolean: exit("Configuration Data Type"::Boolean); Value.IsInteger: exit("Configuration Data Type"::Integer); Value.IsDecimal: exit("Configuration Data Type"::Decimal); Value.IsText: exit("Configuration Data Type"::Text); Value.IsCode: exit("Configuration Data Type"::Code); Value.IsDate: exit("Configuration Data Type"::Date); Value.IsDateTime: exit("Configuration Data Type"::DateTime); else exit("Configuration Data Type"::Variant); end; end; } ``` ## Type-Safe Event Handling ```al codeunit 50405 "Type Safe Event Manager" { procedure RegisterEventHandler<T>(EventName: Code[50]; Handler: Interface "Typed Event Handler") var EventRegistration: Record "Event Handler Registration"; begin EventRegistration.Init(); EventRegistration."Event Name" := EventName; EventRegistration."Handler Type" := GetTypeName<T>(); EventRegistration."Handler Interface" := Handler; EventRegistration.Active := true; EventRegistration.Insert(true); end; procedure RaiseTypedEvent<T>(EventName: Code[50]; EventData: T): Boolean var EventRegistration: Record "Event Handler Registration"; Handler: Interface "Typed Event Handler"; EventVariant: Variant; HandlerExecuted: Boolean; begin EventVariant := EventData; EventRegistration.SetRange("Event Name", EventName); EventRegistration.SetRange("Handler Type", GetTypeName<T>()); EventRegistration.SetRange(Active, true); if EventRegistration.FindSet() then repeat Handler := EventRegistration."Handler Interface"; if Handler.HandleEvent(EventVariant) then HandlerExecuted := true; until EventRegistration.Next() = 0; exit(HandlerExecuted); end; local procedure GetTypeName<T>(): Text var TypeHelper: Codeunit "Type Helper"; DummyValue: T; DummyVariant: Variant; begin DummyVariant := DummyValue; exit(TypeHelper.GetObjectName(DummyVariant)); end; } interface "Typed Event Handler" { procedure HandleEvent(EventData: Variant): Boolean; procedure GetHandlerName(): Text; } ``` ## Type-Safe Collection Operations ```al codeunit 50406 "Type Safe Collections" { procedure CreateTypedList<T>(): List of [T] var TypedList: List of [T]; begin exit(TypedList); end; procedure AddToTypedList<T>(var TypedList: List of [T]; Item: T): Boolean begin TypedList.Add(Item); exit(true); end; procedure FilterTypedList<T>(var TypedList: List of [T]; FilterFunc: Interface "List Filter"): List of [T] var FilteredList: List of [T]; Item: T; ItemVariant: Variant; begin foreach Item in TypedList do begin ItemVariant := Item; if FilterFunc.ShouldInclude(ItemVariant) then FilteredList.Add(Item); end; exit(FilteredList); end; procedure TransformTypedList<TSource, TTarget>(SourceList: List of [TSource]; TransformFunc: Interface "List Transform"): List of [TTarget] var TargetList: List of [TTarget]; SourceItem: TSource; SourceVariant: Variant; TargetVariant: Variant; TargetItem: TTarget; begin foreach SourceItem in SourceList do begin SourceVariant := SourceItem; if TransformFunc.Transform(SourceVariant, TargetVariant) then begin TargetItem := TargetVariant; TargetList.Add(TargetItem); end; end; exit(TargetList); end; } interface "List Filter" { procedure ShouldInclude(Item: Variant): Boolean; } interface "List Transform" { procedure Transform(Source: Variant; var Target: Variant): Boolean; } ``` ## Type-Safe Error Handling ```al codeunit 50407 "Type Safe Error Handler" { procedure TryOperation<TInput, TOutput>(Input: TInput; Operation: Interface "Typed Operation"; var Output: TOutput): Boolean var InputVariant: Variant; OutputVariant: Variant; OperationResult: Boolean; begin InputVariant := Input; if not Operation.Validate(InputVariant) then exit(false); OperationResult := Operation.Execute(InputVariant, OutputVariant); if OperationResult then Output := OutputVariant; exit(OperationResult); end; procedure ExecuteWithFallback<T>(PrimaryOperation: Interface "Typed Operation"; FallbackOperation: Interface "Typed Operation"; Input: T): T var Output: T; begin if TryOperation(Input, PrimaryOperation, Output) then exit(Output); if TryOperation(Input, FallbackOperation, Output) then exit(Output); Error('All operations failed for input type %1', GetTypeName<T>()); end; } interface "Typed Operation" { procedure Validate(Input: Variant): Boolean; procedure Execute(Input: Variant; var Output: Variant): Boolean; procedure GetOperationName(): Text; } ``` This sample demonstrates comprehensive type-safe operation patterns including generic procedures, interface-based validation, typed collections, configuration management, event handling, and error handling with compile-time type safety.