{"id":25164229,"url":"https://github.com/craftedmedia/xperiencecommunity.fusioncache","last_synced_at":"2025-06-21T08:08:08.870Z","repository":{"id":274004434,"uuid":"914269049","full_name":"craftedmedia/XperienceCommunity.FusionCache","owner":"craftedmedia","description":"XperienceCommunity.FusionCache integrates with FusionCache to provide a Hybrid Caching solution within Xperience by Kentico","archived":false,"fork":false,"pushed_at":"2025-04-15T12:19:31.000Z","size":163,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-05-24T07:53:04.046Z","etag":null,"topics":["caching","csharp","hybrid-cache","kentico","kentico-xperience","multi-level-cache","performance"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/craftedmedia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-01-09T09:16:57.000Z","updated_at":"2025-04-11T13:54:09.000Z","dependencies_parsed_at":null,"dependency_job_id":"c1457487-bd19-4fb6-b97c-f124ca853536","html_url":"https://github.com/craftedmedia/XperienceCommunity.FusionCache","commit_stats":null,"previous_names":["craftedmedia/xperiencecommunity.fusioncache"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/craftedmedia/XperienceCommunity.FusionCache","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/craftedmedia%2FXperienceCommunity.FusionCache","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/craftedmedia%2FXperienceCommunity.FusionCache/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/craftedmedia%2FXperienceCommunity.FusionCache/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/craftedmedia%2FXperienceCommunity.FusionCache/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/craftedmedia","download_url":"https://codeload.github.com/craftedmedia/XperienceCommunity.FusionCache/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/craftedmedia%2FXperienceCommunity.FusionCache/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261088356,"owners_count":23107683,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["caching","csharp","hybrid-cache","kentico","kentico-xperience","multi-level-cache","performance"],"created_at":"2025-02-09T04:20:01.589Z","updated_at":"2025-06-21T08:08:03.852Z","avatar_url":"https://github.com/craftedmedia.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# XperienceCommunity.FusionCache\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\n## Description\nThis package integrates with the popular Hybrid Caching library known as [ZiggyCreatures.FusionCache](https://github.com/ZiggyCreatures/FusionCache) providing a true L1 + L2 layered caching solution within Xperience by Kentico.\n\nIt provides some useful utilities such as cache invalidation via Kentico cache dependencies, custom `FusionCache` backed cache tag helper and support for output caching, with content personalization handled out of the box.\n\nIf you're unfamiliar with Hybrid Caching, I would recommend reading the gentle intro over at: https://github.com/ZiggyCreatures/FusionCache/blob/main/docs/AGentleIntroduction.md\n\n### Library Version Matrix\n\n| Xperience Version | Library Version |\n| ----------------- | --------------- |\n| \u003e= 30.0.0         | 1.0.0           |\n\n### Dependencies\n\n- [ASP.NET Core 8.0](https://dotnet.microsoft.com/en-us/download)\n- [Xperience by Kentico](https://docs.kentico.com)\n\n### Other requirements\n\nA Redis instance to use as your L2 cache.\n\n### Package Installation\n\nInstall the `XperienceCommunity.FusionCache` package via nuget or run:\n\n```\nInstall-Package XperienceCommunity.FusionCache\n```\nFrom package manager console.\n\n## Quick Start\n\n### Configuration\n\nInclude the following section within your `appsettings.json` file:\n\n```\n \"XperienceFusionCache\": {\n   \"RedisConnectionString\": \"REDIS CONNECTION STRING GOES HERE\"\n }\n```\n\n### Register services\n\nAdd the following code to your `Program.cs` file:\n\n```\nvar builder = WebApplication.CreateBuilder(args);\n\n// ...\n\nbuilder.Services.AddXperienceFusionCache(builder.Configuration);\n```\n\nAnd include `UseXperienceFusionCache()` before `app.Run()`:\n\n```\napp.UseXperienceFusionCache();\n```\n\n### Update _ViewImports.cshtml\n\nInclude the following in your `_ViewImports.cshtml` file:\n\n```\n@addTagHelper *, XperienceCommunity.FusionCache\n```\n\n### Output caching\n\nUse either:\n\n- Tag helper\n    - `\u003cxperience-fusion-cache /\u003e`\n- Output cache policy\n    - `[OutputCache(PolicyName = \"XperienceFusionCache\", Tags = [\"webpageitem|all\"])]`\n\n### Services\n\nInject `IFusionCache` and use the Get/Set methods, providing `tags` as Kentico cache dependency keys:\n\n```\nvar products = await this.fusionCache.GetOrSetAsync\u003cIEnumerable\u003cProductDTO\u003e?\u003e(\n            key: \"FooWebsite.Products\",\n            factory: async (ctx, _) =\u003e\n            {\n                var products = await this.GetproductsAsync();\n\n                if (products is null)\n                {\n                    ctx.Options.Duration = TimeSpan.Zero;\n                    return null;\n                }\n\n                return products;\n            },\n            tags: [CacheHelper.BuildCacheItemName(new[] { ProductItem.CONTENT_TYPE_NAME, \"all\" })]);\n```\n\n\nAnd that should be enough to get going! Read on for more info.\n\n## Full Instructions\n\n### Default Cache options\n\nYou can choose to configure some default `FusionCacheEntryOptions` via `appsettings.json` config. These will be used as the default for all cache entries, although they can be overridden on a per-call basis when using `IFusionCache`. See: [https://github.com/ZiggyCreatures/FusionCache/blob/main/docs/Options.md](https://github.com/ZiggyCreatures/FusionCache/blob/main/docs/Options.md#defaultentryoptions)\n\nExample:\n\n```\n\"XperienceFusionCache\": {\n  \"DefaultFusionCacheEntryOptions\": {\n    \"Duration\": \"00:05:00\", // 5 mins,\n    \"DistributedCacheDuration\": \"00:10:00\", // 10 mins,\n    \"IsFailSafeEnabled\": true,\n    \"FailSafeMaxDuration\": \"02:00:00\" // 2 hours\n    // Etc...\n  }\n}\n```\n\nAny option available on the [FusionCacheEntryOptions](https://github.com/ZiggyCreatures/FusionCache/blob/f3896a5f5b6e21f918009d687520938d322f79f4/src/ZiggyCreatures.FusionCache/FusionCacheEntryOptions.cs) is also available to be set here.\n\n### Configuring Serialization\n[NewtonsoftJson](https://www.nuget.org/packages/ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson/) is configured as the default serializer for maximum compatibility and ease of use, however it's possible to configure any of the following serializers:\n\n- [SystemTextJson](https://www.nuget.org/packages/ZiggyCreatures.FusionCache.Serialization.SystemTextJson)\n- [CysharpMemoryPack](https://www.nuget.org/packages/ZiggyCreatures.FusionCache.Serialization.CysharpMemoryPack)\n- [NeueccMessagePack](https://www.nuget.org/packages/ZiggyCreatures.FusionCache.Serialization.NeueccMessagePack)\n- [ServiceStackJson](https://www.nuget.org/packages/ZiggyCreatures.FusionCache.Serialization.ServiceStackJson)\n- [ProtoBufNet](https://www.nuget.org/packages/ZiggyCreatures.FusionCache.Serialization.ProtoBufNet)\n\nSee https://github.com/ZiggyCreatures/FusionCache/pull/349 for performance benchmarks for each of these serializers.\n\nTo configure a different serializer, simply specify the `DefaultSerializer` in options:\n```\n\"XperienceFusionCache\": {\n  \"RedisConnectionString\": \"...\",\n  \"DefaultSerializer\": \"NeueccMessagePack\" // OR 'ServiceStackJson' etc...\n},\n```\n`FusionCache` will now use the configured serializer instead of the default. Each serializer has its pros, cons and individual quirks you should familiarize yourself with before using.\n\n\n### Fusion Cache Tag Helper\n\nThe package provides a custom cache tag helper backed by `FusionCache`.\n\nTo use it, include the tag helper in your view:\n\n```\n\u003cxperience-fusion-cache\n    name=\"home-page-cache\"\n    cache-dependencies=\"@(new string[] { \"webpageitem|byid|1\", \"contentitem|bycontenttype|Medio.Clinic\" })\"\n    duration=\"@TimeSpan.FromMinutes(5)\"\n    vary-by-option-types=\"@(new[] { typeof(ContactGroupVaryByOption) })\"\u003e\n\n@* Cached HTML goes here *@\n\n\u003c/xperience-fusion-cache\u003e\n```\n\nSee below, for a full list of options:\n\n| Option               | Description                                                                                                                                                                 | Example                                                           | Default    |\n| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ---------- |\n| name                 | Required. A unique name for the tag instance.                                                                                                                             | `\"product-listing\"`                                                 | `null`     |\n| enabled              | A value indicating whether caching is enabled for the tag.                                                                                                                  | `true`                                                            | `true`     |\n| cache-dependencies   | Collection of cache dependencies for the cache entry. The associated cache item will be cleared when one of the dependencies is touched by the system.                     | `new string[] { \"webpageitem\\|byid\\|3\" }`                         | `null`     |\n| cacheability-rules   | Collection of custom rules that determine whether the tag inner content can be cached based on whether the `IReadOnlyMemory\u003cchar\u003e` pattern was found within the tags HTML. | `Func\u003cReadOnlyMemory\u003cchar\u003e, bool\u003e CacheDisabled = (content) =\u003e content.Span.IndexOf(\"cache-disabled=\\\"True\\\"\") \u003c= -1` | `null`     |\n| duration             | Cache duration.                                                                                                                                                              | `TimeSpan.FromMinutes(5)`                                         | 5 minutes  |\n| vary-by              | Custom vary by string.                                                                                                                                                     | `$\"product-{product.Id}\"`                                         | `null`     |\n| vary-by-header       | Vary the cache by the provided header(s).                                                                                                                                  | `\"header1,header2\"`                                               | `null`     |\n| vary-by-query        | Vary the cache by the provided query parameter(s).                                                                                                                         | `\"page,filter\"`                                                   | `null`     |\n| vary-by-route        | Vary the cache by the provided route parameter(s).                                                                                                                         | `\"lang,id\"`                                                       | `null`     |\n| vary-by-cookie       | Vary the cache by the provided cookie name(s).                                                                                                                             | `\"cookie1,cookie2\"`                                               | `null`     |\n| vary-by-user         | Vary the cache by the current user.                                                                                                                                        | `true`                                                            | `false`    |\n| vary-by-culture      | Vary the cache by the current request culture.                                                                                                                             | `true`                                                            | `false`    |\n| vary-by-option-types | `ICacheVaryByOption` implementations to vary the cache by. Useful for content personalization.                                                                             | `new[] { typeof(ContactGroupVaryByOption) }`                      | `null`     |\n\n\n\n\n### Output cache\n\nThis package integrates with the NET Core Output Caching middleware via a custom `IOutputCacheStore` and `IOutputCachePolicy` which has been integrated with `FusionCache`.\n\nJust reference `XperienceFusionCache` as the policy name when using the `[OutputCache]` attribute and optionally specify cache dependencies via the `Tags` attribute.\n\nExample usage:\n\n```\npublic class HomePageController : Controller\n{\n    [OutputCache(PolicyName = \"XperienceFusionCache\", Tags = [\"webpageitem|all\"])]\n    public async Task\u003cIActionResult\u003e Index()\n    {\n        // Perform some expensive logic...\n\n        // Associate cache dependencies with the current request\n        this.HttpContext.AddCacheDependencies(\n            new HashSet\u003cstring\u003e() {\n                CacheHelper.BuildCacheItemName(new[] { \"webpageitem\", \"bychannel\", \"MyWebsite\", \"bycontenttype\", \"website.homepage\" }),\n            });\n\n        return new TemplateResult();\n    }\n}\n\n```\n\nYou can also specify cache dependencies via the `AddCacheDependencies` extension method (see example above) if they aren't known at runtime.\n\nPolicy defaults can be customized via `appsettings.json`:\n\n```\n\"XperienceFusionCache\": {\n  // ...\n  \"OutputCachePolicyName\": \"MyOutputCachePolicy\",\n  \"OutputCacheExpiration\": \"00:05:00\"\n}\n```\n### Content Personalization\nThe library provides several ways to inject unique vary-by keys into each cache items key entry, granting compatibility with the widget personalization feature within Xperience:\nhttps://docs.kentico.com/business-users/digital-marketing/widget-personalization\n\nTo utilize this feature, simply implement your custom `ICacheVaryByOption` types, ensuring a unique key is returned based on your own use case:\nhttps://docs.kentico.com/developers-and-admins/development/caching/output-caching#implement-custom-personalization-options\n\nComplete example:\n```\npublic class ContactGroupVaryByOption : ICacheVaryByOption\n{\n    public string GetKey()\n    {\n        var contact = ContactManagementContext.GetCurrentContact();\n\n        if (contact?.ContactGroups is null || !contact.ContactGroups.Any())\n        {\n            return string.Empty;\n        }\n\n        var contactGroups = contact.ContactGroups\n            .OrderBy(x =\u003e x.ContactGroupName)\n            .Select(y =\u003e y.ContactGroupName);\n\n        return string.Join(\"||\", [\"VaryByContactGroup\", .. contactGroups]);\n    }\n}\n```\n\nThen pass these types to the `vary-by-option-types` attribute, if using the `\u003cxperience-fusion-cache /\u003e` tag helper, e.g:\n\n```\n\u003cxperience-fusion-cache\n    name=\"my-widget-cache\"\n    duration=\"@TimeSpan.FromMinutes(5)\"\n    vary-by-option-types=\"@(new[] { typeof(ContactGroupVaryByOption) })\"\u003e\n\n@* Cached HTML which should vary by contact group goes here *@\n\n\u003c/xperience-fusion-cache\u003e\n```\n\nOr alternatively, if using controller level `[OutputCache]`, decorate the action result with `[XperienceFusionCacheVaryByOptionTypes]` and specify your custom `ICacheVaryByOption` types in the constructor, e.g:\n```\n[OutputCache(PolicyName = \"XperienceFusionCache\", Tags = [\"webpageitem|all\"])]\n[XperienceFusionCacheVaryByOptionTypes(VaryByOptionTypes = [typeof(ContactGroupVaryByOption)])]\npublic async Task\u003cIActionResult\u003e Index()\n{\n    // Some expensive logic...\n\n    return new TemplateResult();\n}\n```\n\nThis ensures that your custom vary by option implementations are considered when constructing a unique cache key for the cache item.\n\n\n### Extending cache invalidation for custom object types\n\nCache invalidation of standard Kentico objects (pages, content items, media etc...) is handled out of the box but if you want invalidation for general object types (those that inherit from `BaseInfo`) then you should implement the `IGeneralObjectCacheItemsProvider` type and place it somewhere within your application root.\n\nExample:\n\n```\npublic class GeneralObjectsCacheItemsProvider : IGeneralObjectCacheItemsProvider\n{\n    public IEnumerable\u003cObjectTypeInfo\u003e GeneralObjectInfos =\u003e new List\u003cObjectTypeInfo\u003e()\n    {\n        SomeCustomTypeInfo.TYPEINFO,\n        UserInfo.TYPEINFO,\n        //...\n    };\n}\n```\n\nThis will ensure cache invalidation based on the 'General objects' dummy cache keys for the listed types: https://docs.kentico.com/developers-and-admins/development/caching/cache-dependencies#general-objects\n\n## Contributing\n\nTo see the guidelines for Contributing to Kentico open source software, please see [Kentico's `CONTRIBUTING.md`](https://github.com/Kentico/.github/blob/main/CONTRIBUTING.md) for more information and follow the [Kentico's `CODE_OF_CONDUCT`](https://github.com/Kentico/.github/blob/main/CODE_OF_CONDUCT.md).\n\n## License\n\nDistributed under the MIT License. See [`LICENSE.md`](./LICENSE.md) for more information.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcraftedmedia%2Fxperiencecommunity.fusioncache","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcraftedmedia%2Fxperiencecommunity.fusioncache","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcraftedmedia%2Fxperiencecommunity.fusioncache/lists"}