Skip to Content
Author's profile photo Pranay Patel

Create Dynamic Entity Model in SAP Gateway

Hello Everyone, this blog explains how to create Dynamic Entity type/Entity Set in SAP Gateway. We can easily create an Static entity type using Gateway service builder (SEGW). But sometimes requirements can be tricky where you need to generate Entity model at runtime. Recently, i got a requirement to develop an OData service which could not be fulfilled using static entity model.

The requirement was to create an SAPUI5 application (running on SAP OData) that will have dynamic selection screen. On execution, it should generate a dynamic report based on output fields selected by the user (relevant to the selection screen input fields). Dynamic selection screen part was easily achieved using static entity set. But the real challenge was to send multiple output records as part of odata service response since user can select any number of fields to display in the dynamic report.

The approach which i followed in developing OData service was:

  1. Created a static entity with properties: Field_Name, Data_Element & Table_Name. In GET_ENTITYSET method, exposed all the fields that need to be selected as part of Dynamic report.
  2. When an user selects output fields, application calls POST method of OData service to store relevant properties(Field_Name, Data_Element & Table_Name) in a custom table.
  3. Now, Create an entity with some dummy field inside it. We will change the properties of this entity at run time reading entry from the custom table. MPC_EXT class will be used here.
  4. Once response gets sent successfully, delete the whole custom table entries stored.

 

Steps:

Create a custom table (that will store user selected fields along with data element):

Insert some records for fields in the custom table:

Now, create a project ‘ZDYMANIC_ENTITY’ using t-code SEGW. Create an entity ‘TEST’ with one dummy attribute (VBELN).

Create an entityset ‘TESTSet’ for the entity. Generate runtime artifacts.

To create dynamic properties(fields) inside the entity, Redefine DEFINE method of MPC_EXT class. Create a private instance method ‘DEFINE_TEST’.

Call ‘DEFINE_TEST’ method inside DEFINE method.

DYNAMIC PROPERTIES Creation (MPC_EXT Class):

Inside the method ‘DEFINE_TEST’. Fetch the properties to be created as well as the data element (needed for binding). Loop in above properties and create properties as below:

METHOD define_test.

DATA:
lo_annotation     TYPE REF TO /iwbep/if_mgw_odata_annotation,
lo_entity_type    TYPE REF TO /iwbep/if_mgw_odata_entity_typ,
lo_complex_type   TYPE REF TO /iwbep/if_mgw_odata_cmplx_type,
lo_property       TYPE REF TO /iwbep/if_mgw_odata_property,
lo_entity_set     TYPE REF TO /iwbep/if_mgw_odata_entity_set.

DATA: lt_output TYPE STANDARD TABLE OF ztable_fields_op,
ls_output TYPE ztable_fields_op.
**********************************************************************
*   ENTITY – TEST
**********************************************************************

lo_entity_type = model->create_entity_type( iv_entity_type_name = ‘TEST’ iv_def_entity_set = abap_false ). “#EC NOTEXT

**********************************************************************
*Properties
**********************************************************************

SELECT * FROM ztable_fields_op INTO TABLE lt_output.
IF sy-subrc = 0.
LOOP AT lt_output INTO ls_output.
lo_property = lo_entity_type->create_property( iv_property_name = ls_output- field_label ).
lo_property->set_is_key( ).
lo_property->set_type_edm_string( ).
lo_property->set_nullable( abap_false ).

TRY.
DATA: lv_element TYPE string.
lv_element = ls_output-data_element.

CALL METHOD lo_property->bind_data_element
EXPORTING
iv_element_name = lv_element.

CATCH /iwbep/cx_mgw_med_exception .
ENDTRY.
ENDLOOP.
ENDIF.

**********************************************************************
*   ENTITY SETS
**********************************************************************
lo_entity_set = lo_entity_type->create_entity_set( ‘TESTSet’ ). “#EC NOTEXT

lo_entity_set->set_has_ftxt_search( abap_false ).
lo_entity_set->set_subscribable( abap_false ).
lo_entity_set->set_filter_required( abap_false ).

ENDMETHOD.

POPULATE Records(DPC_EXT Class):

Now, Inside DPC_EXT class, Redefine the method /IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_ENTITYSET to populate records in the dynamic entityset:

  METHOD /iwbep/if_mgw_appl_srv_runtime~get_entityset.

DATA: lt_output TYPE STANDARD TABLE OF ztable_fields_op,
ls_output TYPE ztable_fields_op.

DATA: lt_cat TYPE TABLE OF lvc_s_fcat,
ls_cat LIKE LINE OF lt_cat,
d_ref TYPE REF TO data.

FIELD-SYMBOLS : <f_fs> TYPE table.

IF iv_entity_name = ‘TEST’.

SELECT * FROM ztable_fields_op INTO TABLE lt_output.
IF sy-subrc = 0.

LOOP AT lt_output INTO ls_output.
ls_cat-tabname = ls_output-table_name.
ls_cat-fieldname =  ls_output-field_label.
ls_cat-ref_field = ls_output-field_label.
ls_cat-ref_table = ls_output-table_name.
APPEND ls_cat TO lt_cat.
ENDLOOP.
ENDIF.

“”create a dynamic internal table
CALL METHOD cl_alv_table_create=>create_dynamic_table
EXPORTING
it_fieldcatalog = lt_cat
IMPORTING
ep_table        = d_ref.

ASSIGN d_ref->* TO <f_fs>.

**–Fetch records dynamically
SELECT * FROM (ls_output-table_name)
INTO CORRESPONDING FIELDS OF TABLE <f_fs>.

** Call methos copy_data_to_ref and export entity set data
copy_data_to_ref( EXPORTING is_data = <f_fs>
CHANGING cr_data = er_entityset ).

ENDIF.

ENDMETHOD.

Register the service in the gateway and test the service using Gateway client using URI /sap/opu/odata/sap/ZDYNAMIC_ENTITY_SRV/TESTSet?$format=json:

You can see status code is 200 and multiple records are coming as part of entityset in the response.

Cheers!!

Assigned Tags

      20 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Former Member
      Former Member

      I haven’t used Collections to their potential yet, but will look to check it out more closely.

       

      Author's profile photo Joseph BERTHE
      Joseph BERTHE

      Hello,

      I have a question, how do you handle the cache? Because as I can know, the define method is called only when the cache is invalidated.

      Regards,

      Joseph

      Author's profile photo Pranay Patel
      Pranay Patel
      Blog Post Author

      Hello Joseph,

      'Define' method is used to generate metadata of the odata service. You can have a look at the code in the method (MPC class) in any Gateway project, you'll get a better understanding.

      Regards,

      Pranay Patel

      Author's profile photo Joseph BERTHE
      Joseph BERTHE

      Hi,

      I understand what you did in the MPC, but what I want to say is, all the methods from MPC are executed only once when the model cache is empty/cleared. When the cache is full fill, then the define method is not executed anymore.

      So if I'm rigth, you can only have a dynamic data model if you clear the cache for each call.

      If it's works, i'm verry surprise and I should test it.

      Regards,

      Joseph

      Author's profile photo Manuel Hofmann
      Manuel Hofmann

      Hello,

      you are absolutely right. Metadata cache will not update once it has been generated. I imagine the table to store the dynamic fields could be a customizing table. The metadata cache in gateway and backend (assuming a hub scenarioa) will not be updated if a new customizing transport has been transported to target system. In development this is not a problem because it is recommended to deactivate caching, so it will be generated again.

      One way to do it however to redefine method GET_LAST_MODIFIED. However author didnt mention it  and i would not recommend it.

      I also disagree with the authors statement to not be able to fulfill the requirement with a static model. It is possible i have done it myself. You only need to have a static entity type which represents the "Fields". This entity set can then be filled during runtime. With deep entity you can also send those fields again back to backend and do whatever you want.

      Author's profile photo Pranay Patel
      Pranay Patel
      Blog Post Author

      Hi Manuel,

      Thanks for your inputs. To handle Metadata cache issue, we can redefine method GET_LAST_MODIFIED. I missed that point in the blog.

      However, i don't agree with your statement that you can achieve it using static entity. Since you are never certain about what fields are going to be selected by the user to display in the report, how can you make an static entity with those fields. And different users can select fields of their choice in the output.

      And another reason going for dynamic entity is that there can be N number of field choices (which an user can select for output), you don't want to overburden your entity with 100 fields inside it.

      Regards.

       

      Author's profile photo Former Member
      Former Member

      Hello Pranay,

      Thanks for this article. I have created a dynamic metadata on similar lines ..however, i am not able to clear the cache issue . Can you please tell about the usage of get_last_modified method in this context.

      Author's profile photo Ibrahim Khaleel
      Ibrahim Khaleel

      Hi Buddy,

      Use the code something similar to,

       

      DATA: lv_date TYPE timestamp.
      GET TIME STAMP FIELD lv_date.
      rv_last_modified lv_date.

       

      Regards,

      Khaleel

      Author's profile photo Frederik Devinck
      Frederik Devinck

      Hi,

      I've faced a similar development, and we were looking into this solution.

      Cache may be one facet, but how do you handle sorting/filtering on the (dynamic) EntitySet?

      Author's profile photo Pranay Patel
      Pranay Patel
      Blog Post Author

      Hi Frederik,

      /IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_ENTITYSET method has all the importing paramters which you can find in normal GET method of an entityset. You can handle filter/sort in the same way as you do in normal scenario.

      Author's profile photo Former Member
      Former Member

      HI Pranay,

       

      Very Nice article,it really makes a new learner understand how to go for a Dynamic entity set if at all required in our projects.

       

      Thanks.

      Author's profile photo Andre Roberto Pereira
      Andre Roberto Pereira

      Hi Pranay,

      Very nice Post, thanks for sharing.

      At first glance I do not see how can I change the attributes dynamically,I mean

      Let suppose that I perform a scenario to read some vbak data, just like you shown , until here it's OK, and right after I perform the same method TESTSet but I wanna to load some data from KNA1, here is the trick to me, is it possible to handle by using DEFINE method?.

      How to do it ( the logic ) is clear to me but I have no parameters on DEFINE method, then my question is how to make it realy dinamically?

      Regards,

       

      Author's profile photo ALOK JAIN
      ALOK JAIN

      If we go with above solution than data will be double means

      130 Rows having 253 Columns than data rows output 32820. How you can handle the same .

      Author's profile photo Naseera Rahmathilahi
      Naseera Rahmathilahi

      Hello,

      First of all thanks for this blog 🙂

      I am working on a similar requirement. I used this code. For me <f_fs>  table is getting populated properly with correct values for all rows and columns,  but in the output all values of all columns and all rows are null. Any idea why that is?

       

      Regards,

      Naseera

      Author's profile photo Saurabh Chikate
      Saurabh Chikate

      Hello Naseera,

       

      How you were able to get this project activated?

      I have followed all steps but could not resolve error on the last code line.

      I have replaced ER_ENTITYSET with ET_ENTITYSET as it is a get entityset method.

      copy_data_to_refEXPORTING is_data <f_fs>
      CHANGING cr_data et_entityset ).

      Thanks,

      Saurabh

      Author's profile photo Rajat Sharma
      Rajat Sharma

      Hello All,

      I also faced the issue i.e. if, at all my model changes, I have to manually regenerate the OData project.

      I thought of calling the DEFINE method again based on when my model changes. But, it was not at all helpful.

      So, In order to resolve this. We can simply call CL_GUI_CFW => FLUSH to clear the cache.
      The place where you will call this method is totally based on your requirement.

      In my case, My model was changing based on the no of rows being entered in a Z table. Hence, I create an event which will trigger on saving the data. And it will refresh the cache.

      And next time when you call the service with dynamic columns. It will automatically fetch the new model.

      Hope it helps.

      Regards,
      Rajat Sharma

      Author's profile photo phanindra ghanta
      phanindra ghanta

      Hello Pranay/All,

      thanks for sharing this post it was very useful.

      I tried implementing dynamic entity set and its working fine, but I have 2 questions.

      1. you created dynamic entity set by using table data, but how can I create based on the data available during runtime? is there a way to pass field count to define/get_entityset method and create dynamic entity set?
      2. and after implementing dynamic entity set I am unable to access previous static entityset which was created earlier! How can I access my static entityset which was created earlier?

      Thanks in advance.

      Regards,

      Phanindra

       

      Author's profile photo Rajesh C
      Rajesh C

      Hi Pranay,

      Very nice Post, thanks for sharing.

      Is there any way to pass the table name from URL. why because if I maintain multiple tables in custom table i will get all the table details.

      My requirement is to get only table details which will pass through URL.

      Thanks in advance.

      Regards,

      Raj

       

       

       

       

       

       

       

      Author's profile photo Christian Heimerl
      Christian Heimerl

      I don't understand how your Z-table should be able to distinguish users. I see no user column. And in my point of view "user" is also not enough. There should / must be some connection between the POST and the building of the model (like a request ID). Otherwise the same user can hardly use the service multiple times concurrently (e.g. in multiple tabs of a browser consuming the same service).

      It contradicts also to the REST-like approach that gateway services (in my point of view) should have. Actually you have a dependency (state) between the POST and the following GET.

      Can you please comment on that? In my point of view the MPC is not expecting to be used like that.

      Author's profile photo Vinutha Mayanna
      Vinutha Mayanna

      Dear Pranay Patel.

      I have followed all the steps which you have showed or mentioned in above "Create Dynamic Entity Model' blogs  but i can't register the service for this particular project which i have created for dynamic entity.

      PFA of screenshot.