3

Working with Akavache for past couple of days. I have several methods which is using GetAndFetchLatest method but some methods never calls the fetchFunc and the issue is occuring randomly for all functions.

 public async Task<ChecklistModel> GetChecklistTypes()
 {            
        var ChecklistTypesCache = 
        BlobCache.LocalMachine.GetAndFetchLatest("ChecklistTypes",
                                  () => GetChecklistTypesRemote(),
                                  offset =>
                                  {
                                      TimeSpan elapsed = DateTimeOffset.Now - offset;
                                      return elapsed > new TimeSpan(hours: 0, minutes: 5, seconds: 0);
                                  });
        ChecklistTypesCache.Subscribe(subsribedData =>
        {
            _checkilstModel = subsribedData;
        });
        _checkilstModel = await ChecklistTypesCache.FirstOrDefaultAsync();
        return _checkilstModel;
 }

 public async Task<ChecklistModel> GetChecklistTypesRemote()
 {
     //do something
 }

Anyone faced this issue? Do I need to do something else to work? How to fix this?

"Updated Code"

  public async Task<IEnumerable<ChecklistModel.Checklist>> 
  GetChecklistTypesCache()
   {
        IEnumerable<ChecklistModel.Checklist> checklistModel = null;
        var ChecklistTypesCache = 
        BlobCache.LocalMachine.GetAndFetchLatest("ChecklistAccessLevels",
                                 () => GetChecklistTypesRemote(),
                                 offset =>
                                 {
                                     TimeSpan elapsed = DateTimeOffset.Now - 
        offset;
                                     return elapsed > new TimeSpan(hours: 0, 
        minutes: 5, seconds: 0);
                                 });
        ChecklistTypesCache.Subscribe(subsribedData =>
        {
            checklistModel = subsribedData;
        });
        checklistModel = await ChecklistTypesCache.FirstOrDefaultAsync();
        return checklistModel;
    }

     public async Task<IEnumerable<ChecklistModel.Checklist>> GetChecklistTypesRemote()
    {
        //do something
    }
Murugan Durai
  • 133
  • 3
  • 11
  • 1
    Subscribe and await might be odd because it's subscribing twice to the Observable which is a RefCount so it might be helpful to just surface this as an Observable and use that. Second thing is that GetAndFetchLatest will produce two results one will be the cached version and the second will be what's from the func. I'm thinking if you're doing FirstOrDefaultAsync that will probably unsubscribe before the second operation (the func) executes. If you're going to use await it might be better to not use GetAndFetchLatest and instead await a Get then call the func after that yourself and cache it. – Shane Neuville Jun 13 '17 at 17:43
  • 2
    GetAndFetchLatest lends itself more to a design based around Observables (so not using await) as it's going to produce 2 results. Typically you would use await only if you care about the last result of an Observable sequence or if the Observable sequence will only produce one result. – Shane Neuville Jun 13 '17 at 17:45
  • 1
    Maybe try to see how I used it in a sample project here https://github.com/Depechie/2017-ConnectedApp/tree/22fb7c2f4aef298cd5e0d040439e27d95a95c0d1 the setup of using Observables is different than async/await ... ( like @ShaneNeuville already said ) – Depechie Jun 13 '17 at 21:49
  • Thanks a ton Depechie & @ShaneNeuville. Will update the code in my question. It seems like working but wanted to make sure that the code is not breaking. Please check & shed some more light. – Murugan Durai Jun 14 '17 at 18:21
  • So the update still doesn't really make sense.. GetAndFetchLatest provides two messages to the subscriber and you can't represent that concept using Tasks because the Task can only produce one result. The point of GetAndFetchLatest is to provide the subscriber with the cached result, fires the func, then it caches that result, and then it gives the subscriber that result as well. If you're going to stick with awaits don't use GetAndFetchLatest use something else. What is your use case? What exactly are you wanting to achieve? If the value is already cached should it still go to the server? – Shane Neuville Jun 14 '17 at 22:35
  • on the other hand @Geoffrey Huntley makes a good point that if you just change your method to return the Observable from "GetAndFetchLatest" and then understand how that all works you'll find yourself in a better place :-) – Shane Neuville Jun 14 '17 at 22:36
  • @ShaneNeuville Yes I want the func to reach API method for every 10 mins even if the cache data is available. Also, I tried both IObservable & IEnumerable methods, at some point the implemented methods returns null (without hitting the fetchfunc) & (as you said) only thing which I din't tried was removing the await methods. Will try & share the results. Do I need to remove async from both cache & fetchfunc methods? – Murugan Durai Jun 14 '17 at 23:04
  • So what's the purpose of the caching? If you're only going to return one result you need to either return the cached one or the one from the func. The point of GetAndFetchLAtest is to provide both to the subscriber which you can't really do with tasks... – Shane Neuville Jun 14 '17 at 23:18
  • Thanks @ShaneNeuville. It only worked as expected for few hours after removed await in the function then again its not hitting the fetchfunc. Can you please check updated code 2 here https://gist.github.com/MuruganDurai/98d4d715cbf429531ffd775df2a6d6ad – Murugan Durai Jun 17 '17 at 05:53

2 Answers2

4

GetAndFetchLatest returns multiple times. It's designed to be used with the Reactive Extensions as an observable that you subscribe to. The promise/async/await pattern only returns once. Rx is 9+ years old now and well worth learning - see https://reactiveui.net/concepts

Geoffrey Huntley
  • 466
  • 1
  • 5
  • 11
3

So to elaborate on what @Geoffrey Huntley said it's a little bit hard to shape a response because the way you're doing it (even in the Gist) doesn't really make sense "Reactively". GetAndFetchLatest doesn't really make any sense to use if you're going to use the promise/async/await patterns... If you're going to use GetAndFetchLatest your code would just look something like

    public IObservable<ChecklistModel.Checklist> GetChecklistTypesCache()
    { 
        return BlobCache.LocalMachine.GetAndFetchLatest("ChecklistAccessLevels",                                    
                                   async () =>
                                   {
                                       if (!await Tools.IsConnected()) return null;
                                       return await GetChecklistTypesRemote();
                                   },
                                 offset =>
                                 {
                                     TimeSpan elapsed = DateTimeOffset.Now - offset;
                                     return elapsed > new TimeSpan(hours: 0, minutes: 0, seconds: 30);
                                 });

    }

    public async Task<ChecklistModel> GetChecklistTypesRemote()
    {
      //do something
    }


    //calling code
    //Now this is going to possibly fire twice. Once for the cached version 
    //and then one for the version from the Func
     GetChecklistTypesCache().Subscribe(subsribedData =>
        {
            List<ChecklistModel.Checklist> checklistCollection = new 
            List<ChecklistModel.Checklist>();
            checklistCollection.Clear();
            checklistCollection.Add(subsribedData);
            checkilstModel = checklistCollection.ToObservable();
        });

If you want to use async/await then you'd need to just use the Akavache parts that only return one result

These are all taken from the samples on the github page https://github.com/akavache/Akavache

So first you'd try to get your thing from the cache

try {
    toaster = await BlobCache.LocalMachine.GetObjectAsync("ChecklistAccessLevels");
} catch (KeyNotFoundException) {

}

If it's not there then you'd call your func

if(toaster == null)
   toaster = await GetChecklistTypesRemote()

Now you'd insert it into the cache

await BlobCache.LocalMachine.InsertObject("ChecklistAccessLevels", toaster );

But maybe "GetOrFetchObject" would work better for your purpose as it will only fire once and will work with a task? But it's a little hard to shape a response because i'm still not quite clear the use case. I think reading through the https://reactiveui.net/concepts would be useful and once the Reactive concepts take hold a bit more the interactions will make more sense.

Shane Neuville
  • 2,037
  • 13
  • 17
  • Thanks for the detailed answer @shane. Is that alright if I would return cached value instead of returning null here. if (!await Tools.IsConnected()) return null; – Murugan Durai Jun 20 '17 at 19:52
  • the way GetAndFetchLatest works is that the first notification from the observable will be the cached value so then your calling code uses that however it wants and then the func is called and that's the second notification.If there is no cached value then you only get one notification which is from the func and then if you call it again you'll get two. It doesn't really make any sense to return the cached value from the func – Shane Neuville Jun 22 '17 at 02:24