Prepopulating the Cache

Jun 14, 2011 at 11:37 PM

I have a scenario requested by a client and am wondering whether AgFX will fit with this.

We'd like the app to be able to run in offline scenarios so there's always a decent user experience. The idea is that any data retrieved is stored permanently for this offline scenarios and the online calls update this. Also having the cached data retrieved immediately while the web request is being processed.

This seems relatively straight forward with AgFX simply making use the appropriate cache policies (correct me if I'm wrong).

One thing we want to do is ship the app with data to prepopulate the cache so any initial offline scenarios will have data. This is part I'm not entirely confident about how to fit this into AgFX. Looking further would a Save call on the DataManager on inital app startup do the job?

A great library, well done.

Coordinator
Jun 18, 2011 at 5:56 AM

Hi - 

Right, CacheThenRefresh will keep things for reload permanently, then refresh on demand, so that's the behavior you're looking for.

Prepopulation is an interesting scenario, and probably the easiest way to do this is to build it into your loader's GetLoadRequest methods.  

GetLoadRequest returns a LoadRequest object, which is an abstract class.  Most scnearios use WebLoadRequest, but the design was specifically to enable other scenarios.  I didn't think of this specifically but it's an example.  

So if you put the data on disk or in resources somewhere you can write a custom LoadRequest that, in it's Execute() method, goes and fetches that data and returns it when you detect this first load scenario.  Now that I think through this, I think there may be a feature ask here which is a DataManager.IsCacheAvailable<T>(LoadContext) that allows you to check on this.  But anyway that's the idea, let me know if that's not clear and I'll whip up some psuedo code to give you a better picture.  In teh normal case your method would fall through to WebLoadRequest as usual.  Deserialize would not change.

Shawn

Nov 4, 2011 at 1:32 PM

@Shawnburke Would this also apply if I want the app to load and already show data from isolated storage?

I would like to be able to 'visually show' the user his previous request when he reopens the app and when he refreshes, the new items will be 'added'.

Or is it then better to: 1. store the collection itself in isolated storage and reupdate the bindable mvvm property on startup and 2. on deserialize just do clear and add?

Coordinator
Nov 4, 2011 at 3:12 PM

Hi -

Well, if you're looking to show the previous request, then that's exactly what CacheThenRefresh does, but the refresh will replace those items unless you stash them somewhere.

So I'm not sure I'm trying to understand exactly what you're trying to do.  

Say you make a request and get items A, B, C back, with the VM having CacheThenRefresh(3600), which says to consider them valid for an hour, and for requests after an hour, fetch new data.  If you close the app and re open it more than an hour later the following will happen:

1) App will start up

2) App will make request, [A, B, C] will be returned from cache and displayed

3) AgFx will see that the cache is no longer valid (more than 3600 seconds have elapsed) and will kick off a new request

4) Server will return, say [C, D, E]

5) Cache will be replaced and UI will bind to new value.

Are you looking for something different than that?


Thanks,


Shawn

Nov 4, 2011 at 9:14 PM

Hey Shawn,

No the scenario is correct only 2 detail questions...

1. When the app starts, the best way is to just do a CacheThenRefresh on load? So no user interaction, just try and load ( so I get the previous items )?

2. How do I just 'add' the new items? I need to do this myself I guess in the deserialization? Or is there some 'smart' handling in the framwork? Now I do Clear of the collection and just add all items I get back...

 

Thanks for the info, and AgFx rocks by the way ;)

Nov 22, 2011 at 10:04 PM

 I think there may be a feature ask here which is a DataManager.IsCacheAvailable<T>(LoadContext) that allows you to check on this.  

This is something I would be interested in as well.  In my scenario I would also want the IsCacheAvailable to be able to accept a default for an item.  I have defaults for all items initially loaded with my app.  But I want them to be able to update after the user launches with internet the first time. 

For my specific scenario users may run the first time with no internet (it has happened a bunch according to my crash reports).  And if I don't have any items in my list the app is not very useful.

I am looking at implementing a custom LoadRequest.  Thanks for the pointer in the right direction.

 

 

Dec 2, 2011 at 5:45 PM

OK, I got this working for my scenario and thought I would share some snippets to help others.  I think what I kept getting confused about is that the normal flow to deserialize still has to happen.  Your custom load request just sends a string just as if it had gotten it off the wire with a WebLoadRequest.

I am using CacheThenRefresh for my data CachePolicy.  Changing the policy would change the order of things that are called, or when they are called.  But the same logic would work.

    public class LocalLoadRequest : LoadRequest
    {
        private string _uri;

        public LocalLoadRequest(LoadContext loadContext, string uri)
            : base(loadContext)
        {
            _uri = uri;
        }
        
        public override void Execute(Action<LoadRequestResult> result)
        {
            string xmldata = "BIG STATIC PASTE OF A DEFAULT DOCUMENT";

            // Go get the list from local string, but return it so it looks like a stream we just got from the server
            MemoryStream ms = new MemoryStream();

            StreamWriter sw = new StreamWriter(ms);
            sw.Write(xmldata);
            sw.Flush();

            ms.Seek(0, SeekOrigin.Begin);

            LoadRequestResult lrr = new LoadRequestResult(ms);
            result(lrr);
        }
    }

So that will return a static XML fragment.

Then inside my GetLoadRequest I either return a WebLoadRequest if we are online, or my LocalLoadRequest if we are offline.

            public LoadRequest GetLoadRequest(RSSFeedLoadContext loadContext, Type objectType)
            {
                if (Utilities.EthernetConnected())
                {
                    return new WebLoadRequest(loadContext, new Uri(loadContext.Uri));
                }
                else
                {
                    return new LocalLoadRequest(loadContext, loadContext.Uri);
                }
            }

And that was it. The static response is returned and then cached by AGFX.

This brought me to a few new questions that others may have as well.
  • At app start with no local data what happens? Your GetLoadRequest is called like normal and you get the local data if you are offline, otherwise you get the online data.
  • At app start with data already in cache? The cached data is sent to the deserialize and everything is fine.

But now I am stuck when I attempt to refresh.  It doesn't matter how long I wait the ViewModel.IsUpdating is always true until I force a call to Refresh.  Then after that refreshes it is set to False.
Hopefully this will help someone else also.  The framework is awesome.  But it takes a bit of getting your head wrapped around it.