{"id":46412,"date":"2023-07-06T10:00:00","date_gmt":"2023-07-06T17:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=46412"},"modified":"2024-12-13T14:12:22","modified_gmt":"2024-12-13T22:12:22","slug":"caching-abstraction-improvements-in-aspnetcore","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/caching-abstraction-improvements-in-aspnetcore\/","title":{"rendered":"Improvements to the Caching Abstraction in ASP.NET Core"},"content":{"rendered":"<p>We are improving the caching abstraction in .NET to make it more intuitive and reliable. This blog showcases <a href=\"https:\/\/github.com\/mgravell\/DistributedCacheDemo\">a sample project<\/a> with reusable extension methods for <a href=\"https:\/\/learn.microsoft.com\/aspnet\/core\/performance\/caching\/distributed?view=aspnetcore-7.0\">Distributed Caching<\/a> to greatly simplify the repetitive code on object serialization when setting cached values. It also provides guidance on best practices so developers can focus on business logic. We are actively working on bringing this experience into the .NET and our current ETA is post .NET 8, but we wanted to share early progress and get feedback.<\/p>\n<h2>Background<\/h2>\n<p>Application developers mostly use distributed caches to improve data query performance and to save web application session objects. While the concept of caching sounds straight forward, we heard from .NET developers that it requires a decent amount of experience to use caching properly. The challenges are:<\/p>\n<ul>\n<li>The required object serialization code is repetitive and error prone.<\/li>\n<li>It&#8217;s still a common error to use sync over async in caching methods with clients such as <a href=\"https:\/\/stackexchange.github.io\/StackExchange.Redis\/\">StackExchange.Redis<\/a><\/li>\n<li>There is too much overhead from &#8220;boilerplate&#8221; type of work in .NET caching methods.<\/li>\n<\/ul>\n<p>Ideally, the developer should only have to specify what to cache, and the framework should take care of the rest. We are introducing a better caching abstraction in .NET to optimize for common distributed caching and session store caching scenarios. We would like to show you a concept of our solution, hoping to solve some immediate problems and receive feedback. The sample extension methods are written by <a href=\"https:\/\/github.com\/mgravell\">Marc Gravell<\/a>, the maintainer of the popular <a href=\"https:\/\/stackexchange.github.io\/StackExchange.Redis\/\">StackExchange.Redis<\/a>. You can copy the <a href=\"https:\/\/github.com\/mgravell\/DistributedCacheDemo\/blob\/main\/DistributedCacheExtensions.cs\">DistributedCacheExtensions.cs<\/a> into your ASP.NET core web project, configure a distributed cache in <a href=\"https:\/\/github.com\/mgravell\/DistributedCacheDemo\/blob\/9c98c205668aa83347661bed6a8acdcd8886ad35\/Program.cs#L4\">Program.cs<\/a>, and follow <a href=\"https:\/\/github.com\/mgravell\/DistributedCacheDemo\/blob\/9c98c205668aa83347661bed6a8acdcd8886ad35\/Program.cs#L13\">example code<\/a> to start using the extension methods.<\/p>\n<p>The <a href=\"https:\/\/github.com\/mgravell\/DistributedCacheDemo\/blob\/main\/DistributedCacheExtensions.cs\">extension methods sample code<\/a> is very intuitive if you want to jump directly into it. The rest of the blog dives into the detailed implementation behind the extension methods with an example Web API application for a weather forecast.<\/p>\n<h2>Application scenarios for using the new Distributed Cache extension methods<\/h2>\n<p>In the <a href=\"https:\/\/github.com\/mgravell\/DistributedCacheDemo\/blob\/main\/DistributedCacheExtensions.cs\">extension methods sample code<\/a>, only <code>GetAsync()<\/code> methods are exposed. You may wonder where the methods for setting cache values are. The answer is \u2013 you don&#8217;t need the <code>Set<\/code> methods anymore. <code>Set<\/code> methods are abstracted by the <code>GetAsync()<\/code> implementations when reading value from the data source upon a cache miss. The <code>GetAsync()<\/code> methods essentially do the following:<\/p>\n<ul>\n<li><strong>Automate the<\/strong> <a href=\"https:\/\/learn.microsoft.com\/azure\/architecture\/patterns\/cache-aside\"><strong>Cache-Aside pattern<\/strong><\/a>. This means it always attempts to read from cache. In the case of a cache miss, the method executes a user-specified function to return the value and save it in cache for future reads.<\/li>\n<li><strong>Object serialization<\/strong>. The extension methods allow developers to specify what to cache. No custom serialization code needed. The <a href=\"https:\/\/github.com\/mgravell\/DistributedCacheDemo\/blob\/9c98c205668aa83347661bed6a8acdcd8886ad35\/DistributedCacheExtensions.cs#L125\">sample code<\/a> uses Json serializer, but you can edit the code to use <a href=\"https:\/\/github.com\/protobuf-net\/protobuf-net\">protobuf-net<\/a> or other custom serializers for performance optimization.<\/li>\n<li><strong>Thread management<\/strong>. All caching methods are designed to be asynchronous and work reliably with synchronous operations that generate the cache values.<\/li>\n<li><strong>State management<\/strong>. The user-specified functions can optionally be stateful with simplified coding syntax, using static lambdas to avoid captured variables and per-usage delegate creation overheads.<\/li>\n<\/ul>\n<h2>Example for using the sample code<\/h2>\n<p>The <a href=\"https:\/\/github.com\/CawaMS\/WeatherAPI-DistributedCache\/tree\/main\">WeatherAPI-DistributedCache<\/a> demo shows how to easily re-use the extension methods, with <a href=\"https:\/\/learn.microsoft.com\/azure\/azure-cache-for-redis\/cache-overview\">Azure Cache for Redis<\/a> as example.<\/p>\n<ol>\n<li>Copy the <a href=\"https:\/\/github.com\/mgravell\/DistributedCacheDemo\/blob\/main\/DistributedCacheExtensions.cs\">DistributedCacheDemo\/DistributedCacheExtensions.cs<\/a> to your ASP.NET Core project in the same folder directory as the <code>Program.cs<\/code> file. Per <strong>Figure 1,<\/strong> the <code>DistributedCacheExtensions.cs<\/code> code file is placed alongside <code>Program.cs<\/code> in the project folder.\n<p><strong>Figure 1: Copy and paste the DistributedCacheExtensions.cs file to your web project<\/strong><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2023\/07\/solution-explorer.png\" alt=\"Placing the sample DistributedCacheExtensions.cs code in the same folder as program.cs\" \/><\/li>\n<li>Add the <a href=\"https:\/\/github.com\/CawaMS\/WeatherAPI-DistributedCache\/blob\/319b4dfca458812ef66dff734ab2a32311033bae\/Program.cs#L4\">Distributed Cache service<\/a> in <code>Program.cs<\/code> by:\n<ol>\n<li>Adding <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.Extensions.Caching.StackExchangeRedis\">Microsoft.Extensions.Caching.StackExchangeRedis<\/a> to your project<\/li>\n<li>Adding the following code:\n<pre><code class=\"language-C#\">builder.Services.AddStackExchangeRedisCache(options =&gt;\r\n\r\n{\r\n\r\noptions.Configuration = builder.Configuration.GetConnectionString(\"MyRedisConStr\");\r\n\r\noptions.InstanceName = \"SampleInstance\";\r\n\r\n});<\/code><\/pre>\n<\/li>\n<\/ol>\n<\/li>\n<li>Use the extension methods in <a href=\"https:\/\/github.com\/CawaMS\/WeatherAPI-DistributedCache\/blob\/main\/Controllers\/WeatherForecastController.cs\">Weather Controller class<\/a> that contains business logic. Per <strong>Figure 2,<\/strong> include the using statement to access the extension methods.\n<p><strong>Figure 2: add using statement in the class to access the extension methods<\/strong><\/p>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2023\/07\/using-statement.png\" alt=\"Example of including the sample extension code through using statement\" \/><\/li>\n<li>Refer to the <a href=\"https:\/\/github.com\/mgravell\/DistributedCacheDemo\/blob\/main\/Program.cs\">sample code<\/a> to use the new <code>GetAsync()<\/code> methods. The only code needed is the business logic for generating weather forecast data. All caching operations are abstracted by the extension methods. <strong>Figure 3<\/strong> shows a user defined method for generating the weather forecast for the next week, which is the only business logic required from developers as the input to use caching methods.\n<p><strong>Figure 3: define the business logic to use the new GetAsync() extension methods<\/strong><\/p>\n<p><img decoding=\"async\" src=\".\/get-async-method.png\" alt=\"Example of using GetAsync() method by only adding the business logic for generating the cached value if not found\" \/><\/li>\n<\/ol>\n<p>Notice that no object serialization code is required to get or set an <a href=\"https:\/\/github.com\/CawaMS\/WeatherAPI-DistributedCache\/blob\/main\/WeatherForecast.cs\">WeatherForecast<\/a> object in the cache. And in fact, only caching options and cache key name are needed as parameters to use the <code>GetAsync()<\/code> method in a web application. Developers can entirely focus on the business logic without any &#8220;boilerplate&#8221; efforts to get caching operations to work.<\/p>\n<h2>Next steps<\/h2>\n<p>Try out the concept from our <a href=\"https:\/\/github.com\/mgravell\/DistributedCacheDemo\/blob\/main\/DistributedCacheExtensions.cs\">sample code<\/a> today! You can provide feedback by leaving comments in this blog post or filing an issue in the <a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/issues\/new\/choose\">ASP.NET Core repository<\/a>. If you are looking for guidance on using a distributed cache to improve you cloud application&#8217;s performance, we have an example at <a href=\"https:\/\/techcommunity.microsoft.com\/t5\/azure-developer-community-blog\/improving-web-application-performance-using-azure-cache-for\/ba-p\/3840436\">Improving Web Application Performance Using Azure Cache for Redis<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>We are updating caching in ASP.NET Core to be more intuitive and reliable<\/p>\n","protected":false},"author":121589,"featured_media":46424,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,197,7509,3009],"tags":[7749,116],"class_list":["post-46412","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-aspnet","category-aspnetcore","category-performance","tag-caching","tag-redis"],"acf":[],"blog_post_summary":"<p>We are updating caching in ASP.NET Core to be more intuitive and reliable<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/46412","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/121589"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=46412"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/46412\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/46424"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=46412"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=46412"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=46412"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}