Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
Showing results for 
Search instead for 
Did you mean: 

Hi everyone!

Another blog came in 🙂 The topic this time is not really critical, but nice-to-know tips around (mostly) offline CRUD.

First off let's make it clear - a few things:

  • The CUD queue in the offline store will have the same sequence of how you issue CUD operations locally, and after the flush, an OData service receives the queue and execute them one-by-one.
  • The offline store queue doesn't do any combining of the operations. This is because the offline store might encounter side-effects in the OData services from each operation. (e.g. If you create and delete an entity, they will always go up as two separate requests.)

And let's get into further CRUD geek knowledge...

Read with Delta Queries

In the blog #7, the term "Delta Query" was presented.

For those who're not yet familiar with this concept, it is nicely written here - “give me all data that were created/changed/deleted since I last asked”.

From the client programmer perspective, when we fetch the OData collection that supports Delta Query, the OData payload should contain this sort of href link:

<link rel="delta" href="TravelAgencies_DQ?!deltatoken='00237DD11C661ED49AFE5715E7776E7C_20141114113818'"/>

And next time you fetch the same collection, you use this URL instead of using plain collection name - and you'll get the delta portion of data since the last time you fetched them. This delta link string can be picked up by using the deltaPath property.

01  id<SODataEntitySet> myEntityset = (id<SODataEntitySet>)responseSingle.payload;
02  NSString* deltaPath = myEntityset.deltaPath;

You might be tempted to use this value - but actually you don't need to use it explicitly, these are automatically handled by SODataStore. Here's a few notes:

  1. SDK SP6 will introduce a new Online Cache API. It makes use of deltaPath value explained above, but it is inner feature. No need to be aware of the deltaPath.
  2. Don't use deltaPath value explicitly with offline store. The delta links are handled behind the scenes with the defining requests.

So the conclusion is, a new Cache API of online store will make use of the Delta Queries, and offline store handles it too - both are smart enough to handle it automatically for you (most likely, in online case, you don't want to use it without Cache API).

Create with resourcePath

In the blog #7 and #8, we learned how to work with CUD queue. An interesting tip for Create operation.

Let's assume you had created one entity. It is still in the local queue.

Right after you create an entity (that is, invoked scheduleCreateEntity method), requestServerResponse callback will be triggered. You should be able to obtain the entity which is created in the local queue.

01  if (request.mode == SODataRequestModeCreate) {
02    id<SODataResponseSingle> responseSingle = (id<SODataResponseSingle> )requestExecution.response;
03    if ([responseSingle.payload conformsToProtocol:@protocol(SODataEntity)]) {           
04      id<SODataEntity> myEntity = (id<SODataEntity>)responseSingle.payload;
05      NSString* path = myEntity.resourcePath;
06      ...

Please pay an attention in the line #05. It returns the local resourcePath string - it should have a funny value like "(lodata_sys_eid = X'3B3F7EB049DB43FE9B016ACDB2B4CC2D00000000’)". When it is local you have to use the lodata_sys_eid key and once you flush & refresh, can obtain the real ID (= entity resource path in OData service) by scheduleReadEntityWithResourcePath with the local resource path.

You can also keep using the lodata_sys_eid key even after the flush/refresh (it will keep working until the next time your closing & opening the offline store after the flush/refresh).

Update & Delete with ETags

Have you ever heard of "ETag"?

ETag is something needs to be implemented in OData services. NetWeaver Gateway has been supporting it for a is not hard to implement. and what has been happening was no one knows how to use it 🙂 - until we have the offline store.

I hear some discussion if ETag can work with the concurrency of the entity. But ETags are not really meant to optimize concurrent access. They are instead a safety mechanism to make sure no data gets lost if operations conflict.

What does that mean? Let's have an example:

If the backend OData Producer supports ETags, then the offline store will do it’s best to reflect that behavior on the client as well.

Consider the scenario:

  1. The application reads entity1 out of the Offline Store with ETag: etagA
  2. In the OData service side, the entity1 has been changed. - The data in client and server are different now!
  3. The application does a refresh that downloads a change to entity1 and the ETag has now changed to etagB.
  4. The application then attempts to modify entity1 using the version it read in step 1.
  5. The request will fail at step 4 on the scheduleUpdate/Delete operation and the operation will not be added to the request queue at all.

So the offline store will do its own check with the ETag values. Makes sense?

Note: You can spot the ETag value in the property on either the SODataEntity or the SODataRequestParamSingle object. When you read an entity out of the store it will already have its ETag property pre-filled in with the latest known value.  So as long as you use an entity object that you have read from the store to do subsequent updates then you do not need to do anything.

What if we don't refresh in the step 3?

The request would succeed in step 4 (because the ETag would match the local version "etagA") and be added to the request queue. However, it would fail during the flush (because the ETag wouldn’t match the server version) and get an error in the offlineStoreRequestFailed method.

So the ETag is not meant for optimizing concurrent access. If you expect the app can be offline without refresh, design your backend system so that Update & Delete operations never conflict in the first place or have the backend perform its own conflict resolution instead of failing the requests.

That's all for the CRUD trivia 🙂 thanks for reading.

See you in the next blog,


List of blogs