Skip to content

Commit c5b2d1b

Browse files
committed
Docs
1 parent bc46847 commit c5b2d1b

File tree

2 files changed

+69
-10
lines changed

2 files changed

+69
-10
lines changed

docs/DependencyInjection.md

+68-9
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
| ⚡ TL;DR (quick version) |
1010
| -------- |
11-
| FusionCache natively supports .NET dependency injection, along with a builder to allow for easy customization: all the features, options and components are supported, thanks to the fluent api and specific calls for each feature. |
11+
| FusionCache natively supports .NET dependency injection including keyed services, along with a builder to allow for easy customization: all the features, options and components are supported, thanks to a rich fluent API. |
1212

1313
In .NET there's full support for [Dependency Injection (DI)](https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection), a design pattern to achieve a form of Inversion of Control (IoC) in our code.
1414

@@ -58,7 +58,7 @@ public class MyController : Controller
5858

5959
In this way the `cache` param will be automatically populated by the DI framework.
6060

61-
By simply calling `services.AddFusionCache()` we will have FusionCache configured with the default options and using only a memory cache (1st level) and nothing else: no distributed cache (2nd level), no backplane nor any plugin.
61+
By simply calling `services.AddFusionCache()` we will have FusionCache configured with the default options and using only a memory cache (L1) and nothing else: no distributed cache (L2), no backplane nor any plugin.
6262

6363
But usually we want to do more, right? Maybe add a distributed cache or configure some options.
6464

@@ -117,27 +117,33 @@ FusionCache has a _unified_ approach, and everything is (hopefully) very uniform
117117

118118
For example for the memory cache we can tell FusionCache:
119119

120-
- `WithRegisteredMemoryCache()`: USE the one REGISTERED in the DI container (if not there, an exception will be thrown)
121-
- `TryWithRegisteredMemoryCache()`: TRY TO USE the one REGISTERED in the DI container (if not there, no problem)
120+
- `WithRegisteredMemoryCache()`: USE the one registered in the DI container. If not there, an EXCEPTION will be thrown
121+
- `TryWithRegisteredMemoryCache()`: TRY TO USE the one registered in the DI container. If not there, no problem
122+
- `WithRegisteredKeyedMemoryCache(object? serviceKey)`: USE the one registered in the DI container with the specified SERVICE KEY (or the `CacheName` if not specified). If not there, an EXCEPTION will be thrown
123+
- `TryWithRegisteredKeyedMemoryCache(object? serviceKey)`: TRY TO USE the one registered in the DI container with the specified SERVICE KEY (or the `CacheName` if not specified). If not there, no problem
122124
- `WithMemoryCache(IMemoryCache memoryCache)`: USE an instance that we provide DIRECTLY
123125
- `WithMemoryCache(Func<IServiceProvider, IMemoryCache> factory)`: USE an instance built via a FACTORY that we provide
124126

125127
The same is available for other components, like the backplane for example:
126128

127129
- `WithRegisteredBackplane()`
128130
- `TryWithRegisteredBackplane()`
131+
- `WithRegisteredKeyedBackplane(object? serviceKey)`
132+
- `TryWithRegisteredKeyedBackplane(object? serviceKey)`
129133
- `WithBackplane(IFusionCacheBackplane backplane)`
130134
- `WithBackplane(Func<IServiceProvider, IFusionCacheBackplane> factory)`
131135

132136
and so on.
133137

134138
This approach is currently available for these components:
135139
- logger
140+
- memory locker
136141
- memory cache
137142
- distributed cache + serializer
138143
- backplane
144+
- plugins (slightly different, see below)
139145

140-
Of course you can mix these approach for the different components for example by using the registered logger, a specific memory cache via an instance, a backplane via a factory and so on.
146+
Of course we can mix these approach for the different components for example by using the registered logger, a specific memory cache via an instance, a backplane via a factory and so on.
141147

142148
### ⚙️ Distributed cache configuration
143149

@@ -151,8 +157,9 @@ Because of this, in these methods there are some extra params like:
151157

152158
Everything is the same, but since we can have multiple plugins some methods works in a "plural" way: for example we have `WithAllRegisteredPlugins()` which will use all of the registered `IFusionCachePlugin` services, not just one.
153159

154-
Also, we can call `WithPlugin(...)` multiple times and add multiple plugins to the same FusionCache instance.
160+
There's also the _keyed_ version via `WithAllRegisteredKeyedPlugins(object? serviceKey)`, which act as we can imagine: same as above but by a specified service key (or by `CacheName` if not specified).
155161

162+
Also, we can call `WithPlugin(...)` multiple times and add multiple plugins to the same FusionCache instance.
156163

157164
### ⚙️ Post Setup
158165

@@ -162,7 +169,7 @@ To do that we have at our disposal a `WithPostSetup(Action<IServiceProvider, IFu
162169

163170
### ⚙️ Auto-setup
164171

165-
It's probably not that frequent, but let's say you would like FusionCache to try and look into the DI container and basically use everything it can. Found a backplane? Use it. Found a valid distributed cache + a valid serializer? Use them. Any plugin found? Yep, use them all.
172+
It's probably not that frequent, but let's say we would like FusionCache to try and look into the DI container and basically use everything it can. Found a backplane? Use it. Found a valid distributed cache + a valid serializer? Use them. Any plugin found? Yep, use them all.
166173

167174
In this way we don't have a lot of control about what has been done so we may have surprises at runtime, but if this is what we want then we can use the `TryWithAutoSetup()` method: as the name implies, it will _try_ to _automatically_ do a _setup_ of what it can find.
168175

@@ -216,8 +223,60 @@ services.AddFusionCache()
216223

217224
As previously stated, by default nothing is done automagically: the only exceptions to this rule - because of how these specific components work - are the logger, the options and the serializer. The serializer by the way will only be used if it's needed (eg: when also using a distributed cache).
218225

219-
### 💪 It's our choice
220-
221226
All in all, we may prefer flexibility in what we register and then use what is registered, or we may prefer to directly specify what we want to use.
222227

223228
FusionCache supports both approaches, and let us choose what we prefer.
229+
230+
## 🔑 Keyed Services
231+
232+
FusionCache natively supports multiple [Named Caches](NamedCaches.md) thanks to the `IFusionCacheProvider` interface: require it via DI and we can ask for a particular cache with the specified name via `provider.GetCache(name)`.
233+
234+
Since .NET 8 though we now have native support for multiple services of the same type, identified by different keys, thanks to the addition of so called [keyed services](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-8.0#keyed-services).
235+
236+
So, can the two approaches work together? Oh yeah, totally.
237+
238+
The idea is that, when registering a FusionCache instance, we simply add `.AsKeyedService()`, and that's it: that cache will be available _both_ via named caches and via keyed services.
239+
240+
As an example, at startup we simply do this:
241+
242+
```csharp
243+
services.AddFusionCache("MyCache")
244+
.AsKeyedService();
245+
```
246+
247+
and later, to resolve it, we either use `IFusionCacheProvider`:
248+
249+
```csharp
250+
app.MapGet("/foo", (IFusionCacheProvider cacheProvider) => {
251+
var cache = cacheProvider.GetCache("MyCache");
252+
cache.Set("cache-key", 42);
253+
});
254+
```
255+
256+
or just mark a constructor param or web action param of type `IFusionCache` with the `FromKeyedServices` attribute:
257+
258+
```csharp
259+
app.MapGet("/foo", ([FromKeyedServices("MyCache")] IFusionCache) => {
260+
cache.Set("cache-key", 42);
261+
});
262+
```
263+
264+
Nice, eh?
265+
266+
We can even specify a different service key by using `AsKeyedService(myServiceKey)`, where the key can be either of type `string` or anything else really, since the Keyed Services approach in .NET supports that.
267+
268+
So, the next question is which one to choose?
269+
270+
The `[FromKeyedServices("MyCache")]` approach is declarative and quicker, but the `IFusionCacheProvider` approach is programmatic and more powerful, since we can add logic to the selection of the cache name, while allowing us to use both the `GetCache(name)` or `GetCacheOrNull(name)` approach, adding a fallback logic or whatever we can think of.
271+
272+
Basically it's up to us, and as always we can mix and match the two approaches in different scenarios, freely.
273+
274+
But _registering_ FusionCache as a keyed service is one half of the story: the other half is how to _consume_ keyed services as FusionCache components.
275+
276+
Can we do that too? Yep!
277+
278+
When registering a FusionCache instance via the builder we always had the ability to say, for example, `WithRegisteredBackplane()` and FusionCache will look for a service of type `IFusionCacheBackplane` right? Cool now it's also possible to say `WithRegisteredKeyedBackplane(object? serviceKey)` to pick the service of type `IFusionCacheBackplane` registered with the specified `serviceKey` or even just say `WithRegisteredKeyedBackplane()` to do the same but using the `CacheName` as the service key.
279+
280+
This is available for all the building blocks: logger, memory cache, memory locker, distributed cache, serializer, backplane and plugins.
281+
282+
Nice.

docs/NamedCaches.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
| ⚡ TL;DR (quick version) |
1010
| -------- |
11-
| It's possible do register, configure and request multiple named caches: simply register each one and give them a different name (and configuration) and they'll all just work. |
11+
| It's possible do register, configure and request multiple named caches: simply register each one and give them a different name (and configuration) and they'll all just work, both via `IFusionCacheProvider` and via [Keyed Services](DependencyInjection.md). |
1212

1313
Just like with the standard [named http clients](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-7.0#named-clients) in .NET, with FusionCache it's possible to have multiple named caches.
1414

0 commit comments

Comments
 (0)