I'm trying to implement a cache (dictionary) with the key being a predicate (Func
) and the value being the result of the Func
(an IEnumerable).
I'm having a hard time figuring out a key for the dictionary. I tried GetHashCode
(my intiution was that it was not going to work the way I hoped) but whatever predicate I pass, I always end up with the same hash code.
Here's what I have so far.
public partial class DataProvider
{
ICache Cache = new Cache();
static SemaphoreSlim dbLock = new SemaphoreSlim(1);
static List<dynamic> CachedPredicates = new List<dynamic>();
static object cacheLock = new object { };
public async Task<IEnumerable<T>> FindItems<T>(Func<T, bool> predicate) where T : IDocumentModel
{
Func<T, bool> p;
var lookUpDb = false;
List<T> c = new List<T>();
lock (cacheLock)
{
p = CachedPredicates.SingleOrDefault(x => x as Func<T, bool> == predicate);
if (p == null)
{
CachedPredicates.Add(predicate);
lookUpDb = true;
}
}
if (lookUpDb)
{
await dbLock.WaitAsync();
try
{
IDocumentQuery<T> query = AppDatabase.Client.CreateDocumentQuery<T>(UriFactory.CreateDocumentCollectionUri(ProjectCollection.DatabaseId, ProjectCollection.CollectionId))
.Where(predicate).AsQueryable()
.AsDocumentQuery();
while (query.HasMoreResults)
{
c.AddRange(await query.ExecuteNextAsync<T>());
}
}
finally
{
dbLock.Release();
}
Cache.SetItems<T>(c, predicate.GetHashCode().ToString());
return c;
}
else
{
return Cache.Items<T>(predicate.GetHashCode().ToString());
}
}
}
}
And here's an example of how it would be used:
var v = await DataProvider.FindItems<DataTableModel>(x => x.ProjectId != string.Empty);
So the first time this is called, it would query the database, and subsequent calls would simply fetch the results from the Cache
.
I have omitted the code from the Cache
class, but will add it if someone sees the relevancy for it.
The idea is to not have to manage an ID for each results for each predicates passed to FindItems
and simply "recognize" the query for what it is.
Is this in any way possible ?