.NET 6 Web API を使用した Azure Redis Cache の実装:ステップバイステップ ガイド
この記事では、Azure Redis Cache の基本と、.NET Core Web API を使用したその実装について説明します。
議題
- はじめに
- キャッシュとは何ですか?
- キャッシュの種類
- Redis キャッシュ
- Azure Redis キャッシュのセットアップ
- 段階的な実装
前提条件
- Visual Studio 2022
- Azure アカウント
- .NET Core 6
はじめに
キャッシュはアプリケーションのパフォーマンスとスケーラビリティを向上させるため、現在ソフトウェア業界で非常に人気があります。私たちは Gmail や Facebook などの多くの Web アプリケーションを使用しており、それらの応答性と優れたユーザー エクスペリエンスを確認しています。インターネットを使用するユーザーはたくさんいます。アプリケーションに膨大なネットワーク トラフィックと需要がある場合、アプリケーションのパフォーマンスと応答性を向上させるために、多くのことに対処する必要があります。そのため、キャッシングという解決策があり、それがキャッシングが登場する理由です。
キャッシュとは何ですか?
キャッシュは、頻繁にアクセスされるデータを一時ストレージに保存するために使用されるメモリ ストレージです。これにより、パフォーマンスが大幅に向上し、不要なデータベース ヒットが回避され、頻繁に使用されるデータがキャッシュに保存されます。


上の画像からわかるように、2 つのシナリオがあり、1 つはキャッシュを使用しないシナリオ、もう 1 つはキャッシュを使用するシナリオです。したがって、ここでキャッシュを使用しない場合、その場合、ユーザーがデータを必要とすると、毎回データベースにアクセスすることになり、ユーザーが必要とする静的データがあり、それがすべてのユーザーで同じである場合、時間の複雑さが増加し、パフォーマンスが低下します。キャッシュを使用しない場合、それぞれが不要なデータベースにアクセスしてデータを取得することになります。反対側です。キャッシュを使用していることがわかります。この場合、すべてのユーザーに同じ静的で同じデータがある場合、最初のユーザーだけがデータベースにアクセスしてデータをフェッチし、キャッシュ メモリに保存します。その後、他の 2 人のユーザーは不必要にデータベースにアクセスしてデータをフェッチすることなく、キャッシュからそのデータを使用しました。
キャッシュの種類
基本的に、.NET Core がサポートするキャッシュには 2 種類あります
<オル>インメモリ キャッシュを使用すると、データはアプリケーション サーバーのメモリに保存されます。必要なときにいつでもそこからデータを取得し、必要なときにいつでも使用できます。また、分散キャッシュには、Redis やその他の多くのサードパーティ メカニズムが存在します。ただし、このセクションでは、Redis キャッシュの詳細と、.NET Core での Redis キャッシュの動作について説明します。
分散キャッシュ

- 基本的に、分散キャッシュではデータが保存され、複数のサーバー間で共有されます
- また、マルチテナント アプリケーションを使用する場合、複数のサーバー間の負荷を管理した後、アプリケーションのスケーラビリティとパフォーマンスを簡単に向上させることができます
- 将来、1 つのサーバーがクラッシュして再起動しても、必要に応じて複数のサーバーが必要になるため、アプリケーションには影響がないとします。
Redis は、アプリケーションのパフォーマンスとスケーラビリティを向上させるために、現在多くの企業で使用されている最も人気のあるキャッシュです。したがって、Redis とその使用法について 1 つずつ説明していきます。
Redis キャッシュ
- Redis は、データベースとして使用されるオープンソース (BSD ライセンス) インメモリ データ構造ストアです。
- 基本的に、頻繁に使用されるデータと一部の静的データをキャッシュ内に保存し、ユーザーの要件に応じて使用および予約するために使用されます。
- Redis には、List、Set、Hashing、Stream など、データの保存に使用できる多くのデータ構造が存在します。
Azure Redis キャッシュのセットアップ
ステップ 1
Azure ポータルにログインします。
ステップ 2
マーケットプレイスで Azure Cache for Redis を検索して開きます。

ステップ 3
「作成」をクリックして、その他の情報を入力します。




ステップ 4
その後、前に作成したキャッシュ内のアクセス キー セクションに移動し、.NET Core Web API 内で必要なプライマリ接続文字列をコピーします。

段階的な実装
ステップ 1
ビジュアル スタジオを開き、新しい .NET Core Web API プロジェクトを作成します。

ステップ 2
新しいプロジェクトを設定します。
ステップ 3
追加の詳細を入力します。
ステップ 4
プロジェクトの構造。
ステップ 5
商品詳細クラスを作成します。
namespace AzureRedisCacheDemo.Models {
public class ProductDetails {
public int Id {
get;
set;
}
public string ProductName {
get;
set;
}
public string ProductDescription {
get;
set;
}
public int ProductPrice {
get;
set;
}
public int ProductStock {
get;
set;
}
}
} ステップ 6
次に、Data フォルダー内に Db Context Class を追加します。
using AzureRedisCacheDemo.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
namespace AzureRedisCacheDemo.Data {
public class DbContextClass: DbContext {
public DbContextClass(DbContextOptions < DbContextClass > options): base(options) {}
public DbSet < ProductDetails > Products {
get;
set;
}
}
} ステップ 7
その後、最初にデータを挿入するために使用する Seed Data クラスを追加します。
using AzureRedisCacheDemo.Models;
using Microsoft.EntityFrameworkCore;
namespace AzureRedisCacheDemo.Data
{
public class SeedData
{
public static void Initialize(IServiceProvider serviceProvider)
{
using (var context = new DbContextClass(
serviceProvider.GetRequiredService<DbContextOptions<DbContextClass>>()))
{
if (context.Products.Any())
{
return;
}
context.Products.AddRange(
new ProductDetails
{
Id = 1,
ProductName = "IPhone",
ProductDescription = "IPhone 14",
ProductPrice = 120000,
ProductStock = 100
},
new ProductDetails
{
Id = 2,
ProductName = "Samsung TV",
ProductDescription = "Smart TV",
ProductPrice = 400000,
ProductStock = 120
});
context.SaveChanges();
}
}
}
} ステップ 8
appsettings.json ファイル内で Azure Redis Cache 接続文字列を構成します。
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"RedisURL": "<valuefromportal>"
} ステップ 9
接続目的で使用する Helper フォルダ内に Configuration Manager と Connection Helper クラスを作成します。
構成マネージャー
namespace AzureRedisCacheDemo.Helper {
static class ConfigurationManager {
public static IConfiguration AppSetting {
get;
}
static ConfigurationManager() {
AppSetting = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json").Build();
}
}
} 接続ヘルパー
using StackExchange.Redis;
namespace AzureRedisCacheDemo.Helper {
public class ConnectionHelper {
static ConnectionHelper() {
ConnectionHelper.lazyConnection = new Lazy < ConnectionMultiplexer > (() => {
return ConnectionMultiplexer.Connect(ConfigurationManager.AppSetting["RedisURL"]);
});
}
private static Lazy < ConnectionMultiplexer > lazyConnection;
public static ConnectionMultiplexer Connection {
get {
return lazyConnection.Value;
}
}
}
} ステップ 10
次に、リポジトリ内に IProductService を追加します。
using AzureRedisCacheDemo.Models;
namespace AzureRedisCacheDemo.Repositories {
public interface IProductService {
public Task < List < ProductDetails >> ProductListAsync();
public Task < ProductDetails > GetProductDetailByIdAsync(int productId);
public Task < bool > AddProductAsync(ProductDetails productDetails);
public Task < bool > UpdateProductAsync(ProductDetails productDetails);
public Task < bool > DeleteProductAsync(int productId);
}
} ステップ 11
次に、ProductService クラスを作成し、その中に IProductService インターフェイスを実装します。
using AzureRedisCacheDemo.Data;
using AzureRedisCacheDemo.Models;
using Microsoft.EntityFrameworkCore;
using System.Linq;
namespace AzureRedisCacheDemo.Repositories {
public class ProductService: IProductService {
private readonly DbContextClass dbContextClass;
public ProductService(DbContextClass dbContextClass) {
this.dbContextClass = dbContextClass;
}
public async Task < List < ProductDetails >> ProductListAsync() {
return await dbContextClass.Products.ToListAsync();
}
public async Task < ProductDetails > GetProductDetailByIdAsync(int productId) {
return await dbContextClass.Products.Where(ele => ele.Id == productId).FirstOrDefaultAsync();
}
public async Task < bool > AddProductAsync(ProductDetails productDetails) {
await dbContextClass.Products.AddAsync(productDetails);
var result = await dbContextClass.SaveChangesAsync();
if (result > 0) {
return true;
} else {
return false;
}
}
public async Task < bool > UpdateProductAsync(ProductDetails productDetails) {
var isProduct = ProductDetailsExists(productDetails.Id);
if (isProduct) {
dbContextClass.Products.Update(productDetails);
var result = await dbContextClass.SaveChangesAsync();
if (result > 0) {
return true;
} else {
return false;
}
}
return false;
}
public async Task < bool > DeleteProductAsync(int productId) {
var findProductData = dbContextClass.Products.Where(_ => _.Id == productId).FirstOrDefault();
if (findProductData != null) {
dbContextClass.Products.Remove(findProductData);
var result = await dbContextClass.SaveChangesAsync();
if (result > 0) {
return true;
} else {
return false;
}
}
return false;
}
private bool ProductDetailsExists(int productId) {
return dbContextClass.Products.Any(e => e.Id == productId);
}
}
} ステップ 12
IRedisCache インターフェースを作成します。
namespace AzureRedisCacheDemo.Repositories.AzureRedisCache {
public interface IRedisCache {
T GetCacheData < T > (string key);
bool SetCacheData < T > (string key, T value, DateTimeOffset expirationTime);
object RemoveData(string key);
}
} ステップ 13
その後、RedisCache クラスを作成し、前に作成したインターフェイス メソッドをその中に実装します。
using AzureRedisCacheDemo.Helper;
using Newtonsoft.Json;
using StackExchange.Redis;
namespace AzureRedisCacheDemo.Repositories.AzureRedisCache
{
public class RedisCache : IRedisCache
{
private IDatabase _db;
public RedisCache()
{
ConfigureRedis();
}
private void ConfigureRedis()
{
_db = ConnectionHelper.Connection.GetDatabase();
}
public T GetCacheData<T>(string key)
{
var value = _db.StringGet(key);
if (!string.IsNullOrEmpty(value))
{
return JsonConvert.DeserializeObject<T>(value);
}
return default;
}
public object RemoveData(string key)
{
bool _isKeyExist = _db.KeyExists(key);
if (_isKeyExist == true)
{
return _db.KeyDelete(key);
}
return false;
}
public bool SetCacheData<T>(string key, T value, DateTimeOffset expirationTime)
{
TimeSpan expiryTime = expirationTime.DateTime.Subtract(DateTime.Now);
var isSet = _db.StringSet(key, JsonConvert.SerializeObject(value), expiryTime);
return isSet;
}
}
} ステップ 14
新しいプロダクト コントローラーを作成します。
using AzureRedisCacheDemo.Models;
using AzureRedisCacheDemo.Repositories;
using AzureRedisCacheDemo.Repositories.AzureRedisCache;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace AzureRedisCacheDemo.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
private readonly IProductService _productService;
private readonly IRedisCache _redisCache;
public ProductsController(IProductService productService, IRedisCache redisCache)
{
_productService = productService;
_redisCache = redisCache;
}
/// <summary>
/// Product List
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult<List<ProductDetails>>> ProductListAsync()
{
var cacheData = _redisCache.GetCacheData<List<ProductDetails>>("product");
if (cacheData != null)
{
return new List<ProductDetails>(cacheData);
}
var productList = await _productService.ProductListAsync();
if(productList != null)
{
var expirationTime = DateTimeOffset.Now.AddMinutes(5.0);
_redisCache.SetCacheData<List<ProductDetails>>("product", productList, expirationTime);
return Ok(productList);
}
else
{
return NoContent();
}
}
/// <summary>
/// Get Product By Id
/// </summary>
/// <param name="productId"></param>
/// <returns></returns>
[HttpGet("{productId}")]
public async Task<ActionResult<ProductDetails>> GetProductDetailsByIdAsync(int productId)
{
var cacheData = _redisCache.GetCacheData<List<ProductDetails>>("product");
if (cacheData != null)
{
ProductDetails filteredData = cacheData.Where(x => x.Id == productId).FirstOrDefault();
return new ActionResult<ProductDetails>(filteredData);
}
var productDetails = await _productService.GetProductDetailByIdAsync(productId);
if(productDetails != null)
{
return Ok(productDetails);
}
else
{
return NotFound();
}
}
/// <summary>
/// Add a new product
/// </summary>
/// <param name="productDetails"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> AddProductAsync(ProductDetails productDetails)
{
var isProductInserted = await _productService.AddProductAsync(productDetails);
_redisCache.RemoveData("product");
if (isProductInserted)
{
return Ok(isProductInserted);
}
else
{
return BadRequest();
}
}
/// <summary>
/// Update product details
/// </summary>
/// <param name="productDetails"></param>
/// <returns></returns>
[HttpPut]
public async Task<IActionResult> UpdateProductAsync(ProductDetails productDetails)
{
var isProductUpdated = await _productService.UpdateProductAsync(productDetails);
_redisCache.RemoveData("product");
if (isProductUpdated)
{
return Ok(isProductUpdated);
}
else
{
return BadRequest();
}
}
/// <summary>
/// Delete product by id
/// </summary>
/// <param name="productId"></param>
/// <returns></returns>
[HttpDelete]
public async Task<IActionResult> DeleteProductAsync(int productId)
{
var isProductDeleted = await _productService.DeleteProductAsync(productId);
_redisCache.RemoveData("product");
if (isProductDeleted)
{
return Ok(isProductDeleted);
}
else
{
return BadRequest();
}
}
}
} ステップ 15
Program クラス内にいくつかのサービスを登録します。
using AzureRedisCacheDemo.Data;
using AzureRedisCacheDemo.Models;
using AzureRedisCacheDemo.Repositories;
using AzureRedisCacheDemo.Repositories.AzureRedisCache;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using System;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddScoped < IProductService, ProductService > ();
builder.Services.AddDbContext < DbContextClass > (o => o.UseInMemoryDatabase("RedisCacheDemo"));
builder.Services.AddScoped < IRedisCache, RedisCache > ();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
using(var scope = app.Services.CreateScope()) {
var services = scope.ServiceProvider;
var context = services.GetRequiredService < DbContextClass > ();
SeedData.Initialize(services);
}
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) {
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run(); ステップ 16
最後に、アプリケーションを実行すると、API のエンドポイントを備えた Swagger UI が表示されます。
ステップ 17
[製品の取得] エンドポイントを押して、Azure Portal 内で Redis CLI を開くと、最初にエンドポイントに到達したときに製品リストが保存されていることがわかります。
この場合、まずデータがキャッシュに存在するかどうかを確認します。そうでない場合は、データベースからデータを取得し、キャッシュに保存します。これに関連するコードはコントローラー内にすでに記述されています。そこで、次回はキャッシュからデータを取得します。コントローラの中にデバッガを入れておけば、どのように動作するのかが分かりやすくなります。
GitHub URL
https://github.com/Jaydeep-007/AzureRedisCacheDemo/tree/master/AzureRedisCacheDemo
結論
ここでは、Azure でのキャッシュの導入とその構成について調べました。また、.NET Core Web API を使用した段階的な実装。
-
注目すべきRedisConf2021セッションは次のとおりです
毎年RedisコミュニティがRedisConfのために集まり、今年はリアルタイムデータの力を再発見することがすべてでした。 122か国から12,000人を超える開発者、建築家、ビジネスおよびテクノロジーリーダーが、ライブ基調講演、炉辺談話、および60回以上の分科会セッションに登録しました。 5月20日の時点で、すべてのRedisConf2021セッションはYouTubeチャンネルで視聴できます。 ユニークなユースケースをカバーする多くのセッションで、私たちは投票を行い、10の異なる最上級のトップブレイクアウトセッションをまとめました。受賞者は次のとおりです。 最も面白い講演者 CEOのA
-
Redis を使用したインメモリ キャッシュの習得:高速で信頼性の高いデータ取得
迅速に応答する必要があるウェブ アプリや API を構築している場合、多くの場合、キャッシュが秘密のソースになります。 これがないと、サーバーはデータベース、サードパーティ API、または低速ストレージ システムから同じデータを何度も取得するために時間を無駄にする可能性があります。 しかし、そのデータをメモリに保存すると、同じ情報が数ミリ秒で提供されます。そこで Redis が登場します。 Redis は、データを RAM に保存し、即座に取得できる高速かつ柔軟なツールです。ダッシュボードの構築、ソーシャル メディアへの投稿の自動化、ユーザー セッションの管理のいずれの場合でも、Redi