必须填写至少10个字的日志
nnhy 编写于 2012-07-27 18:48:21
X
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using NewLife.Log;
using NewLife.Threading;
using XCode.DataAccessLayer;

namespace XCode.Cache
{
    /// <summary>单对象缓存</summary>
    /// <remarks>
    /// 用一个值为实体的字典作为缓存(键一般就是主键),适用于单表大量互相没有关系的数据。
    /// 同时,AutoSave能够让缓存项在过期时自动保存数据,该特性特别适用于点击计数等场合。
    /// </remarks>
    /// <typeparam name="TKey">键值类型</typeparam>
    /// <typeparam name="TEntity">实体类型</typeparam>
    public class SingleEntityCache<TKey, TEntity> : CacheBase<TEntity>, ISingleEntityCache where TEntity : Entity<TEntity>, new()
    {
        #region 属性
        private Int32 _Expriod = 60;
        /// <summary>过期时间。单位是秒,默认60秒</summary>
        public Int32 Expriod { get { return _Expriod; } set { _Expriod = value; } }

        private Int32 _MaxEntity = 10000;
        /// <summary>最大实体数。默认10000</summary>
        public Int32 MaxEntity { get { return _MaxEntity; } set { _MaxEntity = value; } }

        private Boolean _AutoSave = true;
        /// <summary>缓存到期时自动保存</summary>
        public Boolean AutoSave { get { return _AutoSave; } set { _AutoSave = value; } }

        private Boolean _AllowNull;
        /// <summary>允许缓存空对象</summary>
        public Boolean AllowNull { get { return _AllowNull; } set { _AllowNull = value; } }

        private FindKeyDelegate<TKey, TEntity> _FindKeyMethod;
        /// <summary>查找数据的方法</summary>
        public FindKeyDelegate<TKey, TEntity> FindKeyMethod
        {
            get
            {
                if (_FindKeyMethod == null)
                {
                    _FindKeyMethod = key => Entity<TEntity>.FindByKey(key);

                    if (_FindKeyMethod == null) throw new ArgumentNullException("FindKeyMethod", "没有找到FindByKey方法,请先设置查找数据的方法!");
                }
                return _FindKeyMethod;
            }
            set { _FindKeyMethod = value; }
        }

        //private Boolean _Asynchronous;
        ///// <summary>异步更新</summary>
        //public Boolean Asynchronous
        //{
        //    get { return _Asynchronous; }
        //    set { _Asynchronous = value; }
        //}
        #endregion

        #region 构造
        TimerX timer = null;
        /// <summary>实例化一个实体缓存</summary>
        public SingleEntityCache()
        {
            timer = new TimerX(d => Check(), null, Expriod * 1000, Expriod * 1000);
        }

        /// <summary>定期检查实体,如果过期,则触发保存</summary>
        void Check()
        {
            CacheItem[] cs = null;
            if (Entities.Count <= 0) return;
            lock (Entities)
            {
                if (Entities.Count <= 0) return;

                cs = new CacheItem[Entities.Count];
                Entities.Values.CopyTo(cs, 0);
            }

            if (cs != null && cs.Length > 0)
            {
                var list = new List<TKey>();
                foreach (var item in cs)
                {
                    // 是否过期
                    if (item.ExpireTime > DateTime.Now)
                    {
                        if (item.Entity != null)
                        {
                            // 自动保存
                            if (AutoSave)
                            {
                                // 捕获异常,不影响别人
                                try
                                {
                                    item.Entity.Update();
                                }
                                catch { }
                            }
                            item.Entity = null;
                        }
                        list.Add(item.Key);
                    }
                }
                // 从缓存中删除
                if (list.Count > 0)
                {
                    lock (Entities)
                    {
                        foreach (var item in list)
                        {
                            if (Entities.ContainsKey(item)) Entities.Remove(item);
                        }
                    }
                }
            }
        }

        /// <summary>子类重载实现资源释放逻辑时必须首先调用基类方法</summary>
        /// <param name="disposing">从Dispose调用(释放所有资源)还是析构函数调用(释放非托管资源)。
        /// 因为该方法只会被调用一次,所以该参数的意义不太大。</param>
        protected override void OnDispose(bool disposing)
        {
            Clear();

            base.OnDispose(disposing);
        }
        #endregion

        #region 缓存对象
        /// <summary>缓存对象</summary>
        class CacheItem
        {
            /// <summary>键</summary>
            public TKey Key;

            /// <summary>实体</summary>
            public TEntity Entity;

            /// <summary>缓存过期时间</summary>
            public DateTime ExpireTime;
        }
        #endregion

        #region 单对象缓存
        //private SortedList<TKey, CacheItem> _Entities;
        //! Dictionary在集合方面具有较好查找性能,直接用字段,提高可能的性能
        /// <summary>单对象缓存</summary>
        private Dictionary<TKey, CacheItem> Entities = new Dictionary<TKey, CacheItem>();
        #endregion

        #region 统计
        /// <summary>总次数</summary>
        public Int32 Total;

        /// <summary>命中</summary>
        public Int32 Shoot;

        /// <summary>第一次命中</summary>
        public Int32 Shoot1;

        /// <summary>第二次命中</summary>
        public Int32 Shoot2;

        /// <summary>无效次数</summary>
        public Int32 Invalid;

        /// <summary>下一次显示时间</summary>
        public DateTime NextShow;

        /// <summary>显示统计信息</summary>
        public void ShowStatics()
        {
            if (Total > 0)
            {
                StringBuilder sb = new StringBuilder();
                sb.AppendFormat("单对象缓存<{0},{1}>", typeof(TKey).Name, typeof(TEntity).Name);
                sb.AppendFormat("总次数{0}", Total);
                if (Shoot > 0) sb.AppendFormat(",数据命中{0}({1:P02})", Shoot, (Double)Shoot / Total);
                if (Shoot1 > 0) sb.AppendFormat(",一级命中{0}({1:P02})", Shoot1, (Double)Shoot1 / Total);
                if (Shoot2 > 0) sb.AppendFormat(",二级命中{0}({1:P02})", Shoot2, (Double)Shoot2 / Total);
                if (Invalid > 0) sb.AppendFormat(",无效次数{0}({1:P02})", Invalid, (Double)Invalid / Total);

                XTrace.WriteLine(sb.ToString());
            }
        }
        #endregion

        #region 获取数据
        private TEntity GetItem(TKey key)
        {
            // 为空的key,直接返回null,不进行缓存查找
            if (key == null) return null;
            if (Type.GetTypeCode(typeof(TKey)) == TypeCode.String)
            {
                String value = key as String;
                if (String.IsNullOrEmpty(value)) return null;
            }

            // 更新统计信息
            XCache.CheckShowStatics(ref NextShow, ref Total, ShowStatics);

            // 如果找到项,返回
            CacheItem item = null;
            if (Entities.TryGetValue(key, out item) && item != null)
            {
                Interlocked.Increment(ref Shoot1);
                return GetItem(item, key);
            }

            // 加锁
            lock (Entities)
            {
                // 如果找到项,返回
                if (Entities.TryGetValue(key, out item) && item != null)
                {
                    Interlocked.Increment(ref Shoot2);
                    return GetItem(item, key);
                }

                item = new CacheItem();
                item.Key = key;

                //队列满时,移除最老的一个
                if (Entities.Count >= MaxEntity)
                {
                    TKey keyFirst = GetFirstKey();
                    if (keyFirst != null && (Type.GetTypeCode(typeof(TKey)) != TypeCode.String || String.IsNullOrEmpty(keyFirst as String)))
                    {
                        CacheItem item2 = null;
                        if (Entities.TryGetValue(keyFirst, out item2) && item2 != null)
                        {
                            if (Debug) DAL.WriteLog("单实体缓存{0}超过最大数量限制{1},准备移除第一项{2}", typeof(TEntity).FullName, MaxEntity, keyFirst);

                            Entities.Remove(keyFirst);

                            //自动保存
                            if (AutoSave && item2.Entity != null) InvokeFill(delegate { item2.Entity.Update(); });
                        }
                    }
                }

                //查找数据
                //TEntity entity = FindKeyMethod(key);
                TEntity entity = null;
                InvokeFill(delegate { entity = FindKeyMethod(key); });
                if (entity != null || AllowNull)
                {
                    item.Entity = entity;
                    item.ExpireTime = DateTime.Now.AddSeconds(Expriod);

                    if (!Entities.ContainsKey(key)) Entities.Add(key, item);
                }
                else
                {
                    Interlocked.Increment(ref Invalid);
                }

                return entity;
            }
        }

        /// <summary>内部处理返回对象。
        /// 把对象传进来,而不是只传键值然后查找,是为了避免别的线程移除该项
        /// </summary>
        /// <param name="item"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        private TEntity GetItem(CacheItem item, TKey key)
        {
            if (item == null) return null;

            //未过期,直接返回
            if (DateTime.Now <= item.ExpireTime)
            {
                Interlocked.Increment(ref Shoot);
                return item.Entity;
            }

            //自动保存
            if (AutoSave && item.Entity != null) InvokeFill(delegate { item.Entity.Update(); });

            //查找数据
            //item.Entity = FindKeyMethod(key);
            InvokeFill(delegate { item.Entity = FindKeyMethod(key); });
            item.ExpireTime = DateTime.Now.AddSeconds(Expriod);

            return item.Entity;
        }

        /// <summary>获取数据</summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public TEntity this[TKey key] { get { return GetItem(key); } }

        private TKey GetFirstKey()
        {
            foreach (var item in Entities)
            {
                return item.Key;
            }
            return default(TKey);
        }
        #endregion

        #region 方法
        /// <summary>是否包含指定键</summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public Boolean ContainsKey(TKey key) { return Entities.ContainsKey(key); }

        /// <summary>向单对象缓存添加项</summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public Boolean Add(TKey key, TEntity value)
        {
            // 如果找到项,返回
            CacheItem item = null;
            if (Entities.TryGetValue(key, out item) && item != null && DateTime.Now <= item.ExpireTime) return false;

            // 加锁
            lock (Entities)
            {
                if (Entities.TryGetValue(key, out item))
                {
                    // 如果已存在并且过期,则复制
                    if (item != null)
                    {
                        if (DateTime.Now <= item.ExpireTime) return false;
                        item.Entity.CopyFrom(value);
                    }

                    return false;
                }

                item = new CacheItem();
                item.Key = key;
                item.Entity = value;
                item.ExpireTime = DateTime.Now.AddSeconds(Expriod);

                Entities.Add(key, item);

                return true;
            }
        }

        /// <summary>移除指定项</summary>
        /// <param name="key"></param>
        public void RemoveKey(TKey key)
        {
            CacheItem item = null;
            if (!Entities.TryGetValue(key, out item)) return;
            lock (Entities)
            {
                if (!Entities.TryGetValue(key, out item)) return;

                if (AutoSave && item != null && item.Entity != null) InvokeFill(delegate { item.Entity.Update(); });

                Entities.Remove(key);
            }
        }

        /// <summary>清除所有数据</summary>
        public void Clear()
        {
            if (Debug) DAL.WriteLog("清空单对象缓存:{0}", typeof(TEntity).FullName);

            if (AutoSave)
            {
                lock (Entities)
                {
                    foreach (TKey key in Entities.Keys)
                    {
                        CacheItem item = Entities[key];
                        if (item == null || item.Entity == null) continue;

                        //item.Entity.Update();
                        InvokeFill(delegate { item.Entity.Update(); });
                    }
                }
            }

            Entities.Clear();
        }
        #endregion

        #region ISingleEntityCache 成员
        /// <summary>获取数据</summary>
        /// <param name="key"></param>
        /// <returns></returns>
        IEntity ISingleEntityCache.this[object key] { get { return GetItem((TKey)key); } }
        #endregion
    }

    /// <summary>查找数据的方法</summary>
    /// <typeparam name="TKey">键值类型</typeparam>
    /// <typeparam name="TEntity">实体类型</typeparam>
    /// <param name="key">键值</param>
    /// <returns></returns>
    public delegate TEntity FindKeyDelegate<TKey, TEntity>(TKey key) where TEntity : Entity<TEntity>, new();
}