Saturday, July 31, 2010

Mandatory-Eigenschaft auf Formdatasource-Feldern mit deaktiviertem Konfigurationsschlüssel

Es gibt einen Designfehler mit der Mandatory-Eigenschaft des Formdatasource-Feldes. Wenn man es auf true setzt, wird auch auf den Feldinhalt geprüft, selbst wenn das Feld mit per Konfiguration deaktiviert ist.

Als Beispiel: Die Tabelle TableA hat zwei Felder: FieldA und FieldBFieldB hat als Konfigurationsschlüssel-Eigenschaft den Wert ConfigKeyA. Man erstellt nun ein Formular mit einer Datasource der Tabelle TableA und setzt die Mandatory-Eigenschaft von FieldB auf true. Dann deaktiviert man den Konfigurationsschlüssel ConfigKeyA im Menü Administration/Einstellungen/System/Konfiguration. Zuletzt startet man den Client neu, öffnet das erstellte Formular und versucht einen neuen Datensatz zu speichern. Der Speichervorgang wird fehlschlagen mit der Meldung, dass FieldB ausgefüllt werden muss.

Als Vorsicht beim Manipulieren der Mandatory-Eigenschaft auf  Formdatasource-Feldern.

Dies betrifft AX 2009, aber gut möglich dass das Fehlverhalten auch in früheren Versionen auftritt.

Mandatory falg on form data source field with disabled configuration key

There's a design bug with the form data source field property called mandatory. If you set it to true, the condition if the field is filled in will be checked even the field is deactivated by configuration.

As an example: Your table TableA has two fields: FieldA and FieldB. FieldB has configuration key property value ConfigKeyA. You create a form with a data source from table TableA. You set the data source field FieldB's property mandatory to true. Now you go to administration/settings/system/configuration and deactivate configuration key ConfigKeyA. At least restart client, open the created form and try to save a new record. It will fail with the message that field FieldB has to be filled in.

So be careful if manipulating the mandatory property on form data source fields.

This applies to AX 2009, but I have not tested if it applies to former Versions too.

Debugging von Tablemap-Code im Enterprise Portal

In AX 2009 (oder auch schon in AX 4.0, kann mich gerade nicht erinnern) ist es nicht möglich Ereigniscode von Steuerelementen auf Formularen zu debuggen.

Eine ähnliche Verhaltensweise besteht beim X++-Debuggen im Enterprise Portal von von Tablemap-Methoden. Der Debugger reagiert nicht auch wenn ein Breakpoint vorhanden ist. Die einzige Möglichkeit eine Reaktion des Debugger zu erzwingen ist die Verwendung der Anweisung 'breakpoint', die natürlich nach dem Debuggen wieder entfernt werden sollte.

Vielleicht betrifft dieser Effekt das Debuggen von X++-Tablemap-Code ausgeführt durch den .NET-Business-Connector ganz allgemein, und nicht nur das Enterprise Portal.

Wednesday, July 28, 2010

Debugging tablemap code trough Enterprise Portal

As a known fact (or bug), it's not possible to catch breakpoints in control events on forms since AX 2009 (or even AX 4.0 can't remember exactly).

A similar behaviour exists, if you try to debug X++ code of table maps methods in Enterprise Portal. The debugger won't react even if you have set a breakpoint. The only one method to force the degugger is to use the command 'breakpoint' in your code, which of course should be removed when finishing debugging.

May be this affects X++ table map code debugging executed by .NET Business Connector in general, not especially the Enterprise Portal.

Monday, July 19, 2010

Select Group By und Join Order By

Vorsicht ist geboten bei Select-Statements, welche Group By und Order By mischen. Zwar funktioniert die Abfrage auf der Datenbank korrekt, aber mit der Einbusse von Daten. Daten für Order By-Tabellen sind dann nämlich nicht verfügbar.

Unten stehendes Beispiel bringt die gewünschten Fälligkeitsdaten der Tabelle CustTransOpen, aber die Belege der Tabelle CustTrans fehlen:

CustTable custTable;
CustTrans custTrans;
CustTransOpen custTransOpen;
;

while select CustGroup from custtable group by CustGroup // group by
    join Voucher from custTrans // order by
         where custTrans.AccountNum == custtable.AccountNum
         join DueDate from custTransOpen group by DueDate // group by
              where custTransOpen.RefRecId == custTrans.RecId
{
    info(custTable.CustGroup); // works
    info(custTrans.Voucher); // works not
    info(Date2StrUsr(custTransOpen.DueDate)); // works
}

Wenn man der Tabelle CustTrans ein 'group by' hinzufügt, erhält man dann auch Daten:

CustTable custTable;
CustTrans custTrans;
CustTransOpen custTransOpen;
;

while select CustGroup from custtable group by CustGroup // group by
    join Voucher, RecId from custTrans group by Voucher, RecId // group by
         where custTrans.AccountNum == custtable.AccountNum
         join DueDate from custTransOpen group by DueDate // group by
              where custTransOpen.RefRecId == custTrans.RecId
{
    info(custTable.CustGroup); // works
    info(custTrans.Voucher); // works now
    info(Date2StrUsr(custTransOpen.DueDate)); works
}

Dieselbe Problematik besteht auch mit Datenabfragen die mit dem QueryRun-Objekt verarbeitet werden.

Es bleibt offen, ob die Ursache für dieses Problem bei AX oder an der SQL-Engine liegt; feststeht, dass man mit dem Mischen von Group By und Order By Bugs erzeugt, die man leider nicht so schnell herausfindet.

Thursday, July 15, 2010

Bitmanipulation with integer64

Take care on bit shifting if the result should be a 64 bit integer. Fragments like the follow code example does not take the obviously desired effect:
int64 i64 = 1 << 32; // move the very right positive bit just 32 ranks to the left
The result on that will be 0. The problem comes up with the return value from the operation 1 << 32, it's an 32 bit integer, even the result will be copied in a 64 bit integer (the bit shfiting operation is executed encapsulated before copy to variable i64). The base operand sets the type of result, in our code it's a constant number (1). And because 1 is a 32 bit integer, the results represents a 32 bit integer too. But why it ends with zero result? Its the reaction for the overflow when trying to access a bit area which is not available on a 32 bit integer (a 32 bit integer has just its 32 bits - not more, not less). Other programming languages reacts in the same situation with exceptions type overflow (f.ex. VB6) or moves the bits in circle (f.ex. C#), but AX quits that problem situation just with a type specified null value.

To ensure the code works fine, do it that way:
int64 base = 1;
int64 result = base << 32; // move the very right positive bit just 32 ranks to the left

Bitmanipulation mit Integer64

Aufgepasst bei Bitverschiebungen wenn das Ergebnis ein Integer 64 Bit Länge sein soll. Manipulationen wie etwas der nachstehend abgebildete Code bringen leider nicht das offensichtlich erwünschte Ergebnis:
int64 i64 = 1 << 32; // move the very right positive bit just 32 ranks to the left
Das Resultat draus ist nämlich 0. Das Problem liegt darin, dass der Rückgabewert der Operation 1 << 32 ein Integer 32 Bit Länge ist, auch wenn das ganze sogleich einem Integer 64 Bit Länge zugewiesen wird (die Bitverschiebung wird gekapselt ausgeführt bevor die Zuweisung an die Variable i64 erfolgt). Aber warum ist das Resultat 0? Der Basisoperand der Bitverschiebung bestimmt den Rückgabewert, in unserem Beispiel also eine konstante eins (1). Und weil eine 1 ein Integer 32 Bit Länge darstellt, ist auch das Ergebnis ein simpler Integer 32 Bit Länge. Das Resultat von 0 resultiert aus der Reaktion auf den Überlauf beim Zugriff auf ein Bit-Bereich den es auf einem Integers 32 Bit Länge nicht gibt (ein Integer 32 Bit Länge hat halt eben nur 32 Bits). Andere Programmiersprachen reagieren hier mit einer Ausnahme vom Typ Überlauf (z.B. VB6) oder schieben die Bits im Kreis herum (z.B. C#), AX beendet die Problemsituation schlicht mit einem typenspezifischen Nullwert.

Damit der Code also richtig funktioniert muss er so geschrieben werden:
int64 base = 1;
int64 result = base << 32; // move the very right positive bit just 32 ranks to the left