- 
      
- 
        Save IEvangelist/43832e769fb742bcfb2c20d8446a9cd2 to your computer and use it in GitHub Desktop. 
| public interface IDistributedCache | |
| { | |
| T? GetOrCreate(string key, Func<DistributedCacheEntryOptions, T> factory) | |
| { | |
| var bytes = Get(key); | |
| if (bytes is { Length: > 0 }) | |
| { | |
| var payload = Encoding.UTF8.GetString(bytes); | |
| return JsonSerializer.Deserialize<T>(payload); | |
| } | |
| var options = new DistributedCacheEntryOptions(); | |
| var value = factory(options); | |
| Set(key, value, options); | |
| return value; | |
| } | |
| byte[]? Get(string key); | |
| async Task<T?> GetOrCreateAsync( | |
| string key, | |
| Func<DistributedCacheEntryOptions, Task<T>> factory, | |
| CancellationToken token = default(CancellationToken)) | |
| { | |
| var bytes = await GetAsync(key, token); | |
| if (bytes is { Length: > 0 }) | |
| { | |
| var payload = Encoding.UTF8.GetString(bytes); | |
| return JsonSerializer.Deserialize<T>(payload); | |
| } | |
| var options = new DistributedCacheEntryOptions(); | |
| var value = await factory(options); | |
| await SetAsync(key, value, options, token); | |
| return value; | |
| } | |
| Task<byte[]?> GetAsync(string key, CancellationToken token = default(CancellationToken)); | |
| void Set(string key, byte[] value, DistributedCacheEntryOptions options); | |
| Task SetAsync( | |
| string key, | |
| byte[] value, | |
| DistributedCacheEntryOptions options, | |
| CancellationToken token = default(CancellationToken)); | |
| void Refresh(string key); | |
| Task RefreshAsync(string key, CancellationToken token = default(CancellationToken)); | |
| void Remove(string key); | |
| Task RemoveAsync(string key, CancellationToken token = default(CancellationToken)); | |
| } | 
Thoughts:
- serialization choice is hugely subjective; I would be very cautious asserting JSON, or even any specific JSON serializer, as a choice
- making serialization pluggable is ... fun; honestly, I think it would be better to handle serialization at a layer above - perhaps abstracting away the storage layer (that just talks bytes) from the functional layer (with services such as local cache store, perhaps serialization, etc)
- if we are looking to revisit the API, byte[]is simply a bad choice - we should preferReadOnlyMemory<byte>orReadOnlySequence<byte>- likewise, for writing we should probably think in terms ofIBufferWriter<byte>
- we should think "cache invalidation"; for example, the .NET 7 output cache API supports "tags" as a concept allowing cross-key cache invalidation; whatever logic gave rise to that requirement presumably should be considered an over-arching consideration in caching
- ditto "etag"; in particular, I'm thinking in terms of output cache here; currently, to implement etag, we need to fetch the local cache locally; this allows us to short-circuit the app-to-client connection, but still forces us to exercise the storage-to-app connection; ideally, we should be able to have a "here's my etag; if it is a match, don't bother even fetching things locally - we're going to short-circuit another way"
- I'd also want to think ahead to multi-layer caching, in particular thinking in terms of things like server-assisted-caching in the redis sense, and other such mechanisms
The topic of cache and revising has come up from multiple directions in the last few weeks; I probably have quite a few opinions to add there (from the combined perspectives of: aspnet cache, storage, serialization, multi-tier, etc); please feel free to rope me in to conversations. Likewise, let me know if I can loop you in with any of the other parallel discussions (or talk to Glenn)
When I implement methods like this on IMemoryCache I use a SemaphoreSlim when adding an item to the cache, would this need something similar?
Hi @mbrdev - potentially, that's not a bad idea. At that point, we wouldn't be able to do the default interface implementation approach. Unless the default is a simpler one like proposed here, then current implementations could optionally implement this, overriding the default - and provide those types of mechanisms.
Thoughts:
- serialization choice is hugely subjective; I would be very cautious asserting JSON, or even any specific JSON serializer, as a choice
- making serialization pluggable is ... fun; honestly, I think it would be better to handle serialization at a layer above - perhaps abstracting away the storage layer (that just talks bytes) from the functional layer (with services such as local cache store, perhaps serialization, etc)
- if we are looking to revisit the API,
byte[]is simply a bad choice - we should preferReadOnlyMemory<byte>orReadOnlySequence<byte>- likewise, for writing we should probably think in terms ofIBufferWriter<byte>- we should think "cache invalidation"; for example, the .NET 7 output cache API supports "tags" as a concept allowing cross-key cache invalidation; whatever logic gave rise to that requirement presumably should be considered an over-arching consideration in caching
- ditto "etag"; in particular, I'm thinking in terms of output cache here; currently, to implement etag, we need to fetch the local cache locally; this allows us to short-circuit the app-to-client connection, but still forces us to exercise the storage-to-app connection; ideally, we should be able to have a "here's my etag; if it is a match, don't bother even fetching things locally - we're going to short-circuit another way"
- I'd also want to think ahead to multi-layer caching, in particular thinking in terms of things like server-assisted-caching in the redis sense, and other such mechanisms
The topic of cache and revising has come up from multiple directions in the last few weeks; I probably have quite a few opinions to add there (from the combined perspectives of: aspnet cache, storage, serialization, multi-tier, etc); please feel free to rope me in to conversations. Likewise, let me know if I can loop you in with any of the other parallel discussions (or talk to Glenn)
Hi @mgravell,
serialization choice is hugely subjective; I would be very cautious asserting JSON, or even any specific JSON serializer, as a choice
Absolutely! I agree, but as a default, why not use the System.Text.Json? To your point this could be "pluggable", but as a starting point, it seems like a reasonable default.
if we are looking to revisit the API,
byte[]is simply a bad choice - we should preferReadOnlyMemory<byte>orReadOnlySequence<byte>- likewise, for writing we should probably think in terms ofIBufferWriter<byte>
The byte[] is one of the things that got me looking at this API in the first place, it could benefit from ReadOnlyMemory<byte> or ReadOnlySequence<byte>, but thinking about the consumer standpoint, the convenience of generics and custom strong-types (POCOs and the like) are really appealing.
we should think "cache invalidation"; for example, the .NET 7 output cache API supports "tags" as a concept allowing cross-key cache invalidation; whatever logic gave rise to that requirement presumably should be considered an over-arching consideration in caching
I do like what has been implemented for output caching with ASP.NET Core, and I agree that "cache invalidation" should also exist in this API.
I'd love to get more involved with some of these discussions, even if only to be a fly on the wall and listen/learn. Thanks for thinking of me in that regard.
When I implement methods like this on IMemoryCache I use a SemaphoreSlim when adding an item to the cache, would this need something similar?