C
C#12mo ago
13eck

❔ Filter a ConcurrentDictionary

I have a ConcurrentDictionary that I'm using for a small in-memory cache and I'm wondering what the best way to filter items out of it. one of the fields on the value stored in the dictionary is a DateTimeOffset expiration time and I'm still new to using it. Currently, I'm using .Select() to filter out the expired items and casting it back to the Dict, overwriting the existing variable with a new one:
_cache = (ConcurrentDictionary<string, UserCacheItem>)_cache.Select(item => item.Value.Expiry > DateTimeOffset.Now);
_cache = (ConcurrentDictionary<string, UserCacheItem>)_cache.Select(item => item.Value.Expiry > DateTimeOffset.Now);
Is there a better way to do this?
10 Replies
canton7
canton712mo ago
That will throw an exception, because the thing returned by Select isn't a ConcurrentDictionary Also if you're re-assigning _cache, then you need to be careful about thread-safety. You might need another lock around that field, and consumers might need to take that lock before accessing _cache, etc, and you start to defeat the point of using a concurrent collection in the first place Life is going to be a lot easier if you make _cache readonly, and never assign it. Your approach to removing stale items is then to iterate over it, and remove any values which have expired. ... or you can use a MemoryCache: https://learn.microsoft.com/en-us/dotnet/api/system.runtime.caching.memorycache?view=dotnet-plat-ext-7.0
13eck
13eck12mo ago
My initial code did two foreach loops, one to get the expired items and one to remove them, but thought I should do a Select to cut down on the number of iterations:
List<KeyValuePair<string, UserCacheItem>> Items = new();
foreach (var item in _cache)
{
if (item.Value.Expiry < DateTimeOffset.Now) Items.Add(item);
}
foreach (var item in Items)
{
_cache.TryRemove(item);
}
List<KeyValuePair<string, UserCacheItem>> Items = new();
foreach (var item in _cache)
{
if (item.Value.Expiry < DateTimeOffset.Now) Items.Add(item);
}
foreach (var item in Items)
{
_cache.TryRemove(item);
}
canton7
canton712mo ago
Yep, that's how I'd do it
13eck
13eck12mo ago
Thanks for confirming! I recently started C# after being a front-end dev so I'm still learning 😁
mtreit
mtreit12mo ago
You might consider using a MemoryCache which is thread safe and has built-in support for evicting expired entries.
13eck
13eck12mo ago
I'll have a look, thanks!
Denis
Denis12mo ago
Wow, I've never heard of this type. Thanks for sharing this
canton7
canton712mo ago
☝️
Tarcisio
Tarcisio12mo ago
no one told you to send long answers (jk)
Accord
Accord12mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.