我們聊聊 C# 通用 Cache 緩沖類開發(fā)指南
在現(xiàn)代應(yīng)用程序中,緩存是一種有效的優(yōu)化手段,能夠顯著提高系統(tǒng)的性能,減少延遲。緩存可以用于數(shù)據(jù)庫查詢結(jié)果的存儲(chǔ)、API 響應(yīng)的緩存、復(fù)雜計(jì)算結(jié)果的保存等多個(gè)場景。本文將介紹如何開發(fā)一個(gè)通用的 CacheService 類,并展示其在不同場景中的應(yīng)用。
引言
緩存是一種存儲(chǔ)機(jī)制,用于臨時(shí)性地保存數(shù)據(jù),以減少數(shù)據(jù)獲取的時(shí)間和頻次。在高性能應(yīng)用程序中,合理使用緩存,可以顯著提高系統(tǒng)的響應(yīng)速度,減輕后臺(tái)服務(wù)的負(fù)擔(dān)。接下來我們講解如何開發(fā)一個(gè)通用的 CacheService 并展示其在具體場景中的應(yīng)用。
開發(fā)通用的 CacheService
ICacheService 接口
首先,定義一個(gè) ICacheService 接口,定義基本的緩存操作:
using System;
using System.Threading.Tasks;
public interface ICacheService
{
void Set<T>(string key, T value, TimeSpan expiration);
T Get<T>(string key);
Task SetAsync<T>(string key, T value, TimeSpan expiration);
Task<T> GetAsync<T>(string key);
void Remove(string key);
Task RemoveAsync(string key);
}
CacheService 實(shí)現(xiàn)
接下來,開發(fā) CacheService 類,它實(shí)現(xiàn)了 ICacheService 接口。該類同時(shí)支持內(nèi)存緩存和分布式緩存(例如 Redis),基于啟動(dòng)時(shí)的配置選擇緩存方式:
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
public class CacheService : ICacheService
{
private readonly IMemoryCache _memoryCache;
private readonly IDistributedCache _distributedCache;
private readonly ILogger<CacheService> _logger;
private readonly bool _useDistributedCache;
public CacheService(IMemoryCache memoryCache, IDistributedCache distributedCache, ILogger<CacheService> logger, bool useDistributedCache = false)
{
_memoryCache = memoryCache;
_distributedCache = distributedCache;
_logger = logger;
_useDistributedCache = useDistributedCache;
}
public void Set<T>(string key, T value, TimeSpan expiration)
{
if (_useDistributedCache)
{
var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = expiration
};
var serializedValue = System.Text.Json.JsonSerializer.Serialize(value);
_distributedCache.SetString(key, serializedValue, options);
}
else
{
var cacheEntryOptions = new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = expiration
};
_memoryCache.Set(key, value, cacheEntryOptions);
}
}
public T Get<T>(string key)
{
if (_useDistributedCache)
{
var serializedValue = _distributedCache.GetString(key);
if (serializedValue != null)
{
return System.Text.Json.JsonSerializer.Deserialize<T>(serializedValue);
}
return default;
}
_memoryCache.TryGetValue(key, out T value);
return value;
}
public async Task SetAsync<T>(string key, T value, TimeSpan expiration)
{
if (_useDistributedCache)
{
var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = expiration
};
var serializedValue = System.Text.Json.JsonSerializer.Serialize(value);
await _distributedCache.SetStringAsync(key, serializedValue, options);
}
else
{
var cacheEntryOptions = new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = expiration
};
_memoryCache.Set(key, value, cacheEntryOptions);
}
}
public async Task<T> GetAsync<T>(string key)
{
if (_useDistributedCache)
{
var serializedValue = await _distributedCache.GetStringAsync(key);
if (serializedValue != null)
{
return System.Text.Json.JsonSerializer.Deserialize<T>(serializedValue);
}
return default;
}
_memoryCache.TryGetValue(key, out T value);
return await Task.FromResult(value);
}
public void Remove(string key)
{
if (_useDistributedCache)
{
_distributedCache.Remove(key);
}
else
{
_memoryCache.Remove(key);
}
}
public async Task RemoveAsync(string key)
{
if (_useDistributedCache)
{
await _distributedCache.RemoveAsync(key);
}
else
{
_memoryCache.Remove(key);
}
}
}
配置依賴注入
在項(xiàng)目的 Program.cs 中配置依賴注入:
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
public class Program
{
static void Main(string[] args)
{
var serviceProvider = new ServiceCollection()
.AddMemoryCache()
.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
})
.AddLogging()
.AddSingleton<CacheService>()
.BuildServiceProvider();
// 使用緩存服務(wù)的示例
var cacheService = serviceProvider.GetService<CacheService>();
cacheService.Set("key1", "value1",new TimeSpan(0, 10, 0));
var value = cacheService.Get<string>("key1");
}
}
我們還可以啟用分布試的緩沖(Redis);
static void Main(string[] args)
{
var serviceProvider = new ServiceCollection()
.AddMemoryCache()
.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
})
.AddLogging()
.AddSingleton<CacheService>(sp =>
{
var memoryCache = sp.GetRequiredService<IMemoryCache>();
var distributedCache = sp.GetRequiredService<IDistributedCache>();
var logger = sp.GetRequiredService<ILogger<CacheService>>();
return new CacheService(memoryCache, distributedCache, logger, useDistributedCache: true);
})
.BuildServiceProvider();
// 使用緩存服務(wù)的示例
var cacheService = serviceProvider.GetService<CacheService>();
cacheService.Set("key1", "value1",new TimeSpan(0, 10, 0));
var value = cacheService.Get<string>("key1");
}
圖片
緩存應(yīng)用場景示例
數(shù)據(jù)庫查詢結(jié)果緩存
DatabaseService.cs
using System;
public class DatabaseService
{
private readonly ICacheService _cacheService;
public DatabaseService(ICacheService cacheService)
{
_cacheService = cacheService;
}
public string GetDataFromDatabase(string query)
{
string cacheKey = $"DatabaseQuery-{query}";
var cacheData = _cacheService.Get<string>(cacheKey);
if (cacheData != null)
{
return cacheData;
}
// 模擬數(shù)據(jù)庫查詢操作
string data = "返回?cái)?shù)據(jù)";
_cacheService.Set(cacheKey, data, TimeSpan.FromMinutes(5));
return data;
}
}
程序入口
internal class Program
{
static void Main(string[] args)
{
var serviceProvider = new ServiceCollection()
.AddMemoryCache()
.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
})
.AddLogging()
.AddSingleton<CacheService>()
.AddSingleton<ICacheService, CacheService>()
.AddSingleton<DatabaseService>()
.BuildServiceProvider();
var databaseService = serviceProvider.GetService<DatabaseService>();
string query = "SELECT * FROM Users";
Console.WriteLine("Query Result: " + databaseService.GetDataFromDatabase(query));
// 再次調(diào)用以展示緩存效果
Console.WriteLine("Query Result from Cache: " + databaseService.GetDataFromDatabase(query));
}
}
圖片
API 響應(yīng)緩存
ApiService.cs
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class ApiService
{
private readonly ICacheService _cacheService;
private readonly HttpClient _httpClient;
public ApiService(ICacheService cacheService, HttpClient httpClient)
{
_cacheService = cacheService;
_httpClient = httpClient;
}
public async Task<string> GetApiResponseAsync(string url)
{
string cacheKey = $"ApiUrl-{url}";
var cacheData = await _cacheService.GetAsync<string>(cacheKey);
if (cacheData != null)
{
return cacheData;
}
var response = await _httpClient.GetStringAsync(url);
await _cacheService.SetAsync(cacheKey, response, TimeSpan.FromMinutes(10));
return response;
}
}
程序入口
using System.Net.Http;
using System.Threading.Tasks;
public class Program
{
public static async Task Main(string[] args)
{
var serviceProvider = new ServiceCollection()
.AddMemoryCache()
.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
})
.AddLogging()
.AddSingleton<HttpClient>()
.AddSingleton<CacheService>()
.AddSingleton<ICacheService, CacheService>()
.AddSingleton<ApiService>()
.BuildServiceProvider();
var apiService = serviceProvider.GetService<ApiService>();
string apiUrl = "https://jsonplaceholder.typicode.com/posts";
var result = await apiService.GetApiResponseAsync(apiUrl);
Console.WriteLine("API Response: " + result);
// 再次調(diào)用以展示緩存效果
var cachedResult = await apiService.GetApiResponseAsync(apiUrl);
Console.WriteLine("Cached API Response: " + cachedResult);
}
}
圖片
復(fù)雜計(jì)算結(jié)果緩存
CalculationService.cs
using System;
public class CalculationService
{
private readonly ICacheService _cacheService;
public CalculationService(ICacheService cacheService)
{
_cacheService = cacheService;
}
public int HeavyComputation(int input)
{
string cacheKey = $"HeavyComputation-{input}";
var cacheData = _cacheService.Get<int>(cacheKey);
if (cacheData != 0)
{
return cacheData;
}
// 模擬復(fù)雜計(jì)算
int result = input * input;
_cacheService.Set(cacheKey, result, TimeSpan.FromMinutes(10));
return result;
}
}
程序入口
public class Program
{
public static void Main(string[] args)
{
var serviceProvider = new ServiceCollection()
.AddMemoryCache()
.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379";
})
.AddLogging()
.AddSingleton<CacheService>()
.AddSingleton<ICacheService, CacheService>()
.AddSingleton<CalculationService>()
.BuildServiceProvider();
var calculationService = serviceProvider.GetService<CalculationService>();
int input = 42;
Console.WriteLine("Computation Result: " + calculationService.HeavyComputation(input));
// 再次調(diào)用以展示緩存效果
Console.WriteLine("Cached Computation Result: " + calculationService.HeavyComputation(input));
}
}
總結(jié)
本文介紹了如何開發(fā)一個(gè)通用的 CacheService,并展示了其在不同場景中的應(yīng)用:數(shù)據(jù)庫查詢結(jié)果緩存、API 響應(yīng)緩存、復(fù)雜計(jì)算結(jié)果緩存。這種通用的緩存服務(wù)設(shè)計(jì),可以顯著提高應(yīng)用的性能和響應(yīng)速度,減少對(duì)外部資源(如數(shù)據(jù)庫、外部 API)的頻繁訪問,從而優(yōu)化用戶體驗(yàn)。
通過上述示例,開發(fā)者可以更輕松地在項(xiàng)目中集成和管理緩存,提高應(yīng)用的整體性能和可靠性。希望本文對(duì)你有所幫助,能夠在實(shí)際項(xiàng)目中充分利用緩存技術(shù)。