ABP框架系列之十五:(Caching-缓存)

时间:2024-01-02 15:37:08

Introduction

ASP.NET Boilerplate provides an abstraction for caching. It internally uses this cache abstraction. While default implementation uses MemoryCache, it can be implemented and changable for any other caching provider.Abp.RedisCache package implements cache in Redis for instance (see "Redis Cache Integration" section below).

ASP.NET提供了一个抽象的模板缓存。它在内部使用这个缓存抽象。而使用memorycache默认实现,它可以实现和改变其他任何缓存provider.abp.rediscache包实现比如Redis缓存(见下面的Redis的缓存集成”部分)。

ICacheManager

Main interface for caching is ICacheManager. We can inject it and use it to get a cache. Example:

public class TestAppService : ApplicationService
{
private readonly ICacheManager _cacheManager; public TestAppService(ICacheManager cacheManager)
{
_cacheManager = cacheManager;
} public Item GetItem(int id)
{
//Try to get from cache
return _cacheManager
.GetCache("MyCache")
.Get(id.ToString(), () => GetFromDatabase(id)) as Item;
} public Item GetFromDatabase(int id)
{
//... retrieve item from database
}
}

In this sample, we're injecting ICacheManager and getting a cache named MyCache. Cache names are case sensitive, than means "MyCache" and "MYCACHE" are different caches.

在本示例中,我们将icachemanager获得缓存命名mycache。缓存的名称是区分大小写的,不是指“mycache”和“mycache”不同的高速缓存。

WARNING: GetCache Method

Do not use GetCache method in your constructor. This may dispose the Cache if your class is not singleton.

不要在构造函数使用getcache方法。如果类不是单例,则可以处理缓存。

ICache

ICacheManager.GetCache method returns an ICache. A cache is singleton (per cache name). It is created first time it's requested, then returns always the same cache object. So, we can share same cache with same name in different classes (clients).

In the sample code, we see simple usage of ICache.Get method. It has two arguments:

  • key: A string unique key of an item in the cache.
  • factory: An action which is called if there is no item with the given key. Factory method should create and return the actual item. This is not called if given key has present in the cache.

ICache interface also has methods like GetOrDefault, Set, Remove and Clear. There are also async versions of all methods.

icachemanager.getcache方法返回一个高速缓冲存储器。缓存是单例(每个缓存名称)。它是第一次被请求创建的,然后返回相同的缓存对象。因此,我们可以在不同的类(客户端)共享相同的缓存相同的名称。

在示例代码中,我们看到的内容简单的用法。获得方法。它有两个论点:

关键字:缓存中项的字符串唯一键。
工厂:如果没有带给定键的项,则调用该操作。工厂方法应该创建并返回实际项目。如果给定的密钥存在于缓存中,则不调用它。
高速缓冲存储器接口也有方法,像GetOrDefault一样,设置,删除和清除。也有各种方法的异步版本。

ITypedCache

ICache interface works string as key and object as value. ITypedCache is a wrapper to ICache to provide type safe, generic cache. We can use generic GetCache extension method to get an ITypedCache:

ICache接口string是key,object是value,ITypedCache 包装了ICache提供类型安全,通用的缓存。我们可以使用通用的getcache扩展方法来获得一个itypedcache

ITypedCache<int, Item> myCache = _cacheManager.GetCache<int, Item>("MyCache");

Also, we can use AsTyped extension method to convert an existing ICache instance to ITypedCache.

同时,我们可以用astyped扩展方法将现有的指令缓存实例itypedcache。

Configuration

Default cache expire time is 60 minutes. It's sliding. So, if you don't use an item in the cache for 60 minutes, it's automatically removed from the cache. You can configure it for all caches or for a specific cache.

默认缓存过期时间为60分钟。它在滑动。因此,如果不在缓存中使用一个项目60分钟,它会自动从缓存中移除。您可以为所有缓存或特定的缓存配置它。

//Configuration for all caches
Configuration.Caching.ConfigureAll(cache =>
{
cache.DefaultSlidingExpireTime = TimeSpan.FromHours(2);
}); //Configuration for a specific cache
Configuration.Caching.Configure("MyCache", cache =>
{
cache.DefaultSlidingExpireTime = TimeSpan.FromHours(8);
});

This code should be placed PreInitializemethod of your module. With such a code, MyCache will have 8 hours expire time while all other caches will have 2 hours.

Your configuration action is called once cache is first created (on first request). Configuration is not restricted to DefaultSlidingExpireTime only, since cache object is an ICache, you can use it's properties and methods freely configure and initialize it.

这个代码应该放在分发你的模块的方法。这样的代码,mycache将有8小时的期限而其他缓存将有2小时。

一旦第一次创建缓存(第一次请求),您的配置操作就被调用了。配置不限于defaultslidingexpiretime而已,因为缓存的对象是一个内容,你可以使用它的属性和方法可*配置和初始化。

Entity Caching

While ASP.NET Boilerplate's cache system is general purpose, there is an EntityCache base class that can help you if you want to cache entities. We can use this base class if we get entities by their Ids and we want to cache them by Id to not query from database frequently. Assume that we have a Person entity like that:

而ASP.NET的样板文件的缓存系统是通用的,有一个entitycache基类,可如果你想缓存实体的帮助你。我们可以使用这个基类,如果我们通过它们的ID获取实体,我们希望通过ID缓存它们,而不是频繁地从数据库中查询。假设我们有这样的person实体:

public class Person : Entity
{
public string Name { get; set; } public int Age { get; set; }
}

And assume that we frequently want to get Name of people while we know their Id. First, we should create a class to store cache items:

假设我们经常想知道人们的名字,但首先我们应该创建一个类来存储缓存项:

[AutoMapFrom(typeof(Person))]
public class PersonCacheItem
{
public string Name { get; set; }
}

We should not directly store entities in the cache since caching may need to serialize cached objects and entities may not be serialized (especially if they have navigation properties). That's why we defined a simple (DTOlike) class to store data in the cache. We added AutoMapFrom attribute since we want to use AutoMapper to convert Person entities to PersonCacheItem objects automatically. If we don't use AutoMapper, we should override MapToCacheItem method of EntityCache class to manually convert/map it.

While it's not required, we may want to define an interface for our cache class:

我们不应该直接从缓存中缓存实体店可能需要序列化缓存对象和实体不被序列化(特别是如果他们有导航性能)。这就是为什么我们定义了一个简单的(dtolike)类缓存中存储数据。我们增加了automapfrom属性因为我们想使用AutoMapper将个人实体personcacheitem对象自动。如果我们不使用AutoMapper,我们应该重写entitycache类maptocacheitem方法手动转换/图。

虽然不是必需的,但我们可能希望为缓存类定义一个接口:

public interface IPersonCache : IEntityCache<PersonCacheItem>
{ }

Finally, we can create the cache class to cache Person entities:

public class PersonCache : EntityCache<Person, PersonCacheItem>, IPersonCache, ITransientDependency
{
public PersonCache(ICacheManager cacheManager, IRepository<Person> repository)
: base(cacheManager, repository)
{ }
}

That's all. Our person cache is ready to use. Cache class can be transient (as in this example) or singleton. This does not mean the cached data is transient. It's always cached globally and accessed thread-safe in your application.

这就是全部.我们的缓存已经可以使用了。缓存类可以是临时的(如本例中)或单例。这并不意味着缓存的数据是暂时的。它总是在全局缓存,并在应用程序中访问线程安全。

Now, whenever we need Name of a person, we can get it from cache by the person's Id. An example class that uses the Person cache:

现在,当我们需要一个人的名字时,我们可以通过使用人缓存的Id. An的示例类来从缓存中获取它:

public class MyPersonService : ITransientDependency
{
private readonly IPersonCache _personCache; public MyPersonService(IPersonCache personCache)
{
_personCache = personCache;
} public string GetPersonNameById(int id)
{
return _personCache[id].Name; //alternative: _personCache.Get(id).Name;
}
}

We simply injected IPersonCache, got the cache item and got the Name property.

How EntityCache Works

  • It gets entity from repository (from database) in first call. Then gets from cache in subsequent calls.
  • It automatically invalidates cached entity if this entity is updated or deleted. Thus, it will be retrieved from database in the next call.
  • It uses IObjectMapper to map entity to cache item. IObjectMapper is implemented by AutoMapper module. So, you need to AutoMapper module if you are using it. You can override MapToCacheItem method to manually map entity to cache item.
  • It uses cache class's FullName as cache name. You can change it by passing a cache name to the base constructor.
  • It's thread-safe.

If you need more complex caching requirements, you can extend EntityCache or create your own solution.

它在第一次调用中从存储库(从数据库)获取实体。然后在后续调用中从缓存中获取。
它会自动消除缓存的实体如果这个实体是更新或删除。因此,它将在下一次调用中从数据库中检索。
它采用iobjectmapper地图实体缓存项。iobjectmapper由AutoMapper模块实现。所以,你需要AutoMapper模块如果你使用它。你可以重写maptocacheitem方法手动地图实体缓存项。
它使用的缓存类的全名为缓存名称。您可以通过将缓存名称传递给基本构造函数来更改它。
它是线程安全的。
如果你需要更复杂的缓存要求,您可以扩展entitycache或创建您自己的解决方案。

Redis Cache Integration

Default cache manager uses in-memory caches. So, it can be a problem if you have more than one concurrent web server running the same application. In that case, you may want to a distributed/central cache server. You can use Redis as your cache server easily.

First, you need to install Abp.RedisCache nuget package to your application (you can install it to your Web project, for example). Then you need to add a DependsOn attribute for AbpRedisCacheModule and callUseRedis extension method in PreInitialize method of your module, as shown below:

默认缓存管理器用于内存缓存。因此,如果有一个以上的并发Web服务器运行相同的应用程序,这可能是个问题。在这种情况下,您可能需要一个分布式/*缓存服务器。你可以使用Redis作为你的缓存服务器很容易。

首先,你需要安装abp.rediscache NuGet包添加到您的应用程序(你可以把它安装到您的Web项目,例如)。然后你需要添加一个对AbpRedisCacheModule和calluseredis扩展方法在分发你的模块的方法取决于属性,如下图所示:

//...other namespaces
using Abp.Runtime.Caching.Redis; namespace MyProject.AbpZeroTemplate.Web
{
[DependsOn(
//...other module dependencies
typeof(AbpRedisCacheModule))]
public class MyProjectWebModule : AbpModule
{
public override void PreInitialize()
{
//...other configurations Configuration.Caching.UseRedis();
} //...other code
}
}

Abp.RedisCache package uses "localhost" as connection string as default. You can add connection string to your config file to override it. Example:

使用“localhost”abp.rediscache包你有默认的连接字符串。你可以添加到你的配置文件到连接字符串重写它。例子:

<add name="Abp.Redis.Cache" connectionString="localhost"/>

Also, you can add setting to appSettings to set database id of Redis. Example:

<add key="Abp.Redis.Cache.DatabaseId" value="2"/>

Different database ids are useful to create different key spaces (isolated caches) in same server.

UseRedis method has also an overload that takes an action to directly set option values (overrides values in the config file).

See Redis documentation for more information on Redis and it's configuration.

Note: Redis server should be installed and running to use Redis cache in ABP.

不同的数据库IDS有助于在同一服务器中创建不同的密钥空间(隔离缓存)。

useredis方法还具有过载,需要一个行动直接设置选项值(重写值在配置文件中)。

看到这样的信息,它的配置文件的使用。

注:Redis服务器必须安装并运行在ABP使用Redis缓存。