Name | Modified | Size | Downloads / Week |
---|---|---|---|
Parent folder | |||
README.md | 2025-06-09 | 8.6 kB | |
v2.3.0 source code.tar.gz | 2025-06-09 | 4.8 MB | |
v2.3.0 source code.zip | 2025-06-09 | 5.2 MB | |
Totals: 3 Items | 10.0 MB | 5 |
🔑 Access the cache key in the factory context
Community user @posledam and others asked for the ability to access the cache key in the factory context, to be able to work with it while going to the database or similar, without creating a closure with the lambda.
So that's what I added, but there's a catch here: FusionCache provides automatic cache key manipulation with things like CacheKeyPrefix
usually in conjunction with things like Named Caches, so it would be nice to access both the original one and the processed one.
Therefore I added both of them in the factory context, and it can be used like this:
:::c#
cache.GetOrSet<string>(
"foo",
(ctx, token) => {
ctx.Key; // THE (PROCESSED) CACHE KEY
ctx.OriginalKey; // THE (ORIGINAL) CACHE KEY
}
);
See here for the original issue, and here for the design issue.
⚙️ New InternalStrings
options
FusionCache automatically handles a lot of things for us, and to do that it may need to manipulate some strings used internally like the cache key or the backplane channel name.
For example to use the CacheName
to automatically separate data in a shared cache when used in conjunction with other FusionCache instances, or the set of special cache keys used with Tagging or the way wire format versioning is handled to automatically avoid errors when evolving the internal data structures.
In these cases some special characters are used as separators.
This is all good and well, but recently community user @stebet started working on a NATS version of the backplane (and that's awesome!) and he noticed that some of these special characters create issues on NATS, which has some reserved characters that have special meaning or cannot be used anyway.
Because of this I'm adding a new set of options specifically for changing the set of internal strings used by FusionCache, so that it's possible to work with systems like NATS and avoid issues.
So now we have a new InternalStrings
option inside of FusionCacheOptions
where we can set strings like:
- TagCacheKeyPrefix
- ClearRemoveTag
- DistributedCacheWireFormatSeparator
- BackplaneWireFormatSeparator
- and more
It can be used like this:
:::c#
var options = new FusionCacheOptions()
{
// ...
InternalStrings = {
TagCacheKeyPrefix = "tag__",
ClearRemoveTag = "clear_remove"
// ...
}
};
But there issue here that I can already foresee: in the future I may need to add new internal strings, and anyone who had carefully set them to something "safe" for them will start having issues after updating to the new version with the new internal strings.
Therefore I also added a new method on the internal strings class, a method that I will keep updating in case new strings will be needed of course, and that simply set them to values that uses a commonly accepted safe set of characters, meaning only alphanumeric characters + some common separators.
It can be used like this:
:::c#
options.InternalStrings.SetToSafeStrings();
or, if we want to customize the couple of special chars, like this:
:::c#
options.InternalStrings.SetToSafeStrings(
separator: '-',
specialChar: '_'
);
In this way it will be possible to keep every little detail under control and always be future proof.
See here for the original issue.
⚙️ New FusionCacheEntryOptionsProvider
Community user @gleb-osokin was looking for a way to have DefaultEntryOptions
specific for cache keys, to avoid having only one global entry options and have a way to automatically use the right one based on some custom logic instead of having to specify them at every call site.
The design took some time and some back and forth since the default entry options existed since the beginning, and to get things in the right shape has been a particularly delicate effort.
But now we have a new FusionCacheEntryOptionsProvider
abstract class which anyone can implement with their own custom logic, and that we can set in the FusionCacheOptions
object that we pass to create a FusionCache instance.
Nothing else needs to change, and the per-key provider, if any, is now automatically considered and used.
Particular care has been put into allowing users to have their custom logic without having a ton of new allocations.
Here's the thing:
:::c#
/// <summary>
/// A provider to get <see cref="FusionCacheEntryOptions"/> based on a key.
/// <br/><br/>
/// ⚠️ <strong>IMPORTANT:</strong> in your GetEntryOptions() implementation carefully set the canMutate out param to indicate if the returned object can be mutated or not.
/// </summary>
public abstract class FusionCacheEntryOptionsProvider
{
/// <summary>
/// Provide entry options based on a key, by either returning a new instance or a reference to an existing one (for improved performance).
/// <br/><br/>
/// ⚠️ <strong>IMPORTANT:</strong> carefully set the <paramref name="canMutate"/> out param to indicate if the returned object can be mutated or not.
/// </summary>
/// <param name="ctx">The context, containing supporting features.</param>
/// <param name="key">The cache key.</param>
/// <param name="canMutate">An out parameter that indicate if the returned object can be mutated.</param>
/// <returns>The entry options.</returns>
public abstract FusionCacheEntryOptions? GetEntryOptions(FusionCacheEntryOptionsProviderContext ctx, string key, out bool canMutate);
}
As we can see extra care has been put into the xml comments, to warn implementers about the fact that they have to pay attention to the canMutate
param, which is fundamental to signal that the returned entry options can bu mutated or not (and FusionCache then will take care of the rest, eventually duplicating it if needed).
A new method has been also added to IFusionCache
: historically we had the CreateEntryOptions(...)
, and now we also have the CreateEntryOptions(key, ...)
variant to include the key in the logic.
Here is the original PR, and here the design issue.
🐞 Fix for skipped check in read-only methods
Community user @permagne noticed that read-only methods (eg: TryGet
and GetOrDefault
) were not considering the SkipDistributedCacheRead
entry option: this, in case of a cache miss, means that every call would go to the L2, slowing things down.
This has now been solved.
See here for the original issue.
✅ Update to xUnit v3
I finally took some time to update all the tests to xUnit v3, which has been out for some time now.
On top of this I also added some more tests to cover some missing scenarios, getting the size of the test suite to:
Almost 1500, not bad.
📕 Docs
And finally the docs, which I care a lot about.
I have updated some, like:
- Clear, mostly about the difference between Clear(true)
and Clear(false)
- Core Methods, mostly updating how Expire()
works
- Cache Levels, specifically adding a part about the envelope used with L2 and specify better the wire format versioning logic
- Tagging, mostly related to some common questions about other massive operations, like searching
Some of these came from always welcome questions by community members like @GeddesJ , @bebo-dot-dev , @martinkoslof and @jundayin : thanks!