返回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();
}); //为特定的缓存配置有效期
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实现的更多相关文章

  1. 线上分享-- 基于DDD的.NET开发框架-ABP介绍

    前言 为了能够帮助.Net开发者开拓视野,更好的把最新的技术应用到工作中,我在3月底受邀到如鹏网.net训练营直播间为各位学弟学妹们进行ABP框架的直播分享.同时为了让更多的.NET开发者了解ABP框 ...

  2. 基于DDD的.NET开发框架 - ABP领域服务

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  3. 基于DDD的.NET开发框架 - ABP的Entity设计思想

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  4. 基于DDD的.NET开发框架 - ABP依赖注入

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  5. 基于DDD的.NET开发框架 - ABP初探

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  6. 基于DDD的.NET开发框架 - ABP分层设计

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

  7. 基于DDD的.NET开发框架ABP实例,多租户 (Sass)应用程序,采用.NET MVC, Angularjs, EntityFramework-介绍

    介绍 基于ABPZERO的多租户 (Sass)应用程序,采用ASP.NET MVC, Angularjs-介绍 ASP.NET Boilerplate作为应用程序框架. ASP.NET MVC和ASP ...

  8. 基于DDD的.NET开发框架ABP实例,多租户 (Saas)应用程序,采用.NET MVC, Angularjs, EntityFramework-介绍

    介绍 基于ABPZERO的多租户 (Saas)应用程序,采用ASP.NET MVC, Angularjs-介绍 ASP.NET Boilerplate作为应用程序框架. ASP.NET MVC和ASP ...

  9. 基于DDD的.NET开发框架 - ABP工作单元(Unit of Work)

    返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应 ...

随机推荐

  1. SQL Server 2014新特性——Buffer Pool扩展

    Buffer Pool扩展 Buffer Pool扩展是buffer pool 和非易失的SSD硬盘做连接.以SSD硬盘的特点来提高随机读性能. 缓冲池扩展优点 SQL Server读以随机读为主,S ...

  2. What every programmer should know about memory 笔记

    What every programmer should know about memory, Part 1(笔记) 每个程序员都应该了解的内存知识[第一部分] 2.商用硬件现状      现在硬件的 ...

  3. 0010《SQL必知必会》笔记06-表的修改与删除

    1.表的修改: 1.1 删除列:ALTER TABLE 表名 DROP COLUMN 列名 1.2 添加列:ALTER TABLE 表名 ADD(列名 数据类型) 1.3 修改列名:ALTER TAB ...

  4. PHP6连接SQLServer2005的方法

    1.修改php.ini将extension=php_mssql.dll的注释删除保存. 修改php.in将mssql.secure_connection = Off改为mssql.secure_con ...

  5. poi生成word文件

    一.简介 对于poi来说,poi可以完成对word.excel.ppt的处理.word目前有两种文件格式,一种是doc后缀.另一种是docx后缀的.2007之前的版本都是doc后缀的,这种格式poi使 ...

  6. spring管理bean

  7. db2数组、函数

    一. 数组 在db2中,创建一个数组会在functions下生成两个对象:sys类型和用户类型的Array /*创建数组*/ ) array[]; /*删除数组*/ drop type arrName ...

  8. proteus汉化

    下载地址: http://files.cnblogs.com/files/xiaobo-Linux/proteus7%E6%B1%89%E5%8C%96.zip (别的版本也应该可以汉化) 将这ARE ...

  9. .NET对象的内存布局

    每个虚拟机都有它自己的对象布局,本文我们将针对sscli源码和windbg调试器来查看不同类型的.net对象布局. 在.net虚拟机里,每个对象都需要保存这些信息: 对象的类型: 对象实例的成员属性( ...

  10. [转]C#网络编程(异步传输字符串) - Part.3

    本文转自:http://www.tracefact.net/CSharp-Programming/Network-Programming-Part3.aspx 这篇文章我们将前进一大步,使用异步的方式 ...