You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: docs/DependencyInjection.md
+68-9
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@
8
8
9
9
| ⚡ TL;DR (quick version) |
10
10
| -------- |
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. |
12
12
13
13
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.
14
14
@@ -58,7 +58,7 @@ public class MyController : Controller
58
58
59
59
In this way the `cache` param will be automatically populated by the DI framework.
60
60
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.
62
62
63
63
But usually we want to do more, right? Maybe add a distributed cache or configure some options.
64
64
@@ -117,27 +117,33 @@ FusionCache has a _unified_ approach, and everything is (hopefully) very uniform
117
117
118
118
For example for the memory cache we can tell FusionCache:
119
119
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
122
124
-`WithMemoryCache(IMemoryCache memoryCache)`: USE an instance that we provide DIRECTLY
123
125
-`WithMemoryCache(Func<IServiceProvider, IMemoryCache> factory)`: USE an instance built via a FACTORY that we provide
124
126
125
127
The same is available for other components, like the backplane for example:
This approach is currently available for these components:
135
139
- logger
140
+
- memory locker
136
141
- memory cache
137
142
- distributed cache + serializer
138
143
- backplane
144
+
- plugins (slightly different, see below)
139
145
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.
141
147
142
148
### ⚙️ Distributed cache configuration
143
149
@@ -151,8 +157,9 @@ Because of this, in these methods there are some extra params like:
151
157
152
158
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.
153
159
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).
155
161
162
+
Also, we can call `WithPlugin(...)` multiple times and add multiple plugins to the same FusionCache instance.
156
163
157
164
### ⚙️ Post Setup
158
165
@@ -162,7 +169,7 @@ To do that we have at our disposal a `WithPostSetup(Action<IServiceProvider, IFu
162
169
163
170
### ⚙️ Auto-setup
164
171
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.
166
173
167
174
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.
168
175
@@ -216,8 +223,60 @@ services.AddFusionCache()
216
223
217
224
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).
218
225
219
-
### 💪 It's our choice
220
-
221
226
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.
222
227
223
228
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`:
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.
Copy file name to clipboardexpand all lines: docs/NamedCaches.md
+1-1
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@
8
8
9
9
| ⚡ TL;DR (quick version) |
10
10
| -------- |
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). |
12
12
13
13
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.
0 commit comments