Hi Sebastian,
the short answer is: You can't. By design. What you can do, however, is to do a lock on a proxy record (reading and modifying it) before you access the real target records. We do this to avoid conflicts with our background and longer-running worker process. But this is only an option if you use new tables for your functionality, not standard NAV ones. This actually is our decoupling from NAV business logic: WS->lock the proxy record -> write to (and also lock) buffer tables, Other WS -> lock the proxy record -> lock the buffer records that need procesing -> do the processing and insert warehouse receipts.
The proxy record ensures exclusive access as long as you have successfully modified the proxy record in the first place. The catch is, you need all code accessing the desired tables to adhere to this protocol. But it successfully prevents critical external transfers from failing in the wrong way. It should always fail on gaining access to the proxy record, and since this isn't an exception, we can return a "false" or omething like this back to BizTalk and it doesnt count it as transferred for whatever strange reason.
The function to do the proxy lock is not trivial:
PROCEDURE LockProcessingBuffer@1140009(VAR ProcessingBuffer@1140000 : Record <CENSORED>) : Boolean;
VAR
L_Session@1140002 : Record 2000000009;
L_RetryCount@1140001 : Integer;
BEGIN
// write your own Session-ID - this locks the record for other sessions as long as we haven't committed the transaction.
L_Session.SETRANGE("My Session",TRUE);
L_Session.FINDFIRST;
L_RetryCount := 0;
REPEAT
ProcessingBuffer.InUseBySessionID := L_Session."Connection ID";
//Insert und auch Modify sind bereits ein implizites Locktable.
IF NOT ProcessingBuffer.INSERT THEN BEGIN
SELECTLATESTVERSION;
IF NOT ProcessingBuffer.MODIFY THEN BEGIN
SELECTLATESTVERSION;
ProcessingBuffer.LOCKTABLE;
ProcessingBuffer.GET(ProcessingBuffer.TableID);
ProcessingBuffer.InUseBySessionID := L_Session."Connection ID";
IF ProcessingBuffer.MODIFY THEN;
END;
END;
IF NOT ProcessingBuffer.GET(ProcessingBuffer.TableID) THEN
ProcessingBuffer.InUseBySessionID := 0;
L_RetryCount += 1;
UNTIL (ProcessingBuffer.InUseBySessionID = L_Session."Connection ID") OR (L_RetryCount > 10);
EXIT(L_RetryCount <=10)
END;
This function looks really strange, but it simply operates on the assumption that any of the C/AL instructions are not atomic and can be interrupted and some other process running C/AL gets what we wanted to achieve. The one true unique identifier is Connection ID - that doesn't change for the session. I'm not entirely sure if it works that way on NAV2013 and above. If not, use a GUID created in the function which is "sufficiently" unique.
The only true and verifiable lock you can get in C/SIDE is when you can successfully insert or modify a record (bulk inserts might be a problem, though).
with best regards
Jens