I am trying to implement a simple caching mechanism in a windows phone 8.1 API that I am creating. I have chosen a Windows Phone Portable Class Library template in visual studio.
The cache class looks something like this,
[DataContract]
class cache
{
private const string JSONFILENAME = "data.json";
[DataMember]
Dictionary<Int32, item> cDictionary;
[DataMember]
int _maxSize;
public int MaxSize
{
get { return _maxSize; }
set { _maxSize = value; }
}
public cache(int maxSize){
cDictionary = new Dictionary<int, item>();
_maxSize = maxSize;
}
public void push(Int32 id, item obj)
{
if (!cDictionary.ContainsKey(id)) {
cDictionary.Add(id, obj);
}
}
internal static async Task<cache> Load()
{
cache obj = null;
try
{
var jsonSerializer = new DataContractJsonSerializer(typeof(cache));
using (var myStream = await ApplicationData.Current.LocalFolder.OpenStreamForReadAsync(JSONFILENAME))
{
obj = (cache)jsonSerializer.ReadObject(myStream);
}
}
catch (FileNotFoundException)
{
obj = null;
}
return obj;
}
internal static async void Save(cache obj)
{
var serializer = new DataContractJsonSerializer(typeof(cache));
using (var stream = await ApplicationData.Current.LocalFolder.OpenStreamForWriteAsync(
JSONFILENAME,
CreationCollisionOption.ReplaceExisting))
{
serializer.WriteObject(stream, obj);
}
}}
The item class whose objects go into the dictionary looks like this,
[DataContract]
class item
{
[DataMember]
string _fName;
public string FName
{
get { return _fName; }
set { _fName = value; }
}
[DataMember]
string _lName;
public string LName
{
get { return _lName; }
set { _lName = value; }
}
[DataMember]
int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
public item(int id, string fName, string lName)
{
this.Id = id;
this.FName = fName;
this.LName = lName;
}
}
The idea is : The end user creates an instance of the api and calls a method doSomething(). The method first looks in the cache (not shown in the example) if found, returns the Item object back, or else, gets the item object from a web service(not shown) and then push it to cache.
public class api
{
cache tCache;
string apiKey;
public laas(string apiKey)
{
this.apiKey = apiKey;
this.tCache = new cache(100);
}
public async void Initialize(api obj)
{
//If cache exists
obj.tCache = await cache.Load();
if (obj.tCache == null)
{
obj.tCache = new cache(100);
}
}
public void doSomething(string id)
{
tCache.push(id.GetHashCode(),new item(1,"xxxx","xxx"));
cache.Save(tCache);
}
}
I wanted to initialize/load the cache in the constructor of the api class, but since ApplicationData.Current.LocalFolder provide only async methods to read and write data from persistent storage, I created a separate static async class Initiialize() that would load the cache, since making an async constructor makes no sense.
Problem: the statement tCache.push(id.GetHashCode(),new item(1,"xxxx","xxx")); in the doSomething() throws null reference exceptions. This could possibilly be happening because the tCache hasn't been loaded/initialized yet due to the async operation.
I had tried obj.tCache = await cache.Load().Result to wait for the loading to complete, but that hangs my application. (http://msdn.microsoft.com/en-us/magazine/jj991977.aspx)
Could you please point me in the right directions here? Is my diagnonis right? Is there a better way to do it? Any pointer is appreciated.
Thanks!
What is probably happening is that you're calling Initialize
but not awaiting it, because it is async void
.
What you need to do is change:
public async void Initialize(api obj)
To:
public async Task Initialize(api obj)
Then, you'll need to await Initialize(obj)
which will ensure that caches completion before use.
Note that async void
is ment only for top level event handlers and shouldn't be used otherwise.
Also, the reason Task.Result
hangs your application is because it is causing a deadlock, which is related to the way async
marshals your synchronization context between calls.