基于DDD的.NET开发框架 - ABP缓存Caching实现
返回ABP系列
ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。
ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点,它旨在成为一个通用的WEB应用程序框架和项目模板。
ABP的官方网站:http://www.aspnetboilerplate.com
ABP官方文档:http://www.aspnetboilerplate.com/Pages/Documents
Github上的开源项目:https://github.com/aspnetboilerplate
一、基本概念
ABP中有两种cache的实现方式:MemroyCache 和 RedisCache,两者都继承至ICache接口(准确说是CacheBase抽象类)。ABP核心模块封装了MemroyCache 来实现ABP中的默认缓存功能。 Abp.RedisCache这个模块封装RedisCache来实现缓存(通过StackExchange.Redis这个类库访问redis)。
1、ICacheManager:
缓存的主要接口是ICacheManager。注入该接口并使用该接口获得一个缓存对象:
public class TestAppService : ApplicationService{private readonly ICacheManager _cacheManager;public TestAppService(ICacheManager cacheManager){_cacheManager = cacheManager;}public Item GetItem(int id){//从缓存中获取return _cacheManager.GetCache("MyCache").Get(id.ToString(), () => GetFromDatabase(id)) as Item;}public Item GetFromDatabase(int id){//... 从数据库中检索 }}
不要在构造函数中使用GetCache方法。如果你的类是transient(每次使用都会创建)的,那么这可能会释放缓存,因为第二次创建类的对象时,会再次调用构造函数,之前的第一次的缓存可能会被释放。
2、ICache:
ICacheManager.GetCache方法返回一个ICache。缓存对象是单例的,第一次请求时会创建缓存,以后都是返回相同的缓存对象。因此,我们可以在不同的类(客户端)中共享具有相同名字的相同缓存。
在样例代码中,我们看到了ICache.Get方法的简单使用。它有两个参数:
key:缓存中一个条目的唯一字符串键。
factory:没有找到给定key的缓存条目时调用的action。工厂方法应该创建并返回实际的条目。如果给定的key在缓存中找到了,那么不会调用该action。ICache接口也有像GetOrDefault,Set,Remove,Clear的方法。同时,这些方法也有async版本。
3、ITypedCache:
ICache接口的key为string类型,value为object类型。ITypeCache是ICache的包装器,提供类型安全、泛型的cache。为了将ICache转为ITypedCache,我们可以使用AsTyped扩展方法,如下所示:
ITypedCache<int, Item> myCache = _cacheManager.GetCache("MyCache").AsTyped<int, Item>();
这样,就不需要转换直接可以使用Get方法。
4、配置
缓存的默认有效期是60min。如果你在60min内都没有使用缓存中的元素,那么它会自动从缓存中移除。对于所有的缓存或者特定的某个缓存,你都可以配置有效期。
//为所有缓存配置有效期 Configuration.Caching.ConfigureAll(cache => {cache.DefaultSlidingExpireTime = TimeSpan.FromHours(2); });//为特定的缓存配置有效期 Configuration.Caching.Configure("MyCache", cache => {cache.DefaultSlidingExpireTime = TimeSpan.FromHours(8); });
把上面代码放到模块中的PreInitialize方法中。有了这样的配置,MyCache会有8小时的有效期,而其他cache会有2小时有效期。
cache只要首次创建(第一次请求时),就会调用配置的action。配置并不只局限于DefaultSlidingExpireTime(默认有效期),因为cache对象是一个ICache,你可以使用它的属性和方法自由地配置并初始化。
5、Redis缓存集成
ABP默认缓存管理是使用内存缓存。可以使用Redis作为分布式缓存服务器。
首先,需要安装abp.rediscache NuGet包添加到您的应用程序(可以把它安装到您的Web项目)。然后添加一个AbpRedisCacheModule依赖,在你的模块PreInitialize配置使用:
//...引入命名空间 using Abp.Runtime.Caching.Redis; namespace MyProject.AbpZeroTemplate.Web {[DependsOn(//...模块依赖typeof(AbpRedisCacheModule))]public class MyProjectWebModule : AbpModule{public override void PreInitialize(){//...配置Configuration.Caching.UseRedis();}//...other code } }
Abp.RedisCache默认使用“localhost”作为默认连接字符串。您可以将连接字符串添加到配置文件中以重写它:
<add name="Abp.Redis.Cache" connectionString="localhost"/>
还可以添加设置里设置Redis数据库ID。例如:
<add key="Abp.Redis.Cache.DatabaseId" value="2"/>
注:在ABP中使用Redis缓存请先安装Redis服务器,更多redis配置信息
二、ABP缓存Caching源代码分析
目录结构:
类图:
ICache:缓存的接口
using System; using System.Threading.Tasks;namespace Abp.Runtime.Caching {/// <summary>/// Defines a cache that can be store and get items by keys./// </summary>public interface ICache : IDisposable{/// <summary>/// Unique name of the cache./// </summary>string Name { get; }/// <summary>/// Default sliding expire time of cache items./// Default value: 60 minutes. Can be changed by configuration./// </summary>TimeSpan DefaultSlidingExpireTime { get; set; }/// <summary>/// Gets an item from the cache./// </summary>/// <param name="key">Key</param>/// <param name="factory">Factory method to create cache item if not exists</param>/// <returns>Cached item</returns>object Get(string key, Func<string, object> factory);/// <summary>/// Gets an item from the cache./// </summary>/// <param name="key">Key</param>/// <param name="factory">Factory method to create cache item if not exists</param>/// <returns>Cached item</returns>Task<object> GetAsync(string key, Func<string, Task<object>> factory);/// <summary>/// Gets an item from the cache or null if not found./// </summary>/// <param name="key">Key</param>/// <returns>Cached item or null if not found</returns>object GetOrDefault(string key);/// <summary>/// Gets an item from the cache or null if not found./// </summary>/// <param name="key">Key</param>/// <returns>Cached item or null if not found</returns>Task<object> GetOrDefaultAsync(string key);/// <summary>/// Saves/Overrides an item in the cache by a key./// </summary>/// <param name="key">Key</param>/// <param name="value">Value</param>/// <param name="slidingExpireTime">Sliding expire time</param>void Set(string key, object value, TimeSpan? slidingExpireTime = null);/// <summary>/// Saves/Overrides an item in the cache by a key./// </summary>/// <param name="key">Key</param>/// <param name="value">Value</param>/// <param name="slidingExpireTime">Sliding expire time</param>Task SetAsync(string key, object value, TimeSpan? slidingExpireTime = null);/// <summary>/// Removes a cache item by it's key./// </summary>/// <param name="key">Key</param>void Remove(string key);/// <summary>/// Removes a cache item by it's key (does nothing if given key does not exists in the cache)./// </summary>/// <param name="key">Key</param>Task RemoveAsync(string key);/// <summary>/// Clears all items in this cache./// </summary>void Clear();/// <summary>/// Clears all items in this cache./// </summary> Task ClearAsync();} }
View Code
CacheBase:缓存基类
using System; using System.Threading.Tasks; using Nito.AsyncEx;namespace Abp.Runtime.Caching {/// <summary>/// Base class for caches./// It's used to simplify implementing <see cref="ICache"/>./// </summary>public abstract class CacheBase : ICache{public string Name { get; private set; }public TimeSpan DefaultSlidingExpireTime { get; set; }protected readonly object SyncObj = new object();private readonly AsyncLock _asyncLock = new AsyncLock();/// <summary>/// Constructor./// </summary>/// <param name="name"></param>protected CacheBase(string name){Name = name;DefaultSlidingExpireTime = TimeSpan.FromHours(1);}public virtual object Get(string key, Func<string, object> factory){var cacheKey = key;var item = GetOrDefault(key);if (item == null){lock (SyncObj){item = GetOrDefault(key);if (item == null){item = factory(key);if (item == null){return null;}Set(cacheKey, item);}}}return item;}public virtual async Task<object> GetAsync(string key, Func<string, Task<object>> factory){var cacheKey = key;var item = await GetOrDefaultAsync(key);if (item == null){using (await _asyncLock.LockAsync()){item = await GetOrDefaultAsync(key);if (item == null){item = await factory(key);if (item == null){return null;}await SetAsync(cacheKey, item);}}}return item;}public abstract object GetOrDefault(string key);public virtual Task<object> GetOrDefaultAsync(string key){return Task.FromResult(GetOrDefault(key));}public abstract void Set(string key, object value, TimeSpan? slidingExpireTime = null);public virtual Task SetAsync(string key, object value, TimeSpan? slidingExpireTime = null){Set(key, value, slidingExpireTime);return Task.FromResult(0);}public abstract void Remove(string key);public virtual Task RemoveAsync(string key){Remove(key);return Task.FromResult(0);}public abstract void Clear();public virtual Task ClearAsync(){Clear();return Task.FromResult(0);}public virtual void Dispose(){}} }
View Code
ITypedCache/TypedCacheWrapper: 支持泛型key和value的缓存接口与实现,其内部通过封装ICache实例和CacheExtension定义的对ICache的扩展方法来是实现泛型版本的Icache.
using System; using System.Threading.Tasks;namespace Abp.Runtime.Caching {/// <summary>/// Implements <see cref="ITypedCache{TKey,TValue}"/> to wrap a <see cref="ICache"/>./// </summary>/// <typeparam name="TKey"></typeparam>/// <typeparam name="TValue"></typeparam>public class TypedCacheWrapper<TKey, TValue> : ITypedCache<TKey, TValue>{public string Name{get { return InternalCache.Name; }}public TimeSpan DefaultSlidingExpireTime{get { return InternalCache.DefaultSlidingExpireTime; }set { InternalCache.DefaultSlidingExpireTime = value; }}public ICache InternalCache { get; private set; }/// <summary>/// Creates a new <see cref="TypedCacheWrapper{TKey,TValue}"/> object./// </summary>/// <param name="internalCache">The actual internal cache</param>public TypedCacheWrapper(ICache internalCache){InternalCache = internalCache;}public void Dispose(){InternalCache.Dispose();}public void Clear(){InternalCache.Clear();}public Task ClearAsync(){return InternalCache.ClearAsync();}public TValue Get(TKey key, Func<TKey, TValue> factory){return InternalCache.Get(key, factory);}public Task<TValue> GetAsync(TKey key, Func<TKey, Task<TValue>> factory){return InternalCache.GetAsync(key, factory);}public TValue GetOrDefault(TKey key){return InternalCache.GetOrDefault<TKey, TValue>(key);}public Task<TValue> GetOrDefaultAsync(TKey key){return InternalCache.GetOrDefaultAsync<TKey, TValue>(key);}public void Set(TKey key, TValue value, TimeSpan? slidingExpireTime = null){InternalCache.Set(key.ToString(), value, slidingExpireTime);}public Task SetAsync(TKey key, TValue value, TimeSpan? slidingExpireTime = null){return InternalCache.SetAsync(key.ToString(), value, slidingExpireTime);}public void Remove(TKey key){InternalCache.Remove(key.ToString());}public Task RemoveAsync(TKey key){return InternalCache.RemoveAsync(key.ToString());}} }
View Code
CacheExtension: 定义了ICache的扩展方法. 最关键的是如下两个支持泛型的方法:GetOrDefault和GetOrDefaultAsync。如下,内部调用ICache实例的相应方法并通过类型转换。
using System; using System.Threading.Tasks;namespace Abp.Runtime.Caching {/// <summary>/// Extension methods for <see cref="ICache"/>./// </summary>public static class CacheExtensions{public static object Get(this ICache cache, string key, Func<object> factory){return cache.Get(key, k => factory());}public static Task<object> GetAsync(this ICache cache, string key, Func<Task<object>> factory){return cache.GetAsync(key, k => factory());}public static ITypedCache<TKey, TValue> AsTyped<TKey, TValue>(this ICache cache){return new TypedCacheWrapper<TKey, TValue>(cache);}public static TValue Get<TKey, TValue>(this ICache cache, TKey key, Func<TKey, TValue> factory){return (TValue)cache.Get(key.ToString(), (k) => (object)factory(key));}public static TValue Get<TKey, TValue>(this ICache cache, TKey key, Func<TValue> factory){return cache.Get(key, (k) => factory());}public static async Task<TValue> GetAsync<TKey, TValue>(this ICache cache, TKey key, Func<TKey, Task<TValue>> factory){var value = await cache.GetAsync(key.ToString(), async (keyAsString) =>{var v = await factory(key);return (object)v;});return (TValue)value;}public static Task<TValue> GetAsync<TKey, TValue>(this ICache cache, TKey key, Func<Task<TValue>> factory){return cache.GetAsync(key, (k) => factory());}public static TValue GetOrDefault<TKey, TValue>(this ICache cache, TKey key){var value = cache.GetOrDefault(key.ToString());if (value == null){return default(TValue);}return (TValue) value;}public static async Task<TValue> GetOrDefaultAsync<TKey, TValue>(this ICache cache, TKey key){var value = await cache.GetOrDefaultAsync(key.ToString());if (value == null){return default(TValue);}return (TValue)value;}} }
View Code
AbpCacheNames:定义了四个cache的key常量,这几个cache是供ABP框架使用的
namespace Abp.Runtime.Caching {/// <summary>/// Names of standard caches used in ABP./// </summary>public static class AbpCacheNames{/// <summary>/// Application settings cache: AbpApplicationSettingsCache./// </summary>public const string ApplicationSettings = "AbpApplicationSettingsCache";/// <summary>/// Tenant settings cache: AbpTenantSettingsCache./// </summary>public const string TenantSettings = "AbpTenantSettingsCache";/// <summary>/// User settings cache: AbpUserSettingsCache./// </summary>public const string UserSettings = "AbpUserSettingsCache";/// <summary>/// Localization scripts cache: AbpLocalizationScripts./// </summary>public const string LocalizationScripts = "AbpLocalizationScripts";} }
AbpCacheNames
ICacheConfigurator/CacheConfigurator:封装了cache的name和对该cahce的初始化方法,通过初始化方法可以完成对cache的配置
using System;namespace Abp.Runtime.Caching.Configuration {internal class CacheConfigurator : ICacheConfigurator{public string CacheName { get; private set; }public Action<ICache> InitAction { get; private set; }public CacheConfigurator(Action<ICache> initAction){InitAction = initAction;}public CacheConfigurator(string cacheName, Action<ICache> initAction){CacheName = cacheName;InitAction = initAction;}} }
CacheConfigurator
ICachingConfiguration/CachingConfiguration: 该接口提供完成cache的配置的方法。具体是通过封装了一个ICacheConfigurator集合,并调用CacheConfigurator的InitAction来配置cache。
using System; using System.Collections.Generic; using System.Collections.Immutable; using Abp.Configuration.Startup;namespace Abp.Runtime.Caching.Configuration {internal class CachingConfiguration : ICachingConfiguration{public IAbpStartupConfiguration AbpConfiguration { get; private set; }public IReadOnlyList<ICacheConfigurator> Configurators{get { return _configurators.ToImmutableList(); }}private readonly List<ICacheConfigurator> _configurators;public CachingConfiguration(IAbpStartupConfiguration abpConfiguration){AbpConfiguration = abpConfiguration;_configurators = new List<ICacheConfigurator>();}public void ConfigureAll(Action<ICache> initAction){_configurators.Add(new CacheConfigurator(initAction));}public void Configure(string cacheName, Action<ICache> initAction){_configurators.Add(new CacheConfigurator(cacheName, initAction));}} }
CachingConfiguration
ICacheManager/CacheManagerBase: 该接口和实现用于生成,配置以及销毁ICache实例。具体是通过ICachingConfiguration对象来初始化cache, 并通过ConcurrentDictionary<string, ICache>来存放和管理cache.
AbpMemoryCache:通过MemoryCache来实现Icache.
AbpMemoryCacheManager:重写了CacheManagerBase的CreateCacheImplementation方法,该方法用于创建真实的Icache对象。 具体到AbpMemoryCacheManager就是创建AbpMemoryCache。
基于DDD的.NET开发框架 - ABP缓存Caching实现相关推荐
- 基于DDD的.NET开发框架 - ABP领域服务
返回ABP系列 ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称. ASP.NET Boilerplate是一个用最佳实践和流行 ...
- 基于DDD的.NET开发框架 - ABP初探
全称:Asp.Net Boilerplate 官网地址:https://aspnetboilerplate.com/ GitHub地址:https://github.com/aspnetboilerp ...
- 基于DDD的.NET开发框架 - ABP依赖注入
返回ABP系列 ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称. ASP.NET Boilerplate是一个用最佳实践和流行 ...
- 基于DDD的.NET开发框架 - ABP模块设计
返回ABP系列 ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称. ASP.NET Boilerplate是一个用最佳实践和流行 ...
- 基于DDD的.NET开发框架 - ABP工作单元(Unit of Work)
返回ABP系列 ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称. ASP.NET Boilerplate是一个用最佳实践和流行 ...
- 基于DDD的现代ASP.NET开发框架--ABP系列之1、ABP总体介绍
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之1.ABP总体介绍 ABP是"ASP.NET Boilerplate Project (ASP.NET样 ...
- 基于DDD的现代ASP.NET开发框架
基于DDD的现代ASP.NET开发框架--ABP系列之1.目录和总体介绍 时间 2015-05-25 17:49:00 博客园-原创精华区 原文 http://www.cnblogs.com/mi ...
- 基于实战开发垂直搜索引擎_基于DDD的微服务设计和开发实战
作者:欧创新.邓頔.文艺 你是否还在为微服务应该拆多小而争论不休?到底如何才能设计出收放自如的微服务?怎样才能保证业务领域模型与代码模型的一致性?或许本文能帮你找到答案. 本文是基于 DDD 的微服务 ...
- ddd 访问权限_基于DDD的微服务设计和开发实战
作者:欧创新.邓頔.文艺 你是否还在为微服务应该拆多小而争论不休?到底如何才能设计出收放自如的微服务?怎样才能保证业务领域模型与代码模型的一致性?或许本文能帮你找到答案. 本文是基于 DDD 的微服务 ...
最新文章
- JS 正则匹配字符串
- 覆盖索引与联合索引_什么是覆盖索引?
- Spark Executor内幕
- Android 应用安全性改进: 全面助力打造 零漏洞 应用
- tf 矩阵行和列交换_TF-搞不懂的TF矩阵加法
- LeetCode 1071. 字符串的最大公因子(字符串的最大公约数)
- OSTimeDlyResume()--恢复一个延迟任务(取消任务延时)
- 1.7编程基础之字符串
- InfluxDB Java入门
- 使用http连接到Analysis services
- 关于interface
- 打印时总跟出一页计算机主的纸,如何将多页PDF打印在一张纸上
- 在windows server 2008 R2上安装SVN的时候,提示安装kb2999226
- Android 属性动画
- 淘宝接口 http://ip.taobao.com/service/getIpInfo.php?ip=myip 获取不到手机ip地址
- 安装VM、CentOS和网络配置
- 互联网巨头的人工智能野心,你看懂了吗?
- 百度地图汽车物流数智化应用方案,推进整车运输行业全链条数字化发展
- WIN10环境下配置hadoop+spark并运行实例的教程
- Java中什么是多态?多态的优势和劣势是什么?
热门文章
- 解决fstream不能打开带有中文路径文件的问题
- JRuby大捷:ThoughtWorks宣布Mingle发布在即
- 对typedef void (*sighandler_t)(int)的理解(声明了一种类型:sighandler_t)
- 温故而知新 Vue 原来也有this.$forceUpdate();
- java json转map
- C# 时间+三位随机数
- 使用DOM操作样式表
- css样式中关于li的横向排列
- 内核代号101 — 动手写自己的内核
- 算法:多数元素,多种解法