Exercise - Cache data in Redis

Completed

In this exercise, you'll add caching to the partially completed cloud-native app for your outdoor equipment retailer. You'll add Redis to the AppHost project, and then implement output caching in the WebApp project and distributed caching in the Catalog.API project.

Install prerequisites

The prerequisites for .NET Aspire are:

  • .NET 8
  • Visual Studio 2022 Preview
  • Docker Desktop
  • .NET Aspire workload in Visual Studio

If you've already got these packages installed, you can skip ahead to begin working with a Redis cache.

Install .NET 8

Follow this .NET 8 link, and select the correct installer for your operating system. For example, if you're using Windows 11, and a modern processor, select the x64 .NET 8 SDK for Windows.

After the download is complete, run the installer and follow the instructions. In a terminal window, run the following command to verify that the installation was successful:

dotnet --version

You should see the version number of the .NET SDK you installed. For example:

8.0.300-preview.24203.14

Install Visual Studio 2022 Preview

Follow this Visual Studio 2022 Preview link, and select Download Preview. After the download is complete, run the installer and follow the instructions.

Install Docker Desktop

Follow this Docker Desktop link, and select the correct installer for your operating system. After the download is complete, run the installer and follow the instructions. For the best performance and compatibility, use the WSL 2 backend.

Open the Docker Desktop application and accept the service agreement.

Install the .NET Aspire workload in Visual Studio

Install the .NET Aspire workload using the .NET CLI:

  1. Open a terminal.

  2. Update .NET workloads with this command:

    dotnet workload update
    

    You should see a message that the workloads are updated successfully.

    No workloads installed for this feature band. To update workloads installed with earlier SDK versions, include the --from-previous-sdk option.
    Updated advertising manifest microsoft.net.sdk.ios.
    Updated advertising manifest microsoft.net.workload.mono.toolchain.net6.
    Updated advertising manifest microsoft.net.sdk.android.
    Updated advertising manifest microsoft.net.workload.emscripten.net7.
    Updated advertising manifest microsoft.net.workload.emscripten.net6.
    Updated advertising manifest microsoft.net.sdk.macos.
    Updated advertising manifest microsoft.net.workload.emscripten.current.
    Updated advertising manifest microsoft.net.workload.mono.toolchain.current.
    Updated advertising manifest microsoft.net.sdk.maui.
    Updated advertising manifest microsoft.net.workload.mono.toolchain.net7.
    Updated advertising manifest microsoft.net.sdk.maccatalyst.
    Updated advertising manifest microsoft.net.sdk.tvos.
    Updated advertising manifest microsoft.net.sdk.aspire.
    No workloads installed for this feature band. To update workloads installed with earlier SDK versions, include the --from-previous-sdk option.
    
    Successfully updated workload(s): .
    
  3. Install the .NET Aspire workload with this command:

    dotnet workload install aspire
    

    You should see a message that the Aspire workload has been installed.

    Installing Aspire.Hosting.Sdk.Msi.x64 ...... Done
    Installing Aspire.ProjectTemplates.Msi.x64 ..... Done
    Installing Aspire.Hosting.Orchestration.win-x64.Msi.x64 ............. Done
    Installing Aspire.Hosting.Msi.x64 ..... Done
    Installing Aspire.Dashboard.Sdk.win-x64.Msi.x64 ....... Done
    
    Successfully installed workload(s) aspire.
    
  4. Verify that the .NET Aspire workload is installed with this command:

    dotnet workload list
    

    You should see the details of the aspire workload.

    Installed Workload Id      Manifest Version      Installation Source
    ---------------------------------------------------------------------------------------------
    aspire                     8.0.0/8.0.100         SDK 8.0.300-preview.24203, VS 17.10.34902.84
    
    Use `dotnet workload search` to find additional workloads to install.
    

Clone and modify the sample app

Let's use git to obtain a sample app built with .NET Aspire. The app doesn't yet have caching set up:

  1. In the command line, browse to a folder of your choice where you can work with code.

  2. Execute the following command to clone the Northern Mountains eShop sample application:

    git clone -b aspire-cache https://github.com/MicrosoftDocs/mslearn-aspire-starter
    
  3. Start Visual Studio and then select Open a project or solution.

  4. Browse to the folder where you cloned the eShop, open the start folder and select the eShop.rediscache.sln file, and then select Open.

  5. In Solution Explorer, browse to WebApp/Components/Pages and then double-click Catalog.razor.

  6. Locate the following line of code:

    <SectionContent SectionName="page-header-subtitle">Start the season with the latest in clothing and equipment.</SectionContent>
    
  7. Replace that line with the following code:

    <SectionContent SectionName="page-header-subtitle">Start the season with the latest in clothing and equipment. It's @DateTime.Now</SectionContent>
    
  8. To start the app, press F5 or select Debug > Start Debugging.

  9. If the Start Docker Desktop dialog appears, select Yes.

  10. When the eShop .NET Aspire dashboard appears, for the webapp resource, select one of the endpoints:

    Screenshot showing where to start the webapp in the .NET Aspire dashboard.

  11. The endpoint displays the Northern Mountains homepage. Including the time on the server:

    Screenshot showing the Northern Mountains homepage with the server-side time displayed.

  12. Press F5 to refresh the page. Because the page isn't cached, the time displayed changes every time you refresh it, as long as the second has changed.

  13. Change to the browser tab that displays the .NET Aspire dashboard, and then in the left-hand navigation, select Traces.

  14. Traces with the name webapp: GET / are requests for the homepage. Make a note of the typical Duration for these requests, then for one of them, select View in the Details column:

    Screenshot with the .NET Aspire dashboard showing traces for requests to the Northern Mountain homepage with no caching.

  15. In the timeline view, note that webapp calls multiple microservices to construct the response.

  16. Close the Northern Mountains homepage and the .NET Aspire dashboard.

  17. In Visual Studio, to stop debugging, press SHIFT - F5 or select Debug > Stop Debugging.

Add a caching backing service

Now that you've seen how the homepage performs without caching, let's add output caching to see if it improves responsiveness. Start by adding the output caching integration to the AppHost project:

  1. In Visual Studio, in Solution Explorer, right-click the eShop.AppHost project, select Add, and then select .NET Aspire package.

  2. In the search textbox, at the end of the existing text, type Redis.

  3. Select Aspire.Hosting.Redis package.

  4. In the Version list, select the latest 8.0.0 version, and then select Install.

  5. If the Preview Changes dialog appears, select Apply.

  6. In the License Acceptance dialog, select I Accept.

  7. In Solution Explorer, expand the AppHost project and then double-click Program.cs.

  8. Locate the following lines of code:

    // Databases
    
    var postgres = builder.AddPostgres("postgres").WithPgAdmin();
    var catalogDb = postgres.AddDatabase("CatalogDB");
    
  9. Immediately after those lines, add the following code:

    // Cache
    var redis = builder.AddRedis("cache");
    
  10. Locate the following line of code, which adds the Catalog API project to .NET Aspire orchestration:

    var catalogApi = builder.AddProject<Catalog_API>("catalog-api")
        .WithReference(catalogDb);
    
  11. To pass the Redis cache to the Catalog API project, replace that code with the following lines:

    var catalogApi = builder.AddProject<Catalog_API>("catalog-api")
        .WithReference(catalogDb)
        .WithReference(redis);
    

    Note

    We'll use the cache in the Catalog API to perform distributed caching.

  12. Locate the following line of code, which adds the WebApp project to .NET Aspire orchestration:

    builder.AddProject<WebApp>("webapp")
        .WithReference(catalogApi);
    
  13. To pass the Redis cache to the WebApp project, replace that code with the following lines:

    builder.AddProject<WebApp>("webapp")
        .WithReference(catalogApi)
        .WithReference(redis);
    

    Note

    We'll use the cache in the WebApp to perform output caching.

  14. To save the Program.cs file, press CTRL - S or select File > Save Program.cs.

Use output caching in the WebApp project

Now, let's use the Redis cache in the WebApp project to cache the homepage output:

  1. In Visual Studio, in Solution Explorer, right-click the WebApp project, select Add, and then select .NET Aspire package.

  2. In the search textbox, at the end of the existing text, type Redis.

  3. Select Aspire.StackExchange.Redis.OutputCaching package.

  4. In the Version list, select the latest 8.0.0 version, and then select Install.

  5. If the Preview Changes dialog appears, select Apply.

  6. In the License Acceptance dialog, select I Accept.

  7. When the installation is complete, in Solution Explorer, expand WebApp and then double-click Program.cs.

  8. Locate the following line of code:

    var builder = WebApplication.CreateBuilder(args);
    
  9. Immediately after that line, to add the output cache to the project, add this code:

    builder.AddRedisOutputCache("cache");
    
  10. Locate the following line of code:

    var app = builder.Build();
    
  11. Immediately after that line, to add the caching middleware to the request pipeline, add this code:

    app.UseOutputCache();
    
  12. In Solution Explorer, expand WebApp > Components > Pages and then double-click Catalog.razor.

  13. Locate the following line of code:

    @attribute [StreamRendering]
    
  14. Immediately after that line, to cache the homepage, add this code:

    @attribute [Microsoft.AspNetCore.OutputCaching.OutputCache(Duration = 10)]
    

Test output caching

Output caching is now implemented in the Northern Mountains homepage. Let's test it:

  1. In Visual Studio, to start the app, press F5 or select Debug > Start Debugging.

  2. When the eShop .NET Aspire dashboard appears, for the webapp resource, select one of the endpoints:

    Screenshot showing where to start the webapp in the .NET Aspire dashboard.

  3. The endpoint displays the Northern Mountains homepage, including the time on the server.

  4. Press F5 to refresh the page. Because the page is cached for 10 seconds, the time displayed changes only when it's more than 10 seconds after the cached request.

  5. Change to the browser tab that displays the .NET Aspire dashboard, and then in the left-hand navigation, select Traces.

  6. Traces with the name webapp: GET / are requests for the homepage. Some requests to the homepage, which couldn't be satisfied from the cache, have similar timings to the durations you noted previously. However, other requests, which are returned from the cache, have significantly shorter durations.

  7. For one of the shorter request, select View in the Details column. Notice that the request was retrieved from the Redis cache:

    Screenshot with the .NET Aspire dashboard showing a trace for a cached request.

  8. Close the Northern Mountains homepage and the .NET Aspire dashboard.

  9. In Visual Studio, to stop debugging, press SHIFT - F5 or select Debug > Stop Debugging.

Use distributed caching

We can also use Redis to perform distributed caching in the Catalog.API project:

  1. In Visual Studio, in Solution Explorer, right-click the Catalog.API project, select Add, and then select .NET Aspire package.

  2. In the search textbox, at the end of the existing text, type Redis.

  3. Select Aspire.StackExchange.Redis.DistributedCaching package.

  4. In the Version list, select the latest 8.0.0 version, and then select Install.

  5. If the Preview Changes dialog appears, select Apply.

  6. In the License Acceptance dialog, select I Accept.

  7. When the installation is complete, in Solution Explorer, expand Catalog.API and then double-click Program.cs.

  8. Locate the following line of code:

    var builder = WebApplication.CreateBuilder(args);
    
  9. Immediately after that line, to add the output cache to the project, add this code:

    builder.AddRedisDistributedCache("cache");
    
  10. In Solution Explorer expand Catalog.API > Apis and then double-click CatalogApi.cs.

  11. Locate the following code, which declares the GetAllItems method:

    public static async Task<Results<Ok<PaginatedItems<CatalogItem>>, BadRequest<string>>> GetAllItems(
        [AsParameters] PaginationRequest paginationRequest,
        [AsParameters] CatalogServices services)
    {
    
  12. To obtain the Redis cache through dependency injection, modify that code to add a new parameter to the method:

    public static async Task<Results<Ok<PaginatedItems<CatalogItem>>, BadRequest<string>>> GetAllItems(
        [AsParameters] PaginationRequest paginationRequest,
        [AsParameters] CatalogServices services,
        IDistributedCache cache)
    {
    
  13. Remove the entire contents of the GetAllItems method and replace it with the following code:

    var pageSize = paginationRequest.PageSize;
    var pageIndex = paginationRequest.PageIndex;
    
    var totalItems = await services.DbContext.CatalogItems
        .LongCountAsync();
    
    // Check that there are cached items
    var cachedItems = await cache.GetAsync("catalogItems");
    
    if (cachedItems is null)
    {
        // There are no items in the cache. Get them from the database
        var itemsOnPage = await services.DbContext.CatalogItems
            .OrderBy(c => c.Name)
            .Skip(pageSize * pageIndex)
            .Take(pageSize)
            .AsNoTracking()
            .ToListAsync();
    
        // Store the items in the cache for 10 seconds
        await cache.SetAsync("catalogItems", Encoding.UTF8.GetBytes(System.Text.Json.JsonSerializer.Serialize(itemsOnPage)), new()
        {
            AbsoluteExpiration = DateTime.Now.AddSeconds(10)
        });
    
        ChangeUriPlaceholder(services.Options.Value, itemsOnPage);
        return TypedResults.Ok(new PaginatedItems<CatalogItem>(pageIndex, pageSize, totalItems, itemsOnPage));
    
    }
    else
    {
        // There are items in the cache. Deserialize them to display.
        var itemsOnPage = System.Text.Json.JsonSerializer.Deserialize<List<CatalogItem>>(cachedItems);
        // Make sure itemsOnPage is not null
        if (itemsOnPage is null)
        {
            itemsOnPage = new List<CatalogItem>();
        }
    
        ChangeUriPlaceholder(services.Options.Value, itemsOnPage);
        return TypedResults.Ok(new PaginatedItems<CatalogItem>(pageIndex, pageSize, totalItems, itemsOnPage));
    }
    

Test distributed caching

Distributed caching is now implemented in the Catalog.API project. Let's test it:

  1. In Visual Studio, to start the app, press F5 or select Debug > Start Debugging.

  2. When the eShop .NET Aspire dashboard appears, for the catalog-api resource, select the endpoint:

    Screenshot showing where to start the Catalog API in the .NET Aspire dashboard.

  3. The endpoint displays the Swagger interface for the Catalog API microservice. Next to the /api/v1/catalog/items method, select GET.

  4. Select Try it out and then select Execute. Results are displayed in the Response body window:

    Screenshot showing catalog results displayed in the Swagger user interface.

  5. Click Execute several more times to call the API again. These requests should get the items from the cache, as long as the request is less than 10 seconds after the first one.

  6. Change to the browser tab that displays the .NET Aspire dashboard, and then in the left-hand navigation, select Traces.

  7. Traces with the name catalog-api: GET /api/v1/catalog/items are requests for the items method of the Catalog API. Notice that the first request to that method takes longer to formulate that subsequent requests, which the API obtains the items from the Redis cache:

    Screenshot with the .NET Aspire dashboard Traces page with cached requests to the Catalog API.

  8. Close the Swagger page and the .NET Aspire dashboard.

  9. In Visual Studio, to stop debugging, press SHIFT - F5 or select Debug > Stop Debugging.