基于DDD的.NET开发框架 - ABP缓存Caching实现
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();
}); //为特定的缓存配置有效期
Configuration.Caching.Configure("MyCache", cache =>
{
cache.DefaultSlidingExpireTime = TimeSpan.FromHours();
});
把上面代码放到模块中的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=""/>
注:在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();
}
}
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();
} 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();
} public abstract void Remove(string key); public virtual Task RemoveAsync(string key)
{
Remove(key);
return Task.FromResult();
} public abstract void Clear(); public virtual Task ClearAsync()
{
Clear();
return Task.FromResult();
} public virtual void Dispose()
{ }
}
}
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());
}
}
}
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;
}
}
}
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介绍
前言 为了能够帮助.Net开发者开拓视野,更好的把最新的技术应用到工作中,我在3月底受邀到如鹏网.net训练营直播间为各位学弟学妹们进行ABP框架的直播分享.同时为了让更多的.NET开发者了解ABP框 ...
- 基于DDD的.NET开发框架 - ABP领域服务
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 基于DDD的.NET开发框架 - ABP的Entity设计思想
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 基于DDD的.NET开发框架 - ABP依赖注入
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 基于DDD的.NET开发框架 - ABP初探
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 基于DDD的.NET开发框架 - ABP分层设计
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
- 基于DDD的.NET开发框架ABP实例,多租户 (Sass)应用程序,采用.NET MVC, Angularjs, EntityFramework-介绍
介绍 基于ABPZERO的多租户 (Sass)应用程序,采用ASP.NET MVC, Angularjs-介绍 ASP.NET Boilerplate作为应用程序框架. ASP.NET MVC和ASP ...
- 基于DDD的.NET开发框架ABP实例,多租户 (Saas)应用程序,采用.NET MVC, Angularjs, EntityFramework-介绍
介绍 基于ABPZERO的多租户 (Saas)应用程序,采用ASP.NET MVC, Angularjs-介绍 ASP.NET Boilerplate作为应用程序框架. ASP.NET MVC和ASP ...
- 基于DDD的.NET开发框架 - ABP工作单元(Unit of Work)
返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...
随机推荐
- Java Se :Map 系列
之前对Java Se中的线性表作了简单的说明.这一篇就来看看Map. Map系列的类,并不是说所有的类都继承了Map接口,而是说他们的元素都是以<Key, Value>形式设计的. Dic ...
- mysql5.6主从参数详解
mysql5.6的主从相当的不错,增加了不少参数,提升了主从同步的安全和效率,以下是mysql5.6主从参数详解. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ...
- PHP 取前一天或后一天、一个月时间
//获得当前时间 //date()格式化时间返回String类型. date("Y-m-d H:i:s") $current_date = date(’Y-m-d’ ...
- mysql 触发器的创建 修改 删除
//做一个简单的练习,创建一个简单的触发器 完成添加文章的时候,自动加上时间,默认作者 为 ‘日记本的回忆‘ show columns from test; //查看表结构 //查看已存在触发器 sh ...
- MaskedTextBox的聚焦和光标位置
遇到一个关于MaskedTextBox聚焦和光标居左的问题. ①. 先说说聚焦: 起初给MaskedTextBox设置了Focus(),运行之后,并没有作用. 然后又给Form添加了Load事件,又设 ...
- javaScript事件(五)事件类型之鼠标事件
一.事件 二.事件流 以上内容见:javaScript事件(一)事件流 三.事件处理程序 四.IE事件处理程序 以上内容见javaScript事件(二)事件处理程序 五.事件对象 以上内容见javaS ...
- Caffe fine-tuning 微调网络
转载请注明出处,楼燚(yì)航的blog,http://www.cnblogs.com/louyihang-loves-baiyan/ 目前呢,caffe,theano,torch是当下比较流行的De ...
- Codeforces Round #285 (Div.1 B & Div.2 D) Misha and Permutations Summation --二分+树状数组
题意:给出两个排列,求出每个排列在全排列的排行,相加,模上n!(全排列个数)得出一个数k,求出排行为k的排列. 解法:首先要得出定位方法,即知道某个排列是第几个排列.比如 (0, 1, 2), (0, ...
- 校园导游系统(C++实现,VC6.0编译,使用EasyX图形库)
运行效果: 说明: 由于当年还不会使用多线程,所以很多获取用户点击的地方都是使用循环实现的...CPU占用率会比较高. 代码: //校园导游系统.cpp 1 #include <graphics ...
- XBOX ONE游戏开发之登陆服务器(一)
XBOX ONE游戏开发之登陆服务器(一) XBOX LIVE是微软自已的认证服务器, 当我们开发游戏时,如果是联网游戏,需要自已架设单点登陆(SSO)服务器 这个需要微软提供Relying Part ...