$batch request resulting in error "Default changeset implementation allows only one operation"

SAPUI5GUY picture SAPUI5GUY · May 12, 2017 · Viewed 8.3k times · Source

I am making a worklist application using SAPUI5. The problem is that when I create an entry and then create another one right after that, I get the following error:

Default changeset implementation allows only one operation.

I checked the $batch header and I see that there is a MERGE and a POST, with the MERGE updating the previous entry for some reason. Can anyone shed some light? Could it be a backend error and not a UI5 error?

Creating the new entry:

_onMetadataLoaded: function() {
  var oModel = this.getView().getModel();
  var that = this;
  // ...
  oModel.read("/USERS_SET", {
    success: function(oData) {
      var oProperties = {
        Qmnum: "0",
        Otherstuff: "cool"
      };
      that._oContext = that._oView.getModel().createEntry("/ENTITYSET", {
        properties: oProperties
      });
      that.getView().setBindingContext(that._oContext);
      // ...
    }
  });
},

handleSavePress: function(oEvent) {
  // ...
  this.getView().getModel().submitChanges({
    success: function(oData) {
      // ...
    },
    error: function(oError) {
      // ...
    }
  });
},

Answer

nintendojet picture nintendojet · Dec 7, 2017

tl-dr: Apparently you must be using the SAP Gateway. If you do not need to process those requests in one transaction then send them in different changesets. If you do not need batch calls at all consider turning it off by supplying your model with "useBatch": false upon instantiation. However if you need to process the requests together in one transaction then you have to read the details below.

In order to understand the problem you have to understand how the gateway and the batch and changeset requests work.

Batch requests consist of multiple requests bundled together. The purpose is to open only one connection and group together relevant requests so that the overhead is minimalized. Changesets form smaller blocks inside batch requests, where modification requests can be bundled and processed together in order to ensure an all-or-nothing characteristic.

So on the gateway side: there are two relevant classes for your OData service, assuming that you have used the SAP Gateway Service Builder (SEGW transaction). There is one with the ending ...DPC and one with ...DPC_EXT. Don't touch the former, it will be always regenerated when you update your service in the service builder. The latter is the one that we will need in this example. You will have to redefine at least two methods:

  • /IWBEP/IF_MGW_APPL_SRV_RUNTIME~CHANGESET_BEGIN
  • /IWBEP/IF_MGW_APPL_SRV_RUNTIME~CHANGESET_PROCESS

By default the changeset_begin method will only allow changeset processing for changesets where the number of requests equals to one. This can be handled automatically that's why a limitation exists. If there were more requests one could not ensure their processing automatically as they could have a business dependency on each other.

So make sure to allow a bundled (deferred mode) processing of changesets under the desired conditions:

  • /IWBEP/IF_MGW_APPL_SRV_RUNTIME~CHANGESET_BEGIN: first call the super->/iwbep/if_mgw_appl_srv_runtime~changeset_begin method in a try catch block, then loop at it_operation_info to decide and narrow down processing only in selected cases and then allow cv_defer_mode only for the selected cases, otherwise throw a /iwbep/cx_mgw_tech_exception=>changeset_not_supported exception.
  • /IWBEP/IF_MGW_APPL_SRV_RUNTIME~CHANGESET_PROCESS: all requests will be available in the it_changeset_request. Make sure to fill the ct_changeset_response table with the responses.

      METHOD /iwbep/if_mgw_appl_srv_runtime~changeset_process.
        DATA:
            lv_operation_counter TYPE i VALUE 0,
            lr_context           TYPE REF TO /iwbep/cl_mgw_request,
            lr_entry_provider    TYPE REF TO /iwbep/if_mgw_entry_provider,
            lr_message_container TYPE REF TO /iwbep/if_message_container,
            lr_entity_data       TYPE REF TO data,
            ls_context_details   TYPE /iwbep/if_mgw_core_srv_runtime=>ty_s_mgw_request_context,
            ls_changeset_response LIKE LINE OF ct_changeset_response.
    
        FIELD-SYMBOLS:
            <fs_ls_changeset_request>  LIKE LINE OF it_changeset_request.
    
        LOOP AT it_changeset_request ASSIGNING <fs_ls_changeset_request>.
            lr_context          ?= <fs_ls_changeset_request>-request_context.
            lr_entry_provider    = <fs_ls_changeset_request>-entry_provider.
            lr_message_container = <fs_ls_changeset_request>-msg_container.
            ls_context_details   = lr_context->get_request_details( ).
            CASE ls_context_details-target_entity.
              WHEN 'SomeEntity'.
                "Do the processing here
              WHEN OTHERS.
            ENDCASE.
        ENDLOOP.
      ENDMETHOD.