Skip to Content
0

DataTable.Rows.Remove kills SAP

Apr 27 at 09:41 AM

60

avatar image

I am using SAP 9.2 PL03, C# UI and DI API and trying to delete rows from a Grid's DataTable, but every time I call

Grid oGrid = (Grid)oForm.Items.Item(...ItemID).Specific;
oGrid.DataTable.Rows.Remove(2); // Index does not matter, tried 1 too

SAP just stops working (placed a breakpoint just before the call, the it works until I do call it, and I made sure I have existing rows in the DataTable --> I added 10 rows and tried to delete the first and second).

I tried doing oForm.Freeze(true) before the deletion, but without success, and I tried it during and ItemPress and a Form Open event too. The executing Thread right before the call is MTA (Console.WriteLine( System.Threading.Thread.CurrentThread.GetApartmentState() ;)

Edit:

I could catch the exception:

The server threw an exception. (Exception from HRESULT: 0x80010105 (RPC_E_SERVERFAULT))

Edit2:

I explicitly ran the code on STA:

Thread thread = new Thread(RunOnSTA);
thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
thread.Start();
thread.Join();

private void RunOnSTA()
{
    try
    {
        Grid oGrid = (Grid)oForm.Items.Item(....ItemID).Specific;
        oGrid.DataTable.Rows.Remove(2);
    }  
    catch (Exception e)
    {
        Console.Write("asd");
    }
}

Still crashes the SAP client.

Should I just give up and implement my own function where I get the DataTable as an XML string, Parse/Delete the row and Load it back from XML?

This kind of works, but messes up checkboxes for me:

public static void RemoveRowFromGridUsingXML(Grid oGrid, int rowIndex)
{
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(oGrid.DataTable.SerializeAsXML(BoDataTableXmlSelect.dxs_DataOnly));

    XmlNode node = doc.SelectSingleNode($"/DataTable/Rows/Row[{rowIndex}]");
    if (node != null)
    {
        XmlNode parent = node.ParentNode;
        parent.RemoveChild(node);
        string newXML = doc.OuterXml;
        oGrid.DataTable.LoadSerializedXML(BoDataTableXmlSelect.dxs_DataOnly, newXML);
    }
}
10 |10000 characters needed characters left characters exceeded
* Please Login or Register to Answer, Follow or Comment.

2 Answers

Best Answer
Szabolcs Kelemen May 07 at 01:48 PM
0

Okay, It seems like If I do not set the rowheaders (oGrid.RowHeaders.SetValue(i, "indexVal+1")) and try to insert new rows and move around rows at the same time, the next Rows.Delete will fail. (Details in my comments)

Share
10 |10000 characters needed characters left characters exceeded
ANKIT CHAUHAN
May 02 at 01:58 PM
0

Hi Szabolcs Kelemen,

1. Have you tested the same on the latest available version and patch?

2. Can this be reproduced on the DEMO Database on the latest available version and patch?

3. What are the exact steps to reproduce the issue?

4. Are you removing the row from the Grid once the Grid is populated with the data from the query?

5. Any other information about this issue?

Kind regards,

ANKIT CHAUHAN

SAP SME Support

Show 6 Share
10 |10000 characters needed characters left characters exceeded

1. I haven't, the company I am developing for only has 9.2 PL03 available and no one mentioned version updates in the near future. Also, there are two other addon running, whose developers should be notified too, making this really hard for me.

2. I could not find the DEMO database in our current system. Do you have any resource about the DEMO database installation?

3,4. I made a new custom form using SAP Studio, saved the form as XML. In my addon, I place a new button on some system forms when they are opened. These buttons trigger the my form's opening, which basically means I load the XML and show it using FormCreationParams and Application.Forms.AddEx(). The Grid is empty by default and has a DataTable as a resource (not query based, manual columns). After the form is opened, I manually trigger a few row writes (I first add 8 rows, then manually create a few models and call WriteToGridRow on my model, which implements the method to set cell values on the grid):

public void WriteToGridRow(Grid oGrid, RowIndex rowIndex)
{
    DataTable oDataTable = oGrid.DataTable;

    if (oDataTable.Rows.Count <= rowIndex.AsZeroBased())
    {
        throw new InvalidOperationException($"No such a row exists. RowIndex (Zero based):{rowIndex.IndexAsZeroBased}, DT.Count: {oDataTable.Rows.Count}");
    }
    
    int i = rowIndex.IndexAsZeroBased;
    
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_ISCHECKED, i, IsChecked ? "Y" : "N");
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_EXISTING_ITEM, i, IsExisting ? "Y" : "N");
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_ITEM_CODE, i, ItemCode);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_ITEM_DESC, i, ItemName);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_BRAND, i, Brand);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_SWW, i, SWW);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_SUPPCATNUM, i, SuppCatNum);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_SUBCATNUM, i, SubCatNum);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_QUANTITY, i, Quantity);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_BUYUNITMSR, i, BuyUnitMsr);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_VENDOR, i, Vendor);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_PROCPRICE, i, ProcPrice);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_MULTIPLIER, i, Multiplier);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_SALESPRICE, i, SalesPrice);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_ITEMMAINCAT, i, ItemMainCat);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_ITEMSUBCAT, i, ItemSubCat);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_VEHICLEMANUF, i, VehicleManufacturer);
    oDataTable.SetValue(ItemManagerFormIdentifiers.DT_MAIN_VEHICLETYPE, i, VehicleType);
}


These work as expected, up to this point, I do not trigger any row deletes and the rows are displayed correctly, without error.

I placed a new button on my form, which triggers a new event where I am trying to test the row delete feature (the event was caught without problem and everything works right until I try to call the Remove method): (Check the RunOnSTA method)

public void OnItemEvent(string FormUID, ref ItemEvent pVal, ref bool BubbleEvent)
{
    try
    {
        if (BubbleEvent)
        {
            if (FormUID.Equals(UniqueId))
            {
                if (pVal.BeforeAction)
                {

                }

                if (!pVal.BeforeAction)
                {
                    if (pVal.EventType == BoEventTypes.et_ITEM_PRESSED)
                    {
                        if (pVal.ItemUID.Equals(ItemManagerFormIdentifiers.ID_BUTTON_ELLENORZES.ItemID))
                        {
                            Grid oGrid = (Grid)oForm.Items.Item(ItemManagerFormIdentifiers.ID_GRID_MAIN.ItemID).Specific;
                            string xml = oGrid.DataTable.GetAsXML(); // just for debug

                            try
                            {
                                //  oGrid.DataTable.Rows.Remove(2); // throws exception on MTA too
                                Thread thread = new Thread(RunOnSTA);
                                thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
                                thread.Start();
                                thread.Join(); //Wait for the thread to end
                            } catch (Exception e)
                            {
                                Console.Write("asd");
                            } catch
                            {
                                Console.Write("asd");
                            }
                        //    SBOTools.RemoveRowFromGridUsingXML(oGrid, 2);
                        }
                    }
                }
            }
        }
    }
    catch (Exception e)
    {
        Logger.TraceException(e);
    }
}

private void RunOnSTA()
{
    try
    {
        Grid oGrid = (Grid)oForm.Items.Item(ItemManagerFormIdentifiers.ID_GRID_MAIN.ItemID).Specific;
        oGrid.DataTable.Rows.Remove(2); // <-- SBO Crash
    }  catch (Exception e)
    {
        Console.Write("asd");
    }
}


5. I use my own code to route the events between different modules inside my application, I only use the old event handling, I had lot's of problems using the event properties the SDK presents, but this issue does not seem to depend on the events.
The grid has checkboxes, text and number columns too.

0

Hi Szabolcs Kelemen,

If you have installed SAP Business One SDK on your Development System, you will find a sample for the Grid at the following location:

C:\Program Files (x86)\sap\SAP Business One SDK\Samples\COM UI\CSharp\19.Grid

Could you please help to edit it for me so that I can check/reproduce this issue my side?

Kind regards,

ANKIT CHAUHAN

SAP SME Support

0

Okay I will do, but I am not in the office now. I will post a new reply on Monday with the details (will try to reproduce the issue using the demo code). Thanks

0

Also, Is there any server side log which could give me more information about the error? (The exception on my side points to the server -->RPC_E_SERVERFAULT )

0

I could not reproduce the issue, I have no idea what is causing the error and could not find any logs that could be helpful. I am stuck.

0

I could somewhat narrow it down, by trying a lot...

I need to move rows around, so I made a method to push rows down and insert a new row at a certain index. This method seems to cause the issue when I am calling the delete (If I do not call the push method beforehand, the delete method succeeds, but If I call it more than once, SAP will fail on row delete. Note: The push method seems to work fine, the rows are in the expected position and no error is thrown until I call a delete).

The push method (the commented out portion does the same as the previous longer, both causes fail on delete):

public void PushDownRowsFromIndex(RowIndex rowIndex)
{
    Grid oGrid = (Grid)oForm.Items.Item(ItemManagerFormIdentifiers.ID_GRID_MAIN.ItemID).Specific;
    DataTable oDataTable = oGrid.DataTable;

    if (oDataTable.Rows.Count == 0)
        return;

    var rowDatas = new List<ItemManagerRow>();
    for (int i = 0; i < oDataTable.Rows.Count; i++)
    {
        var rowData = ItemManagerRow.FromGridRow(oGrid, new RowIndex(RowIndexType.ZeroBased, i));
        rowDatas.Add(rowData);
    }

    oDataTable.Rows.Clear();
    oDataTable.Rows.Add(rowDatas.Count + 1);
    for (int i = 0; i < rowDatas.Count + 1; i++)
    {
        if (i == rowIndex.IndexAsZeroBased)
        {
            new ItemManagerRow().WriteToGridRow(oGrid, rowIndex);
        }
        else
        {
            if (i < rowIndex.IndexAsZeroBased)
            {
                rowDatas[i].WriteToGridRow(oGrid, new RowIndex(RowIndexType.ZeroBased, i));
            }
            else
            {
                rowDatas[i - 1].WriteToGridRow(oGrid, new RowIndex(RowIndexType.ZeroBased, i));
            }
        }
    }
    /*
    oDataTable.Rows.Add();

    for (int i = oDataTable.Rows.Count - 2; i >= rowIndex.IndexAsZeroBased; i--)
    {
        var rowData = ItemManagerRow.FromGridRow(oGrid, new RowIndex(RowIndexType.ZeroBased, i));
        rowData.WriteToGridRow(oGrid, new RowIndex(RowIndexType.ZeroBased, i + 1));
    }
    new ItemManagerRow().WriteToGridRow(oGrid, rowIndex);*/
}<br>

The ItemManagerRow object: https://paste.ubuntu.com/p/JHCvJzGNYp/

Even If I comment out the ItemManagerRow's Write method's body, SAP will freeze on delete.

0