zoukankan      html  css  js  c++  java
  • 基于DDD的.NET开发框架

    返回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:封装了cachename和对该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:重写了CacheManagerBaseCreateCacheImplementation方法,该方法用于创建真实的Icache对象。 具体到AbpMemoryCacheManager就是创建AbpMemoryCache

  • 相关阅读:
    webpack配置之代码优化
    react组件生命周期
    javascript记住用户名和登录密码
    ajax异步请求原理和过程
    深入理解ajax系列第五篇——进度事件
    ajax多次请求,只执行最后一次的方法
    CentOS6.8下MySQL MHA架构搭建笔记
    HTTP状态码
    什么是 Redis 事务?原理是什么?
    Redis 通讯协议是什么?有什么特点?
  • 原文地址:https://www.cnblogs.com/yinrq/p/5522681.html
Copyright © 2011-2022 走看看