From dcf3763ad8d5678bce6bf83987e939c3409bcda5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 1 Oct 2020 20:45:05 +0300 Subject: [PATCH 1/4] Added Unit Of Work Level Cache --- docs/en/Caching.md | 51 ++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/docs/en/Caching.md b/docs/en/Caching.md index 7de7d82f1e..0f175c488e 100644 --- a/docs/en/Caching.md +++ b/docs/en/Caching.md @@ -2,7 +2,7 @@ ABP Framework extends the [ASP.NET Core distributed cache](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed). -## Volo.Abp.Caching Package +## Installation > This package is already installed by default with the [application startup template](Startup-Templates/Application.md). So, most of the time, you don't need to install it manually. @@ -14,7 +14,9 @@ abp add-package Volo.Abp.Caching You need to run this command on a command line terminal in a folder containing a `csproj` file (see [other options](https://abp.io/package-detail/Volo.Abp.Caching) to install). -## `IDistributedCache` Interface +## Usage + +### `IDistributedCache` Interface ASP.NET Core defines the `IDistributedCache` interface to get/set the cache values. But it has some difficulties: @@ -29,7 +31,7 @@ ASP.NET Core defines the `IDistributedCache` interface to get/set the cache valu See [ASP.NET Core's distributed caching document](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed) for more information. -## `IDistributedCache` Interface +### `IDistributedCache` Interface ABP framework defines the generic `IDistributedCache` interface in the [Volo.Abp.Caching](https://www.nuget.org/packages/Volo.Abp.Caching/) package. `TCacheItem` is the type of the object stored in the cache. @@ -41,9 +43,7 @@ ABP framework defines the generic `IDistributedCache` interface in t * Allows to define a **global cache key prefix** per application, so different applications can use their isolated key pools in a shared distributed cache server. * ABP's distributed cache also **can tolerate errors** wherever possible and bypasses the cache. This is useful when you have temporary problems on the cache server. -### Usage - -An example class to store an item in the cache: +**Example: Store Book names and prices in the cache** ````csharp namespace MyProject @@ -103,13 +103,11 @@ namespace MyProject Other methods of the `IDistributedCache` are same as ASP.NET Core's `IDistributedCache` interface, so you can refer [it's documentation](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed). -## `IDistributedCache` Interface +### `IDistributedCache` Interface `IDistributedCache` interface assumes that the type of your **cache key** is `string` (so, you need to manually convert your key to string if you need to use a different kind of cache key). While this is not a big deal, `IDistributedCache` can be used when your cache key type is not `string`. -### Usage - -An example class to store an item in the cache: +**Example: Store Book names and prices in the cache** ````csharp using Volo.Abp.Caching; @@ -126,7 +124,9 @@ namespace MyProject } ```` -Example usage (assumes that your cache key type is `Guid`): +* This example uses the `CacheName` attribute for the `BookCacheItem` class to set the cache name. + +You can inject and use the `IDistributedCache` service to get/set `BookCacheItem` objects: ````csharp using System; @@ -167,7 +167,8 @@ namespace MyProject * This sample service uses the `GetOrAddAsync()` method to get a book item from the cache. * Since cache explicitly implemented as using `Guid` as cache key, `Guid` value passed to `_cache_GetOrAddAsync()` method. -* This example uses the `CacheName` attribute for the `BookCacheItem` class. + +#### Complex Types as the Cache Key `IDistributedCache` internally uses `ToString()` method of the key object to convert it to a string. If you need to use a complex object as the cache key, you need to override `ToString` method of your class. @@ -205,16 +206,6 @@ public class BookService : ITransientDependency } ```` -## Error Handling - -When you design a cache for your objects, you typically try to get the value from cache first. If not found in the cache, you query the object from the **original source**. It may be located in a **database** or may require to perform an HTTP call to a remote server. - -In most cases, you want to **tolerate the cache errors**; If you get error from the cache server you don't want to cancel the operation. Instead, you silently hide (and log) the error and **query from the original source**. This is what the ABP Framework does by default. - -ABP's Distributed Cache [handle](Exception-Handling.md), log and hide errors by default. There is an option to change this globally (see the options below). - -In addition, all of the `IDistributedCache` (and `IDistributedCache`) methods have an optional `hideErrors` parameter, which is `null` by default. The global value is used if this parameter left as `null`, otherwise you can decide to hide or throw the exceptions for individual method calls. - ## Configuration ### AbpDistributedCacheOptions @@ -238,8 +229,24 @@ Configure(options => * `KeyPrefix` (`string`, default: `null`): If your cache server is shared by multiple applications, you can set a prefix for the cache keys for your application. In this case, different applications can not overwrite each other's cache items. * `GlobalCacheEntryOptions` (`DistributedCacheEntryOptions`): Used to set default distributed cache options (like `AbsoluteExpiration` and `SlidingExpiration`) used when you don't specify the options while saving cache items. Default value uses the `SlidingExpiration` as 20 minutes. +## Error Handling + +When you design a cache for your objects, you typically try to get the value from cache first. If not found in the cache, you query the object from the **original source**. It may be located in a **database** or may require to perform an HTTP call to a remote server. + +In most cases, you want to **tolerate the cache errors**; If you get error from the cache server you don't want to cancel the operation. Instead, you silently hide (and log) the error and **query from the original source**. This is what the ABP Framework does by default. + +ABP's Distributed Cache [handle](Exception-Handling.md), log and hide errors by default. There is an option to change this globally (see the options below). + +In addition, all of the `IDistributedCache` (and `IDistributedCache`) methods have an optional `hideErrors` parameter, which is `null` by default. The global value is used if this parameter left as `null`, otherwise you can decide to hide or throw the exceptions for individual method calls. + ## Advanced Topics +### Unit Of Work Level Cache + +Distributed cache service provides an interesting feature. Assume that you've updated the price of a book in the database, then set the new price to the cache, so you can use the cached value later. What if you have an exception after setting the cache and you **rollback the transaction** that updates the price of the book? In this case, cache value will be incorrect. + +`IDistributedCache<..>` methods gets an optional parameter, named `considerOuw`, which is `false` by default. If you set it to `true`, then the changes you made for the cache are not actually applied to the real cache store, but associated with the current [unit of work](Unit-Of-Work.md). You get the value you set in the same unit of work, but the changes are applied **only if the current unit of work succeed**. + ### IDistributedCacheSerializer `IDistributedCacheSerializer` service is used to serialize and deserialize the cache items. Default implementation is the `Utf8JsonDistributedCacheSerializer` class that uses `IJsonSerializer` service to convert objects to [JSON](Json.md) and vice verse. Then it uses UTC8 encoding to convert the JSON string to a byte array which is accepted by the distributed cache. From 14bd12b176b6edf3b1ed519d77361c280f088f76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 1 Oct 2020 21:15:23 +0300 Subject: [PATCH 2/4] Add the Redis-Cache document --- docs/en/Caching.md | 8 ++++++-- docs/en/Redis-Cache.md | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 docs/en/Redis-Cache.md diff --git a/docs/en/Caching.md b/docs/en/Caching.md index 0f175c488e..7a29247628 100644 --- a/docs/en/Caching.md +++ b/docs/en/Caching.md @@ -27,7 +27,7 @@ ASP.NET Core defines the `IDistributedCache` interface to get/set the cache valu > `IDistributedCache` is defined in the `Microsoft.Extensions.Caching.Abstractions` package. That means it is not only usable for ASP.NET Core applications, but also available to **any type of applications**. -> Default implementation of the `IDistributedCache` interface is the `MemoryDistributedCache` which works **in-memory**. See [ASP.NET Core's documentation](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed) to see how to switch to Redis or another cache provider. +> Default implementation of the `IDistributedCache` interface is the `MemoryDistributedCache` which works **in-memory**. See [ASP.NET Core's documentation](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed) to see how to switch to Redis or another cache provider. Also, see the [Redis Cache](Redis-Cache.md) document if you want to use the Redis as the distributed cache server. See [ASP.NET Core's distributed caching document](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed) for more information. @@ -255,4 +255,8 @@ You can [replace](Dependency-Injection.md) this service by your own implementati ### IDistributedCacheKeyNormalizer -`IDistributedCacheKeyNormalizer` is implemented by the `DistributedCacheKeyNormalizer` class by default. It adds cache name, application cache prefix and current tenant id to the cache key. If you need a more advanced key normalization, you can [replace](Dependency-Injection.md) this service by your own implementation. \ No newline at end of file +`IDistributedCacheKeyNormalizer` is implemented by the `DistributedCacheKeyNormalizer` class by default. It adds cache name, application cache prefix and current tenant id to the cache key. If you need a more advanced key normalization, you can [replace](Dependency-Injection.md) this service by your own implementation. + +## See Also + +* [Redis Cache](Redis-Cache.md) \ No newline at end of file diff --git a/docs/en/Redis-Cache.md b/docs/en/Redis-Cache.md new file mode 100644 index 0000000000..ee6b7056ae --- /dev/null +++ b/docs/en/Redis-Cache.md @@ -0,0 +1,39 @@ +# Redis Cache + +ABP Framework [Caching System](Caching.md) extends the [ASP.NET Core distributed cache](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed). So, **any provider** supported by the standard ASP.NET Core distributed cache can be usable in your application and can be configured just like **documented by Microsoft**. + +However, ABP provides an **integration package** for Redis Cache: [Volo.Abp.Caching.StackExchangeRedis](https://www.nuget.org/packages/Volo.Abp.Caching.StackExchangeRedis). There are two reasons for using this package, instead of the standard [Microsoft.Extensions.Caching.StackExchangeRedis](https://www.nuget.org/packages/Microsoft.Extensions.Caching.StackExchangeRedis/) package. + +1. It implements `SetManyAsync` and `GetManyAsync` methods. These are not standard methods of the Microsoft Caching library, but added by the ABP Framework [Caching](Caching.md) system. They **significiantly increases the performance** when you need to set/get multiple cache items with a single method call. +2. It **simplifies** the Redis cache **configuration** (will be explained below). + +> Volo.Abp.Caching.StackExchangeRedis is already depends on the Microsoft.Extensions.Caching.StackExchangeRedis package, but extends and improves it. + +## Installation + +> This package is already installed in the application startup template if it is using Redis. + +Open a command line in the folder of your `.csproj` file and type the following ABP CLI command: + +````bash +abp add-package Volo.Abp.Caching.StackExchangeRedis +```` + +## Configuration + +Volo.Abp.Caching.StackExchangeRedis package automatically gets the redis [configuration](Configuration.md) from the `IConfiguration`. So, for example, you can set your configuration inside the `appsettings.json`: + +````js +"Redis": { + "Configuration": "127.0.0.1" +} +```` + +Alternatively you can configure the standard [RedisCacheOptions](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.caching.stackexchangeredis.rediscacheoptions) [options](Options.md) class in the `ConfigureServices` method of your [module](Module-Development-Basics.md): + +````csharp +Configure(options => +{ + //... +}); +```` \ No newline at end of file From 9058e97cdccd20c6edc28155552b39cf637582f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 1 Oct 2020 21:28:54 +0300 Subject: [PATCH 3/4] Added Batch Operations section to the cache document --- docs/en/Caching.md | 16 +++++++++++++--- docs/en/Redis-Cache.md | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/en/Caching.md b/docs/en/Caching.md index 7a29247628..4da27e4e98 100644 --- a/docs/en/Caching.md +++ b/docs/en/Caching.md @@ -41,7 +41,8 @@ ABP framework defines the generic `IDistributedCache` interface in t * It automatically adds a **cache name** prefix to the cache keys based on the object type stored in the cache. Default cache name is the full name of the cache item class (`CacheItem` postfix is removed if your cache item class ends with it). You can use the **`CacheName` attribute** on the cache item class to set the cache name. * It automatically adds the **current tenant id** to the cache key to distinguish cache items for different tenants (if your application is [multi-tenant](Multi-Tenancy.md)). Define `IgnoreMultiTenancy` attribute on the cache item class to disable this if you want to share the cached objects among all tenants in a multi-tenant application. * Allows to define a **global cache key prefix** per application, so different applications can use their isolated key pools in a shared distributed cache server. -* ABP's distributed cache also **can tolerate errors** wherever possible and bypasses the cache. This is useful when you have temporary problems on the cache server. +* It **can tolerate errors** wherever possible and bypasses the cache. This is useful when you have temporary problems on the cache server. +* It has methods like `GetManyAsync` and `SetManyAsync` which significantly improve the performance on **batch operations**. **Example: Store Book names and prices in the cache** @@ -97,11 +98,11 @@ namespace MyProject } ```` -* This sample service uses the `GetOrAddAsync()` method to get a book item from the cache. +* This sample service uses the `GetOrAddAsync()` method to get a book item from the cache. `GetOrAddAsync` is an additional method that was added by the ABP Framework to the standard ASP.NET Core distributed cache methods. * If the book was not found in the cache, it calls the factory method (`GetBookFromDatabaseAsync` in this case) to retrieve the book item from the original source. * `GetOrAddAsync` optionally gets a `DistributedCacheEntryOptions` which can be used to set the lifetime of the cached item. -Other methods of the `IDistributedCache` are same as ASP.NET Core's `IDistributedCache` interface, so you can refer [it's documentation](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed). +`IDistributedCache` supports the same methods of the ASP.NET Core's standard `IDistributedCache` interface, so you can refer [it's documentation](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed). ### `IDistributedCache` Interface @@ -239,6 +240,15 @@ ABP's Distributed Cache [handle](Exception-Handling.md), log and hide errors by In addition, all of the `IDistributedCache` (and `IDistributedCache`) methods have an optional `hideErrors` parameter, which is `null` by default. The global value is used if this parameter left as `null`, otherwise you can decide to hide or throw the exceptions for individual method calls. +## Batch Operations + +ABP's distributed cache interfaces provide methods to perform batch get/set methods those improves the performance when you want to get or set multiple cache items in a single method call. + +* `SetManyAsync` and `SetMany` methods can be used to set multiple values to the cache. +* `GetManyAsync` and `GetMany` methods can be used to retrieve multiple values from the cache. + +> These are not standard methods of the ASP.NET Core caching. So, a provide may not support them. They are supported by the [ABP Redis Cache integration package](Redis-Cache.md). + ## Advanced Topics ### Unit Of Work Level Cache diff --git a/docs/en/Redis-Cache.md b/docs/en/Redis-Cache.md index ee6b7056ae..584ae40306 100644 --- a/docs/en/Redis-Cache.md +++ b/docs/en/Redis-Cache.md @@ -7,7 +7,7 @@ However, ABP provides an **integration package** for Redis Cache: [Volo.Abp.Cach 1. It implements `SetManyAsync` and `GetManyAsync` methods. These are not standard methods of the Microsoft Caching library, but added by the ABP Framework [Caching](Caching.md) system. They **significiantly increases the performance** when you need to set/get multiple cache items with a single method call. 2. It **simplifies** the Redis cache **configuration** (will be explained below). -> Volo.Abp.Caching.StackExchangeRedis is already depends on the Microsoft.Extensions.Caching.StackExchangeRedis package, but extends and improves it. +> Volo.Abp.Caching.StackExchangeRedis is already uses the Microsoft.Extensions.Caching.StackExchangeRedis package, but extends and improves it. ## Installation From 4a1fa49faefb813eee223fda6200fa8a5c78cba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Thu, 1 Oct 2020 21:32:57 +0300 Subject: [PATCH 4/4] Update Caching.md --- docs/en/Caching.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Caching.md b/docs/en/Caching.md index 4da27e4e98..042f13ffe0 100644 --- a/docs/en/Caching.md +++ b/docs/en/Caching.md @@ -247,7 +247,7 @@ ABP's distributed cache interfaces provide methods to perform batch get/set meth * `SetManyAsync` and `SetMany` methods can be used to set multiple values to the cache. * `GetManyAsync` and `GetMany` methods can be used to retrieve multiple values from the cache. -> These are not standard methods of the ASP.NET Core caching. So, a provide may not support them. They are supported by the [ABP Redis Cache integration package](Redis-Cache.md). +> These are not standard methods of the ASP.NET Core caching. So, some providers may not support them. They are supported by the [ABP Redis Cache integration package](Redis-Cache.md). If the provider doesn't support, it fallbacks to `SetAsync` and `GetAsync` methods (called once for each item). ## Advanced Topics