教程:在 ASP.NET 中创建缓存端排行榜Tutorial: Create a cache-aside leaderboard on ASP.NET

在本教程中,我们将更新在针对 Azure Redis 缓存的 ASP.NET 快速入门中创建的 ContosoTeamStats ASP.NET Web 应用,以包括将缓存端模式与 Azure Redis 缓存配合使用的排行榜。In this tutorial you will update the ContosoTeamStats ASP.NET web app, created in the ASP.NET quickstart for Azure Cache for Redis, to include a leaderboard that uses the cache-aside pattern with Azure Cache for Redis. 该示例应用程序显示数据库中的团队统计信息列表,并演示如何通过不同的方法使用用于 Redis 的 Azure 缓存在缓存中存储和检索数据,以提高性能。The sample application displays a list of team statistics from a database and demonstrates different ways to use Azure Cache for Redis to store and retrieve data from the cache to improve performance. 完成本教程后,将有一个运行的 Web 应用,该应用可以对数据库执行读写操作,已通过用于 Redis 的 Azure 缓存进行优化,并且托管在 Azure 中。When you complete the tutorial you have a running web app that reads and writes to a database, optimized with Azure Cache for Redis, and hosted in Azure.

本教程介绍如何执行下列操作:In this tutorial, you learn how to:

  • 使用用于 Redis 的 Azure 缓存来存储和检索数据,以提高数据吞吐量并降低数据库负载。Improve data throughput and reduce database load by storing and retrieving data using Azure Cache for Redis.
  • 使用 Redis 排序的集来检索排名前五的团队。Use a Redis sorted set to retrieve the top five teams.
  • 使用资源管理器模板为应用程序预配 Azure 资源。Provision the Azure resources for the application using a Resource Manager template.
  • 使用 Visual Studio 将应用程序发布到 Azure。Publish the application to Azure using Visual Studio.

如果没有 Azure 订阅,可在开始前创建一个试用帐户If you don't have an Azure subscription, create a trial account before you begin.

先决条件Prerequisites

若要完成本教程,必须满意以下先决条件:To complete this tutorial, you must have the following prerequisites:

将排行榜添加到项目Add a leaderboard to the project

在本教程的本部分,我们将配置 ContosoTeamStats 项目,该项目中的排行榜报告虚构团队列表的赢局、输局和平局统计信息。In this section of the tutorial, you configure the ContosoTeamStats project with a leaderboard that reports the win, loss, and tie statistics for a list of fictional teams.

将实体框架添加到项目Add the Entity Framework to the project

  1. 在 Visual Studio 中,打开在用于 Redis 的 Azure 缓存的 ASP.NET 快速入门中创建的 ContosoTeamStats 解决方案。In Visual Studio, open the ContosoTeamStats Solution that you created in the ASP.NET quickstart for Azure Cache for Redis.

  2. 单击“工具”>“NuGet 包管理器”>“包管理器控制台”。 Click Tools > NuGet Package Manager > Package Manager Console.

  3. 在“包管理器控制台”窗口中,运行以下命令安装 EntityFramework: Run the following command from the Package Manager Console window to install EntityFramework:

    Install-Package EntityFramework
    

有关此包的详细信息,请参阅 EntityFramework NuGet 页。For more information about this package, see the EntityFramework NuGet page.

添加团队模型Add the Team model

  1. 右键单击“解决方案资源管理器”中的“模型”,并选择“添加”>“类”。 Right-click Models in Solution Explorer, and choose Add, Class.

  2. 输入 Team 作为类名,并单击“添加”。 Enter Team for the class name and click Add.

    添加模型类

  3. Team.cs 文件顶部的 using 语句替换为以下 using 语句:Replace the using statements at the top of the Team.cs file with the following using statements:

    using System;
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.Data.Entity.SqlServer;
    
  4. Team 类的定义替换为以下代码片段,其中包含更新的 Team 类定义以及某些其他实体框架帮助器类。Replace the definition of the Team class with the following code snippet that contains an updated Team class definition as well as some other Entity Framework helper classes. 本教程对实体框架使用代码优先方法。This tutorial is using the code first approach with Entity Framework. 此方法可让实体框架通过代码创建数据库。This approach allows Entity Framework to create the database from your code. 有关本教程中使用的实体框架 Code First 方法的详细信息,请参阅 对新数据库使用 Code FirstFor more information on the code first approach to Entity Framework that's used in this tutorial, see Code first to a new database.

    public class Team
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int Wins { get; set; }
        public int Losses { get; set; }
        public int Ties { get; set; }
    
        static public void PlayGames(IEnumerable<Team> teams)
        {
            // Simple random generation of statistics.
            Random r = new Random();
    
            foreach (var t in teams)
            {
                t.Wins = r.Next(33);
                t.Losses = r.Next(33);
                t.Ties = r.Next(0, 5);
            }
        }
    }
    
    public class TeamContext : DbContext
    {
        public TeamContext()
            : base("TeamContext")
        {
        }
    
        public DbSet<Team> Teams { get; set; }
    }
    
    public class TeamInitializer : CreateDatabaseIfNotExists<TeamContext>
    {
        protected override void Seed(TeamContext context)
        {
            var teams = new List<Team>
            {
                new Team{Name="Adventure Works Cycles"},
                new Team{Name="Alpine Ski House"},
                new Team{Name="Blue Yonder Airlines"},
                new Team{Name="Coho Vineyard"},
                new Team{Name="Contoso, Ltd."},
                new Team{Name="Fabrikam, Inc."},
                new Team{Name="Lucerne Publishing"},
                new Team{Name="Northwind Traders"},
                new Team{Name="Consolidated Messenger"},
                new Team{Name="Fourth Coffee"},
                new Team{Name="Graphic Design Institute"},
                new Team{Name="Nod Publishers"}
            };
    
            Team.PlayGames(teams);
    
            teams.ForEach(t => context.Teams.Add(t));
            context.SaveChanges();
        }
    }
    
    public class TeamConfiguration : DbConfiguration
    {
        public TeamConfiguration()
        {
            SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
        }
    }
    
  5. 在“解决方案资源管理器”中,双击“Web.config”将其打开。 In Solution Explorer, double-click Web.config to open it.

    Web.config

  6. 将下面的 connectionStrings 节添加到 configuration 节中:Add the following connectionStrings section inside the configuration section. 连接字符串的名称必须与实体框架数据库上下文类(即 TeamContext)的名称相匹配。The name of the connection string must match the name of the Entity Framework database context class, which is TeamContext.

    此连接字符串假设已满足先决条件,并已安装 SQL Server Express LocalDB(属于连同 Visual Studio 2019 一起安装的 .NET 桌面开发工作负荷的一部分)。This connection string assumes you have met the Prerequisites and installed SQL Server Express LocalDB, which is part of the .NET desktop development workload installed with Visual Studio 2019.

    <connectionStrings>
        <add name="TeamContext" connectionString="Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Teams.mdf;Integrated Security=True" providerName="System.Data.SqlClient" />
    </connectionStrings>
    

    以下示例显示新的 connectionStrings 节跟随 configSections 一起位于 configuration 节中:The following example shows the new connectionStrings section following configSections inside the configuration section:

    <configuration>
        <configSections>
        <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
        </configSections>
        <connectionStrings>
        <add name="TeamContext" connectionString="Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\Teams.mdf;Integrated Security=True"     providerName="System.Data.SqlClient" />
        </connectionStrings>
        ...
    

添加 TeamsController 和视图Add the TeamsController and views

  1. 在 Visual Studio 中生成项目。In Visual Studio, build the project.

  2. 在“解决方案资源管理器”中,右键单击“Controllers”文件夹,选择“添加”,再选择“控制器”。 In Solution Explorer, right-click the Controllers folder and choose Add, Controller.

  3. 选择“使用实体框架的包含视图的 MVC 5 控制器”并单击“添加”。 Choose MVC 5 Controller with views, using Entity Framework, and click Add. 如果在单击“添加” 后出现错误,请确保已先生成该项目。If you get an error after clicking Add, ensure that you have built the project first.

    添加控制器类

  4. 从“模型类”下拉列表中选择“Team (ContosoTeamStats.Models)”。 Select Team (ContosoTeamStats.Models) from the Model class drop-down list. 从“数据上下文类”下拉列表中选择“TeamContext (ContosoTeamStats.Models)”。 Select TeamContext (ContosoTeamStats.Models) from the Data context class drop-down list. 在“控制器”名称文本框中键入 TeamsController(如果尚未自动填充)。 Type TeamsController in the Controller name textbox (if it is not automatically populated). 单击“添加” 创建控制器类并添加默认视图。Click Add to create the controller class and add the default views.

    配置控制器

  5. 在“解决方案资源管理器”中展开“Global.asax”,并双击“Global.asax.cs”将其打开。 In Solution Explorer, expand Global.asax and double-click Global.asax.cs to open it.

    Global.asax.cs

  6. 将以下两个 using 语句添加到文件顶部的其他 using 语句下方:Add the following two using statements at the top of the file under the other using statements:

    using System.Data.Entity;
    using ContosoTeamStats.Models;
    
  7. Application_Start 方法的末尾添加以下代码行:Add the following line of code at the end of the Application_Start method:

    Database.SetInitializer<TeamContext>(new TeamInitializer());
    
  8. 在“解决方案资源管理器”中展开 App_Start,并双击 RouteConfig.csIn Solution Explorer, expand App_Start and double-click RouteConfig.cs.

    RouteConfig.cs

  9. RegisterRoutes 方法中,将 Default 路由中的 controller = "Home" 替换为 controller = "Teams",如以下示例所示:In the RegisterRoutes method, replace controller = "Home" in the Default route with controller = "Teams" as shown in the following example:

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Teams", action = "Index", id = UrlParameter.Optional }
    );
    

配置布局视图Configure the Layout view

  1. 在“解决方案资源管理器”中,先展开 Views 文件夹,再展开 Shared 文件夹,并双击 _Layout.cshtmlIn Solution Explorer, expand the Views folder and then the Shared folder, and double-click _Layout.cshtml.

    _Layout.cshtml

  2. 更改 title 元素的内容,将 My ASP.NET Application 替换为 Contoso Team Stats,如以下示例所示:Change the contents of the title element and replace My ASP.NET Application with Contoso Team Stats as shown in the following example:

    <title>@ViewBag.Title - Contoso Team Stats</title>
    
  3. body 节中,紧靠在 Azure Cache for Redis Test 链接的下方为 Contoso Team Stats 添加以下新的 Html.ActionLink 语句。In the body section, add the following new Html.ActionLink statement for Contoso Team Stats just below the link for Azure Cache for Redis Test.

    @Html.ActionLink("Contoso Team Stats", "Index", "Teams", new { area = "" }, new { @class = "navbar-brand" })`
    

    代码更改

  4. 按“Ctrl+F5” 生成并运行应用程序。Press Ctrl+F5 to build and run the application. 此版本的应用程序直接从数据库读取结果。This version of the application reads the results directly from the database. 请注意已通过“使用实体框架的包含视图的 MVC 5 控制器”基架自动添加到应用程序的“新建”、“编辑”、“详细信息”和“删除”操作。 Note the Create New, Edit, Details, and Delete actions that were automatically added to the application by the MVC 5 Controller with views, using Entity Framework scaffold. 在本教程的下一部分中,你将添加用于 Redis 的 Azure 缓存来优化数据访问,并向应用程序提供其他功能。In the next section of the tutorial, you'll add Azure Cache for Redis to optimize the data access and provide additional features to the application.

    入门应用程序

为用于 Redis 的 Azure 缓存配置应用Configure the app for Azure Cache for Redis

在本教程的此部分,请对示例应用程序进行配置,以便使用 StackExchange.Redis 缓存客户端通过用于 Redis 的 Azure 缓存实例来存储和检索 Contoso 团队统计信息。In this section of the tutorial, you configure the sample application to store and retrieve Contoso team statistics from an Azure Cache for Redis instance by using the StackExchange.Redis cache client.

将缓存连接添加到团队控制器Add a cache connection to the Teams Controller

在快速入门中,我们已安装 StackExchange.Redis 客户端库包。You already installed the StackExchange.Redis client library package in the quickstart. 我们还配置了要在本地使用的 CacheConnection 应用设置,以及发布的应用服务。You also have already configured the CacheConnection app setting to be used locally, and with the published App Service. 在本教程中请使用相同的客户端库以及 TeamsController 中的 CacheConnection 信息。Use this same client library and CacheConnection information in the TeamsController.

  1. 在“解决方案资源管理器”中,展开“Controllers”文件夹,并双击“TeamsController.cs”将其打开。 In Solution Explorer, expand the Controllers folder and double-click TeamsController.cs to open it.

    团队控制器

  2. 将以下两个 using 语句添加到 TeamsController.csAdd the following two using statements to TeamsController.cs:

    using System.Configuration;
    using StackExchange.Redis;
    
  3. 将以下两个属性添加到 TeamsController 类:Add the following two properties to the TeamsController class:

    // Redis Connection string info
    private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
    {
        string cacheConnection = ConfigurationManager.AppSettings["CacheConnection"].ToString();
        return ConnectionMultiplexer.Connect(cacheConnection);
    });
    
    public static ConnectionMultiplexer Connection
    {
        get
        {
            return lazyConnection.Value;
        }
    }
    

更新 TeamsController,以从缓存或数据库读取数据Update the TeamsController to read from the cache or the database

在此示例中,团队统计信息可以从数据库或缓存中检索。In this sample, team statistics can be retrieved from the database or from the cache. 团队统计信息以序列化 List<Team>的形式存储在缓存中,同时以排序集的形式按 Redis 数据类型存储。Team statistics are stored in the cache as a serialized List<Team>, and also as a sorted set using Redis data types. 从排序集检索项目时,可以检索部分项目或所有项目,也可以查询特定项目。When retrieving items from a sorted set, you can retrieve some, all, or query for certain items. 本示例将按赢局数在排序集中查询排名前 5 的团队。In this sample, you'll query the sorted set for the top 5 teams ranked by number of wins.

不需在缓存中以多种格式存储团队统计信息即可使用用于 Redis 的 Azure 缓存。It is not required to store the team statistics in multiple formats in the cache in order to use Azure Cache for Redis. 本教程以多种格式演示了缓存数据时可以使用的不同方法和不同数据类型的一部分。This tutorial uses multiple formats to demonstrate some of the different ways and different data types you can use to cache data.

  1. 将以下 using 语句添加到 TeamsController.cs 文件顶部,与其他 using 语句放置在一起:Add the following using statements to the TeamsController.cs file at the top with the other using statements:

    using System.Diagnostics;
    using Newtonsoft.Json;
    
  2. 将当前的 public ActionResult Index() 方法实现替换为以下实现:Replace the current public ActionResult Index() method implementation with the following implementation:

    // GET: Teams
    public ActionResult Index(string actionType, string resultType)
    {
        List<Team> teams = null;
    
        switch(actionType)
        {
            case "playGames": // Play a new season of games.
                PlayGames();
                break;
    
            case "clearCache": // Clear the results from the cache.
                ClearCachedTeams();
                break;
    
            case "rebuildDB": // Rebuild the database with sample data.
                RebuildDB();
                break;
        }
    
        // Measure the time it takes to retrieve the results.
        Stopwatch sw = Stopwatch.StartNew();
    
        switch(resultType)
        {
            case "teamsSortedSet": // Retrieve teams from sorted set.
                teams = GetFromSortedSet();
                break;
    
            case "teamsSortedSetTop5": // Retrieve the top 5 teams from the sorted set.
                teams = GetFromSortedSetTop5();
                break;
    
            case "teamsList": // Retrieve teams from the cached List<Team>.
                teams = GetFromList();
                break;
    
            case "fromDB": // Retrieve results from the database.
            default:
                teams = GetFromDB();
                break;
        }
    
        sw.Stop();
        double ms = sw.ElapsedTicks / (Stopwatch.Frequency / (1000.0));
    
        // Add the elapsed time of the operation to the ViewBag.msg.
        ViewBag.msg += " MS: " + ms.ToString();
    
        return View(teams);
    }
    
  3. 将以下三种方法添加到 TeamsController 类,以便实现在以前的代码片段中添加的 switch 语句中的 playGamesclearCacherebuildDB 操作类型。Add the following three methods to the TeamsController class to implement the playGames, clearCache, and rebuildDB action types from the switch statement added in the previous code snippet.

    PlayGames 方法可以通过对赛季进行模拟来更新团队统计信息,将结果保存到数据库,并从缓存中清除现已过时的数据。The PlayGames method updates the team statistics by simulating a season of games, saves the results to the database, and clears the now outdated data from the cache.

    void PlayGames()
    {
        ViewBag.msg += "Updating team statistics. ";
        // Play a "season" of games.
        var teams = from t in db.Teams
                    select t;
    
        Team.PlayGames(teams);
    
        db.SaveChanges();
    
        // Clear any cached results
        ClearCachedTeams();
    }
    

    RebuildDB 方法使用默认的团队集来重新初始化数据库,为其生成统计信息,并从缓存中清除现已过时的数据。The RebuildDB method reinitializes the database with the default set of teams, generates statistics for them, and clears the now outdated data from the cache.

    void RebuildDB()
    {
        ViewBag.msg += "Rebuilding DB. ";
        // Delete and re-initialize the database with sample data.
        db.Database.Delete();
        db.Database.Initialize(true);
    
        // Clear any cached results
        ClearCachedTeams();
    }
    

    ClearCachedTeams 方法从缓存中删除任何已缓存的团队统计信息。The ClearCachedTeams method removes any cached team statistics from the cache.

    void ClearCachedTeams()
    {
        IDatabase cache = Connection.GetDatabase();
        cache.KeyDelete("teamsList");
        cache.KeyDelete("teamsSortedSet");
        ViewBag.msg += "Team data removed from cache. ";
    }
    
  4. 将以下四种方法添加到 TeamsController 类,以便通过不同的方式从缓存和数据库检索团队统计信息。Add the following four methods to the TeamsController class to implement the various ways of retrieving the team statistics from the cache and the database. 这些方法均会返回 List<Team>,然后通过视图将其显示出来。Each of these methods returns a List<Team>, which is then displayed by the view.

    GetFromDB 方法从数据库读取团队统计信息。The GetFromDB method reads the team statistics from the database.

    List<Team> GetFromDB()
    {
        ViewBag.msg += "Results read from DB. ";
        var results = from t in db.Teams
            orderby t.Wins descending
            select t;
    
        return results.ToList<Team>();
    }
    

    GetFromList 方法以序列化 List<Team> 的形式从缓存读取团队统计信息。The GetFromList method reads the team statistics from cache as a serialized List<Team>. 如果缓存中没有统计信息,则会发生缓存未命中。If the statistics are not present in the cache, a cache miss occurs. 如果缓存未命中,则从数据库读取团队统计信息,然后将该信息存储在缓存中,供下一次请求使用。For a cache miss, the team statistics are read from the database and then stored in the cache for the next request. 本示例中使用 JSON.NET 序列化来序列化传入和传出缓存的 .NET 对象。In this sample, JSON.NET serialization is used to serialize the .NET objects to and from the cache. 有关详细信息,请参阅如何处理用于 Redis 的 Azure 缓存中的 .NET 对象For more information, see How to work with .NET objects in Azure Cache for Redis.

    List<Team> GetFromList()
    {
        List<Team> teams = null;
    
        IDatabase cache = Connection.GetDatabase();
        string serializedTeams = cache.StringGet("teamsList");
        if (!String.IsNullOrEmpty(serializedTeams))
        {
            teams = JsonConvert.DeserializeObject<List<Team>>(serializedTeams);
    
            ViewBag.msg += "List read from cache. ";
        }
        else
        {
            ViewBag.msg += "Teams list cache miss. ";
            // Get from database and store in cache
            teams = GetFromDB();
    
            ViewBag.msg += "Storing results to cache. ";
            cache.StringSet("teamsList", JsonConvert.SerializeObject(teams));
        }
        return teams;
    }
    

    GetFromSortedSet 方法从缓存的排序集读取团队统计信息。The GetFromSortedSet method reads the team statistics from a cached sorted set. 如果缓存未命中,则会从数据库读取团队统计信息,并将该信息以排序集的形式存储在缓存中。If there is a cache miss, the team statistics are read from the database and stored in the cache as a sorted set.

    List<Team> GetFromSortedSet()
    {
        List<Team> teams = null;
        IDatabase cache = Connection.GetDatabase();
        // If the key teamsSortedSet is not present, this method returns a 0 length collection.
        var teamsSortedSet = cache.SortedSetRangeByRankWithScores("teamsSortedSet", order: Order.Descending);
        if (teamsSortedSet.Count() > 0)
        {
            ViewBag.msg += "Reading sorted set from cache. ";
            teams = new List<Team>();
            foreach (var t in teamsSortedSet)
            {
                Team tt = JsonConvert.DeserializeObject<Team>(t.Element);
                teams.Add(tt);
            }
        }
        else
        {
            ViewBag.msg += "Teams sorted set cache miss. ";
    
            // Read from DB
            teams = GetFromDB();
    
            ViewBag.msg += "Storing results to cache. ";
            foreach (var t in teams)
            {
                Console.WriteLine("Adding to sorted set: {0} - {1}", t.Name, t.Wins);
                cache.SortedSetAdd("teamsSortedSet", JsonConvert.SerializeObject(t), t.Wins);
            }
        }
        return teams;
    }
    

    GetFromSortedSetTop5 方法从缓存的排序集中读取排名前 5 的团队。The GetFromSortedSetTop5 method reads the top five teams from the cached sorted set. 该方法首先会检查缓存中是否存在 teamsSortedSet 键。It starts by checking the cache for the existence of the teamsSortedSet key. 如果该键不存在,则会调用 GetFromSortedSet 方法以读取团队统计信息并将其存储在缓存中。If this key is not present, the GetFromSortedSet method is called to read the team statistics and store them in the cache. 接下来会对缓存的排序集进行查询,以便返回排名前 5 的团队。Next, the cached sorted set is queried for the top five teams, which are returned.

    List<Team> GetFromSortedSetTop5()
    {
        List<Team> teams = null;
        IDatabase cache = Connection.GetDatabase();
    
        // If the key teamsSortedSet is not present, this method returns a 0 length collection.
        var teamsSortedSet = cache.SortedSetRangeByRankWithScores("teamsSortedSet", stop: 4, order: Order.Descending);
        if(teamsSortedSet.Count() == 0)
        {
            // Load the entire sorted set into the cache.
            GetFromSortedSet();
    
            // Retrieve the top 5 teams.
            teamsSortedSet = cache.SortedSetRangeByRankWithScores("teamsSortedSet", stop: 4, order: Order.Descending);
        }
    
        ViewBag.msg += "Retrieving top 5 teams from cache. ";
        // Get the top 5 teams from the sorted set
        teams = new List<Team>();
        foreach (var team in teamsSortedSet)
        {
            teams.Add(JsonConvert.DeserializeObject<Team>(team.Element));
        }
        return teams;
    }
    

更新用于缓存的 Create、Edit 和 Delete 方法Update the Create, Edit, and Delete methods to work with the cache

作为此示例一部分生成的基架代码包括用于添加、编辑和删除团队的方法。The scaffolding code that was generated as part of this sample includes methods to add, edit, and delete teams. 只要添加、编辑或删除了团队,缓存中的数据就变成了过时的数据。Anytime a team is added, edited, or removed, the data in the cache becomes outdated. 在本部分,我们将修改这三个清除缓存团队的方法,以便刷新缓存。In this section, you'll modify these three methods to clear the cached teams so that the cache will be refreshed.

  1. 浏览到 TeamsController 类中的 Create(Team team) 方法。Browse to the Create(Team team) method in the TeamsController class. 添加对 ClearCachedTeams 方法的调用,如以下示例所示:Add a call to the ClearCachedTeams method, as shown in the following example:

    // POST: Teams/Create
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see https://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "ID,Name,Wins,Losses,Ties")] Team team)
    {
        if (ModelState.IsValid)
        {
            db.Teams.Add(team);
            db.SaveChanges();
            // When a team is added, the cache is out of date.
            // Clear the cached teams.
            ClearCachedTeams();
            return RedirectToAction("Index");
        }
    
        return View(team);
    }
    
  2. 浏览到 TeamsController 类中的 Edit(Team team) 方法。Browse to the Edit(Team team) method in the TeamsController class. 添加对 ClearCachedTeams 方法的调用,如以下示例所示:Add a call to the ClearCachedTeams method, as shown in the following example:

    // POST: Teams/Edit/5
    // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
    // more details see https://go.microsoft.com/fwlink/?LinkId=317598.
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit([Bind(Include = "ID,Name,Wins,Losses,Ties")] Team team)
    {
        if (ModelState.IsValid)
        {
            db.Entry(team).State = EntityState.Modified;
            db.SaveChanges();
            // When a team is edited, the cache is out of date.
            // Clear the cached teams.
            ClearCachedTeams();
            return RedirectToAction("Index");
        }
        return View(team);
    }
    
  3. 浏览到 TeamsController 类中的 DeleteConfirmed(int id) 方法。Browse to the DeleteConfirmed(int id) method in the TeamsController class. 添加对 ClearCachedTeams 方法的调用,如以下示例所示:Add a call to the ClearCachedTeams method, as shown in the following example:

    // POST: Teams/Delete/5
    [HttpPost, ActionName("Delete")]
    [ValidateAntiForgeryToken]
    public ActionResult DeleteConfirmed(int id)
    {
        Team team = db.Teams.Find(id);
        db.Teams.Remove(team);
        db.SaveChanges();
        // When a team is deleted, the cache is out of date.
        // Clear the cached teams.
        ClearCachedTeams();
        return RedirectToAction("Index");
    }
    

将缓存方法添加到“团队索引”视图Add caching methods to the Teams Index view

  1. 在“解决方案资源管理器”中,先展开 Views 文件夹,再展开 Teams 文件夹,然后双击“Index.cshtml”。 In Solution Explorer, expand the Views folder, then the Teams folder, and double-click Index.cshtml.

    Index.cshtml

  2. 在该文件顶部附近查找以下段落元素:Near the top of the file, look for the following paragraph element:

    操作表

    此链接创建新团队。This link creates a new team. 将段落元素替换为下表内容。Replace the paragraph element with the following table. 该表的操作链接可用于创建新的团队、举行新赛季的比赛、清除缓存、以多种格式从缓存中检索团队、从数据库检索团队,以及使用最新的示例数据重新构建数据库。This table has action links for creating a new team, playing a new season of games, clearing the cache, retrieving the teams from the cache in several formats, retrieving the teams from the database, and rebuilding the database with fresh sample data.

    <table class="table">
        <tr>
            <td>
                @Html.ActionLink("Create New", "Create")
            </td>
            <td>
                @Html.ActionLink("Play Season", "Index", new { actionType = "playGames" })
            </td>
            <td>
                @Html.ActionLink("Clear Cache", "Index", new { actionType = "clearCache" })
            </td>
            <td>
                @Html.ActionLink("List from Cache", "Index", new { resultType = "teamsList" })
            </td>
            <td>
                @Html.ActionLink("Sorted Set from Cache", "Index", new { resultType = "teamsSortedSet" })
            </td>
            <td>
                @Html.ActionLink("Top 5 Teams from Cache", "Index", new { resultType = "teamsSortedSetTop5" })
            </td>
            <td>
                @Html.ActionLink("Load from DB", "Index", new { resultType = "fromDB" })
            </td>
            <td>
                @Html.ActionLink("Rebuild DB", "Index", new { actionType = "rebuildDB" })
            </td>
        </tr>
    </table>
    
  3. 滚动到 Index.cshtml 文件底部,并添加下面的 tr 元素,使之成为文件中最后一个表的最后一行:Scroll to the bottom of the Index.cshtml file and add the following tr element so that it is the last row in the last table in the file:

    <tr><td colspan="5">@ViewBag.Msg</td></tr>
    

    此行返回值 ViewBag.Msg,其中包含有关当前操作的状态报告。This row displays the value of ViewBag.Msg which contains a status report about the current operation. 单击上一步中的任何操作链接即可设置 ViewBag.MsgThe ViewBag.Msg is set when you click any of the action links from the previous step.

    状态消息

  4. 按“F6” 生成项目。Press F6 to build the project.

在本地运行应用Run the app locally

在计算机本地运行应用程序,验证是否已添加相应的功能来支持团队。Run the application locally on your machine to verify the functionality that has been added to support the teams.

在此测试中,应用程序和数据库都在本地运行。In this test, the application and database, are both running locally. 但是,用于 Redis 的 Azure 缓存远程托管在 Azure 中。However, the Azure Cache for Redis is hosted remotely in Azure. 因此,缓存的性能可能比数据库略低。Therefore, the cache will likely under-perform the database slightly. 为了获得最佳性能,应让客户端应用程序和用于 Redis 的 Azure 缓存实例位于同一位置。For best performance, the client application and Azure Cache for Redis instance should be in the same location. 在下一部分,我们要将所有资源部署到 Azure,以查看在使用缓存后性能是否得到提升。In the next section, you will deploy all resources to Azure to see the improved performance from using a cache.

在本地运行应用:To run the app locally:

  1. 按“Ctrl+F5” 运行应用程序。Press Ctrl+F5 to run the application.

    本地运行的应用

  2. 测试已添加到视图的每个新方法。Test each of the new methods that were added to the view. 由于这些测试中的缓存在远程位置,因此数据库的性能应该比缓存略高。Since the cache is remote in these tests, the database should slightly outperform the cache.

在 Azure 中发布和运行Publish and run in Azure

为应用预配 SQL Azure 数据库Provision a SQL Azure database for the app

在本部分,我们将为应用预配新的 SQL Azure 数据库,以便在 Azure 中托管时使用。In this section, you will provision a new SQL Azure database for the app to use while hosted in Azure.

  1. Azure 门户中,单击左上角的“创建资源”。 In the Azure portal, Click Create a resource in the upper left-hand corner of the Azure portal.

  2. 在“新建”页上,单击“数据库” > “SQL 数据库”。 On the New page, click Databases > SQL Database.

  3. 对新 SQL 数据库使用以下设置:Use the following settings for the new SQL Database:

    设置Setting       建议的值Suggested value 说明Description
    数据库名称Database name ContosoTeamsDatabaseContosoTeamsDatabase 如需有效的数据库名称,请参阅 Database Identifiers(数据库标识符)。For valid database names, see Database Identifiers.
    订阅Subscription 订阅Your subscription 选择用于创建缓存和托管应用服务的同一订阅。Select the same subscription you used to create the cache and host the App Service.
    资源组Resource group TestResourceGroupTestResourceGroup 单击“使用现有项”,并使用缓存和应用服务所在的同一资源组。 Click Use existing and use the same resource group where you placed your cache and App Service.
    选择源Select source 空白数据库Blank database 从空白数据库开始。Start with a blank database.
  4. 在“服务器”下,单击“配置所需的设置” > “创建新服务器”并提供以下信息,然后单击“选择”按钮: Under Server, click Configure required settings > Create a new server and provide the following information and then click the Select button:

    设置Setting       建议的值Suggested value 说明Description
    服务器名称Server name 任何全局唯一名称Any globally unique name
    服务器管理员登录名Server admin login 任何有效的名称Any valid name 如需有效的登录名,请参阅 Database Identifiers(数据库标识符)。For valid login names, see Database Identifiers.
    密码Password 任何有效的密码Any valid password 密码必须至少有 8 个字符,且必须包含以下类别中的三个类别的字符:大写字符、小写字符、数字以及非字母数字字符。Your password must have at least 8 characters and must contain characters from three of the following categories: upper case characters, lower case characters, numbers, and non-alphanumeric characters.
    LocationLocation 华北China North 选择创建缓存和应用服务的同一区域。Select the same region where you created the cache and App Service.
  5. 单击“固定到仪表板”,然后单击“创建”以创建新数据库和服务器。 Click Pin to dashboard and then Create to create the new database and server.

  6. 创建新数据库后,单击“显示数据库连接字符串”,并复制 ADO.NET 连接字符串。 Once the new database is created, click Show database connection strings and copy the ADO.NET connection string.

    显示连接字符串

  7. 在 Azure 门户中,导航到应用服务并单击“应用程序设置”,然后在“连接字符串”部分下单击“添加新的连接字符串”。 In the Azure portal, navigate to your App Service and click Application Settings, then Add new connection string under the Connection strings section.

  8. 添加名为 TeamContext 的新连接字符串以匹配实体框架数据库上下文类。Add a new connection string named TeamContext to match the Entity Framework database context class. 粘贴新数据库的连接字符串作为值。Paste the connection string for your new database as the value. 请务必替换连接字符串中的以下占位符,然后单击“保存”: Be sure to replace the following placeholders in the connection string and click Save:

    占位符Placeholder 建议的值Suggested value
    {your_username}{your_username} 使用刚刚创建的数据库服务器的服务器管理员登录名Use the server admin login for the database server you just created.
    {your_password}{your_password} 使用刚刚创建的数据库服务器的密码。Use the password for the database server you just created.

    将用户名和密码添加为应用程序设置后,你的用户名和密码将不会包含在代码中。By adding the username and password as an Application Setting, your username and password are not included in your code. 这种做法有助于保护这些凭据。This approach helps protect those credentials.

将应用程序更新发布到 AzurePublish the application updates to Azure

在本教程的此步骤,我们将应用程序更新发布到 Azure 并在云中运行应用程序。In this step of the tutorial, you'll publish the application updates to Azure to run it in the cloud.

  1. 在 Visual Studio 中右键单击“ContosoTeamStats”项目,并选择“发布”。 Right-click the ContosoTeamStats project in Visual Studio and choose Publish.

    发布

  2. 单击“发布”,使用快速入门中创建的相同发布配置文件。 Click Publish to use the same publishing profile you created in the quickstart.

  3. 完成发布后,Visual Studio 会在默认的 Web 浏览器中启动应用。Once publishing is complete, Visual Studio launches the app in your default web browser.

    添加的缓存

    下表描述了示例应用程序中的每个操作链接:The following table describes each action link from the sample application:

    操作Action 说明Description
    新建Create New 创建新的团队。Create a new Team.
    举行赛季的比赛Play Season 举行一赛季的比赛、更新团队统计信息,以及从缓存中清除任何过时的团队数据。Play a season of games, update the team stats, and clear any outdated team data from the cache.
    清除缓存Clear Cache 清除缓存中的团队统计信息。Clear the team stats from the cache.
    缓存中的列表List from Cache 检索缓存中的团队统计信息。Retrieve the team stats from the cache. 如果缓存未命中,则从数据库加载统计信息,并将其保存到缓存中以便下次使用。If there is a cache miss, load the stats from the database and save to the cache for next time.
    缓存中的排序集Sorted Set from Cache 使用排序集检索缓存中的团队统计信息。Retrieve the team stats from the cache using a sorted set. 如果缓存未命中,则从数据库加载统计信息,并使用排序集将其保存到缓存中。If there is a cache miss, load the stats from the database and save to the cache using a sorted set.
    缓存中排名前 5 的团队Top 5 Teams from Cache 使用排序集检索缓存中排名前 5 的团队。Retrieve the top 5 teams from the cache using a sorted set. 如果缓存未命中,则从数据库加载统计信息,并使用排序集将其保存到缓存中。If there is a cache miss, load the stats from the database and save to the cache using a sorted set.
    从数据库加载Load from DB 检索数据库中的团队统计信息。Retrieve the team stats from the database.
    重新生成数据库Rebuild DB 重新生成数据库并使用示例团队数据重新加载它。Rebuild the database and reload it with sample team data.
    编辑/详细信息/删除Edit / Details / Delete 编辑团队、查看团队详细信息、删除团队。Edit a team, view details for a team, delete a team.

单击部分操作,试验如何从不同的源检索数据。Click some of the actions and experiment with retrieving the data from the different sources. 请注意通过不同方式从数据库和缓存检索数据所存在的时间差异。Note the differences in the time it takes to complete the various ways of retrieving the data from the database and the cache.

清理资源Clean up resources

完成示例性的教程应用程序以后,即可删除所用的 Azure 资源,以便节省成本和资源。When you are finished with the sample tutorial application, you can delete the Azure resources used in order to conserve cost and resources. 所有资源应包含在同一资源组中。可以通过一个操作将这些资源一并删除:删除该资源组即可。All of your resources should be contained in the same resource group, you can delete them together in one operation by deleting the resource group. 本主题的说明使用了名为 TestResources 的资源组。The instructions for this topic used a resource group named TestResources.

Important

删除资源组的操作不可逆,资源组以及其中的所有资源将被永久删除。Deleting a resource group is irreversible and that the resource group and all the resources in it are permanently deleted. 请确保不会意外删除错误的资源组或资源。Make sure that you do not accidentally delete the wrong resource group or resources. 如果在现有资源组(其中包含要保留的资源)中为托管此示例而创建了相关资源,则可从各自的边栏选项卡逐个删除这些资源。If you created the resources for hosting this sample inside an existing resource group, that contains resources you want to keep, you can delete each resource individually from their respective blades.

  1. 登录到 Azure 门户,然后单击“资源组”。 Sign in to the Azure portal and click Resource groups.

  2. 在“筛选项目...” 文本框中键入资源组的名称。Type the name of your resource group into the Filter items... textbox.

  3. 单击资源组右侧的“...”,然后单击“删除资源组”。 Click ... to the right of your resource group and click Delete resource group.

    Delete

  4. 系统会要求确认是否删除资源组。You will be asked to confirm the deletion of the resource group. 键入资源组的名称进行确认,然后单击“删除” 。Type the name of your resource group to confirm, and click Delete.

    片刻之后,将会删除该资源组及其包含的所有资源。After a few moments, the resource group and all of its contained resources are deleted.

后续步骤Next steps