Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 
master_t
Partner - Creator II
Partner - Creator II

Qlik Sense Engine API - restoring a selection state is not working

Hello qlikers

I'm developing an app that uses the Qlik Sense Engine API (via the .NET SDK) to interact with a qlik sense app.

In the app, I need to make a series of selections, that I later have to revert. To do this, I thought I could use the StoreTempSelectionState and RestoreTempSelectionState methods, as I assumed by the name that they would allow me to save and then recall the state of the app's selections.

However, after testing it, it appears I'm either wrong or doing something incorrect, because the Restore method seems to do absolutely nothing. Here's the code I'm using:

var state = app.StoreTempSelectionState();

app.GetField("foo").Select("...");
app.GetField("bar").Select("...");
//etc.

app.RestoreTempSelectionState(state.Id);

 

Did I misunderstand what those methods do? Is there a proper way to do this? 

Labels (3)
1 Solution

Accepted Solutions
Øystein_Kolsrud
Employee
Employee

Wow... You've found a bug that from what I can see is pretty much as old as the SDK itself! So you now have the honor of holding the title of the finder of the oldest SDK bug ever recorded 🙂 It goes back at least to v3.0 which was the oldest one I could test without too much effort, but I suspect this goes all the way back to 1.0.

The call to DestroyGenericBookmark will correctly clear the bookmark from the engine so you won't suffer any "ghost" bookmarks. It's when the response to DestroyGenericBookmark is interpreted that the error is thrown. A workaround (apart from a try/catch) is to use the CreateBookmark method from the client namespace instead of CreateGenericBookmark from the engine namespace.

The problem stems from how the result of CreateGenericBookmark is deserialized, and affects all creation methods that returns a result structure instead of a single return value. So other examples of endpoints affected by this are:

  • CreateGenericObject
  • CreateGenericMeasure
  • CreateGenericDimension
  • CreateVariableEx
  • CreateBookmarkEx

However, the method CreateGenericSessionObject is NOT affected by this problem as it returns a single ObjectInterface value instead of a struct. The reason why the CreateBookmark from the client namespace does not suffer from the problem is simply that it deserializes the response from the engine sligthly differently from the corresponding engine namespace method.

View solution in original post

8 Replies
Øystein_Kolsrud
Employee
Employee

I don't think you have misunderstood those methods, but they only work with QCS. That functionality is not available in QSEfW. However, I see there is no mention of this in the documentation of the method which should be considered a documentation error I guess...

master_t
Partner - Creator II
Partner - Creator II
Author

Thanks for the explanation... do you think there's an alternative way of doing it that works on both QSE and QCS?

At the moment as a workaround I am manually reading all the selections on all the fields and then manually restoring them, but it seems like a bit of a hack to me. 

han
Employee
Employee

Think that are some options for you but I don't know which is the best:

 

- Try the https://qlik.dev/apis/json-rpc/qix/doc#%23%2Fentries%2FDoc%2Fentries%2FBack for going back in the "selection stack"

- Create a bookmark https://qlik.dev/apis/json-rpc/qix/doc#%23%2Fentries%2FDoc%2Fentries%2FCreateBookmarkEx that you apply when you want to reset the selections

- Do your selections in an alternate state https://qlik.dev/apis/json-rpc/qix/doc#%23%2Fentries%2FDoc%2Fentries%2FAddSessionAlternateState when you want to go back change back the state

master_t
Partner - Creator II
Partner - Creator II
Author

Thanks for your suggestions.

I'm trying the bookmarks route, it seems to work to some extent, but I have a questions if you dont' mind.

I've written the following code:

var bookmark = app.CreateGenericBookmark(new GenericBookmarkProperties()
{
    Info = new NxInfo() { Type = "bookmark", Id = Guid.NewGuid().ToString() }
});

app.GetField("foo").Select("...");
app.GetField("bar").Select("...");
//etc.

app.ApplyGenericBookmark(bookmark.Info.Id); //seems to work correctly, selections are restored
app.DestroyGenericBookmark(bookmark.Info.Id); //throws ArgumentNullException

 

It mostly works, until the last instruction (calling DestroyGenericBookmark) that throws a ArgumentNullException:

"Value cannot be null. Parameter name: key"
  at Qlik.Engine.Communication.QlikConnection.AwaitResponseTask[T](T task, String methodName, CancellationToken cancellationToken)
   at Qlik.Engine.Communication.QlikConnection.AwaitResponse[T](Task`1 task, String methodName, CancellationToken cancellationToken)
   at Qlik.Engine.App.DestroyGenericBookmark(String id)

Can anyone tell me why this happens? My program needs to run every day on several qlik apps, I don't want to fill them with unused "ghost" bookmarks...

PS: yes, I've checked, I'm not passing a null ID to the method, I'm passing the correct ID, the problem is somewhere else...

Øystein_Kolsrud
Employee
Employee

Wow... You've found a bug that from what I can see is pretty much as old as the SDK itself! So you now have the honor of holding the title of the finder of the oldest SDK bug ever recorded 🙂 It goes back at least to v3.0 which was the oldest one I could test without too much effort, but I suspect this goes all the way back to 1.0.

The call to DestroyGenericBookmark will correctly clear the bookmark from the engine so you won't suffer any "ghost" bookmarks. It's when the response to DestroyGenericBookmark is interpreted that the error is thrown. A workaround (apart from a try/catch) is to use the CreateBookmark method from the client namespace instead of CreateGenericBookmark from the engine namespace.

The problem stems from how the result of CreateGenericBookmark is deserialized, and affects all creation methods that returns a result structure instead of a single return value. So other examples of endpoints affected by this are:

  • CreateGenericObject
  • CreateGenericMeasure
  • CreateGenericDimension
  • CreateVariableEx
  • CreateBookmarkEx

However, the method CreateGenericSessionObject is NOT affected by this problem as it returns a single ObjectInterface value instead of a struct. The reason why the CreateBookmark from the client namespace does not suffer from the problem is simply that it deserializes the response from the engine sligthly differently from the corresponding engine namespace method.

master_t
Partner - Creator II
Partner - Creator II
Author

Happy to help 🤣

Thanks for the pointers, I'll just wrap the call in a try/catch until a bugfix is released

master_t
Partner - Creator II
Partner - Creator II
Author

Some further info on this for anyone facing the same issue:

Wrapping the call in try/catch is not a viable solution, because when the ArgumentNullException is thrown the IApp object gets automatically disposed and is no longer in a usable state.

Using the deprecated CreateBookmark() method seems to work better.

Øystein_Kolsrud
Employee
Employee

Ouch! Well, v15.5.1 was just released which contains a fix for that null-value issue.

https://www.nuget.org/packages/QlikSense.NetSDK/15.5.1