• ABP官方文档翻译 2.3 缓存


    缓存

    介绍

      ABP为缓存提供了一个抽象接口,它内部使用这个缓存抽象。默认使用MemoryCache实现,但可以换用其他的缓存提供者实现。例如Abp.RedisCache包实现了在Redis中实现缓存。(参见下面的“Redis缓存集成”部分)。

    ICacheManager

      缓存的主要接口是ICacheManager。我们可以注入并使用它获取缓存。示例:

    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
        }
    }

      在这个例子中,我们注入了ICacheManager并得到了一个名为MyCache的缓存。缓存名称是大小写敏感的,意味着“MyCache”和“MYCACHE”是不同的缓存。

      警告:GetCache方法

      不要在构造函数中使用GetCache。如果类不是单例的话,缓存可能会被释放。

    ICache

      ICacheManager.GetCaChe方法返回ICache。缓存是单例的(每个缓存名称)。首次需要的时候创建,然后每次都返回同样的缓存对象。所以,我们可以在不同的类(客户端)使用相同的名字共享同样的缓存。

      在实例代码中,我们看到了ICache.Get方法的简单使用。它有两个参数:

    • Key:缓存中一个项的唯一字符串键。
    • factory:如果指定的键没有项目时调用。工厂方法应该创建并返回真实的项。如果指定的键在缓存中存在的话就不会调用这个方法。

      ICache接口也有如GetOrDefault,Set,Remove和Clear这样的方法。同样也有这些方法的异步版本。

    ITypedCache

    ICache接口使用string类型作为键,object作为值。ITypeCache是ICache的包装器,用来提供类型安全、泛型的缓存。我们可以使用GetCache扩展方法获得ITypedCache:

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

      我们也可以使用AsTyped扩展方法将一个已存在的ICache实例转换为ITypedCache。

     配置

      默认缓存超时时间为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);
    });

      这段代码需要放在模块的PreInitialize方法中。使用这段代码,MyCache将在8个小时后过期,其他缓存将在2小时后过期。

      配置动作在缓存第一建立(首次请求)时调用。配置并不仅限于DefaultSlidingExpireTime,因为缓存对象为ICache,可以使用它的属性和方法自由配置和初始化。

    实体缓存

      虽然ABP缓存系统是通用的,如果想缓存实体,可以使用EntityCache基类。如果我们通过Ids获取到实体并且想通过Id缓存实体而不是经常从数据库查询,我们就可以使用这个基类。假如我们有一个Person实体,如下:

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

      假如我们知道Id并想以此获取people的Name。首先,我们创建一个类储存缓存项:

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

      不应该直接在缓存中直接存储实体,因为缓存可能需要序列化缓存的对象,实体可能不能被序列化(尤其是有导航属性时)。这就是我们在缓存中定义一个简单类(像DTO)存储数据的原因。因为我们想使用AutoMapper自动将Person实体转换为PersonCahcedItem,所以在类上添加了AutoMapFrom特性。如果我们不使用AutoMapper,那需要重写EntityCache类的MapToCacheItem方法来实现自动转换或映射。

      我们希望定义一个缓存类的接口,虽然这不是必须的:

    public interface IPersonCache : IEntityCache<PersonCacheItem>
    {
    
    }

    最后,我们可以创建缓存类缓存Person实体了:

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

      就这样,我们的person缓存可以使用了。Cache类可以是临时的(如本例)也可以是单例的。这并不意味着缓存数据是临时的。在应用中,它总是全局缓存并且访问是线程安全的。

      现在,不管何时我们需要person的Name时,我们可以通过person`s Id从缓存中得到。使用Person缓存的示例类如下:

    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;
        }
    }

      我们简单的注入IPersonCache,获取缓存项和Name属性。

    实体缓存如何工作

    • 在首次调用时,从仓储(从数据库)获取实体。然后在之后的调用从缓存获取。
    • 如果实体被更新或删除,会自动使缓存实体无效。然后,在下次调用时会从数据库中重新获取。
    • 使用IObjectMapper映射实体和缓存项。IObjectMapper通过AutoMapper模块实现。所以,如果使用的话需要AutoMapper模块。可以重写MapToCacheItem方法手动映射实体和缓存项。
    • 使用缓存类FullName作为缓存名称。可以通过向基类构造函数传递缓存名称来改变它。
    • 线程安全。

      如果需要更复杂的缓存,可以扩展EntityCache或创建自己的解决方案。

    Redis缓存集成

      默认的缓存管理器使用内存缓存。所以,如果有多于一个并发网络服务器运行同样的应用,将会成为一个问题。在这种情况下,需要使用一个分布式/中央缓存服务器。可以很简单的使用Redis作为缓存服务器。

      首先,需要在应用(例如,可以安装在Web工程)中安装Abp.RedisCache nuget包。然后,需要需要给AbpRedisCacheModule添加DependsOn特性,并在模块的PreInitialize方法中调用UseRedis方法,如下所示:

    //...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包使用“localhost”作为默认连接字符串。可以在配置文件中添加链接字符串以覆盖默认值。例如:

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

      也可以添加配置到appSettings中设置Redis的数据库id。例如:

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

      在同一个服务器中,不同的数据库ids用来创建不同的关键空间(隔离缓存)。

      UseRedis方法有个重载,可以用来直接设置选项值(在配置文件中重写值)。

      参见Redis文档获取关于Redis及配置的更多信息。

      注意:Redis服务器应该安装并运行,以便在ABP中使用Redis缓存。

    返回主目录

  • 相关阅读:
    面试笔试
    scala(9) Monad
    scala (8) 模糊匹配
    scala (7) Set and Tuple
    scala (6) Map
    scala (5) 可变序列和不可变序列
    scala (4) 可变数组和不可变数组
    scala (3) Function 和 Method
    scala (2) while 和变量
    scala (1) for 循环
  • 原文地址:https://www.cnblogs.com/xajh/p/6771406.html
Copyright © 2020-2023  润新知