基于EFCore的数据Cache实现

.NetCore 内置缓存加入到EFCore操作中,数据更新或者查询时自动更新缓存。
初步完成逻辑代码编写,尚未经过测试,诸多细节有待完善。地址

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace FXY_NetCore_DbContext
{
    public class DefaultContext
    {
        /// <summary>
        /// a queue to save the handle,if will be empted when call savechanges().
        /// </summary>
        private ConcurrentQueue<CacheEntity> CacheQueue { get; set; }

        /// <summary>
        /// databse context.
        /// </summary>
        private DbContext Context { get; set; }

        /// <summary>
        /// netocre inner cache.
        /// </summary>
        private IMemoryCache Cache { get; set; }

        /// <summary>
        /// the time of cache's life cycle
        /// </summary>
        private int ExpirtTime { get; set; } = 60;

        /// <summary>
        /// entity context.
        /// </summary>
        /// <param name="context">database context</param>
        /// <param name="cache">cache</param>
        /// <param name="expirtTime">expirt time,default 60 sencond.</param>
        public DefaultContext(DbContext context, IMemoryCache cache, int expirtTime=60)
        {
            CacheQueue = new ConcurrentQueue<CacheEntity>();
            Context = context;
            Cache = cache;
            ExpirtTime = expirtTime;
        }

        /// <summary>
        /// add entity to database context and add the handle to the queue.
        /// <para>it will be excuted when call Savechange().</para>
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        public void Add<TEntity>(TEntity entity)
            where TEntity : class
        {
            bool reesult = Enqueue(entity, CacheHandleEnum.ADD);
            if (reesult)
                Context.Add(entity);
        }

        /// <summary>
        /// add entity list to database context and add the handle to the queue.
        /// <para>it will be excuted when call Savechange().</para>
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entities"></param>
        public void AddRange<TEntity>(params TEntity[] entities)
            where TEntity : class
        {
            foreach (var item in entities)
                Add(item);
        }

        /// <summary>
        /// remove entity to database context and add the handle to the queue.
        /// <para>it will be excuted when call Savechange().</para>
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        public void Remove<TEntity>(TEntity entity)
            where TEntity : class
        {
            bool reesult = Enqueue(entity, CacheHandleEnum.DELETE);
            if (reesult)
                Context.Remove(entity);
        }

        /// <summary>
        /// remove entity list to database context and add the handle to the queue.
        /// <para>it will be excuted when call Savechange().</para>
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entities"></param>
        public void RemoveRange<TEntity>(params TEntity[] entities)
            where TEntity : class
        {
            foreach (var item in entities)
                Remove(item);
        }

        /// <summary>
        /// update entity to database context and add the handle to the queue.
        /// <para>it will be excuted when call Savechange().</para>
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        public void Update<TEntity>(TEntity entity)
            where TEntity : class
        {
            bool reesult = Enqueue(entity, CacheHandleEnum.UPDATE);
            if (reesult)
                Context.Update(entity);
        }

        /// <summary>
        /// update entity list to database context and add the handle to the queue.
        /// <para>it will be excuted when call Savechange().</para>
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entities"></param>
        public void UpdateRange<TEntity>(params TEntity[] entities)
            where TEntity : class
        {
            foreach (var item in entities)
                Update(item);
        }

        /// <summary>
        /// attach entity to database context add the handle to the queue.
        /// <para>it will be excuted when call Savechange().</para>
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entities"></param>
        public void Attach<TEntity>(TEntity entity)
            where TEntity : class
        {
            bool reesult = Enqueue(entity, CacheHandleEnum.UPDATE);
            if (reesult)
                Context.Attach(entity);
        }

        /// <summary>
        /// attach entity list to database context add the handle to the queue.
        /// <para>it will be excuted when call Savechange().</para>
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entities"></param>
        public void AttachRange<TEntity>(params TEntity[] entities)
            where TEntity : class
        {
            foreach (var item in entities)
                Attach(item);
        }


        /// <summary>
        /// update cache and database.
        /// <para>update cache at first,if update cache is failed,return false,else commit the changes to database.</para>
        /// </summary>
        /// <returns></returns>
        public bool SaveChanges()
        {
            bool result = Dequeue();
            if (result)
                result = Context.SaveChanges() > 0;
            return result;
        }

        /// <summary>
        /// single query.
        /// <para>find it in the cache first,return if find it,otherwise search it in database by efcore.</para>
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public TEntity Get<TEntity>(string key)
            where TEntity : class
        {
            var result = GetCache<TEntity>();
            if (result == null)
                result = Context.Find<TEntity>(key);
            /*if add it to the cache before back the query result?*/
            Enqueue(result, CacheHandleEnum.ADD);
            return result;
        }

        /// <summary>
        /// collection query.
        /// <para>do not allow fuzzy query</para>
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="keys"></param>
        /// <returns></returns>
        public List<TEntity> Get<TEntity>(string[] keys)
        where TEntity : class
        {
            var result = new List<TEntity>();
            foreach (var item in keys)
                result.Add(Get<TEntity>(item));
            return result;
        }

        #region private

        #region cache queue

        /// <summary>
        /// add the handle to the context queue.
        /// </summary>
        /// <param name="model"></param>
        /// <param name="handleEnum"></param>
        private bool Enqueue(object model, CacheHandleEnum handleEnum)
        {
            var key = GetModelKey(model);
            var entity = new CacheEntity()
            {
                ExpiryTime = ExpirtTime,
                Handle = handleEnum,
                Value = model,
                key = key
            };
            if (CacheQueue.TryPeek(out CacheEntity cacheEntity1))
                return false;
            else
            {
                CacheQueue.Enqueue(entity);
                return CacheQueue.TryPeek(out CacheEntity cacheEntity2);
            }
        }

        /// <summary>
        /// update the changes to cache,and remove it from the cache queue.
        /// <para>include add,delete and update.</para>
        /// </summary>
        /// <returns></returns>
        private bool Dequeue()
        {
            bool check = false;
            bool dequeue = CacheQueue.TryDequeue(out CacheEntity cacheEntity);
            if (dequeue)
            {
                switch (cacheEntity.Handle)
                {
                    case CacheHandleEnum.ADD:
                        check = AddCache(cacheEntity);
                        break;

                    case CacheHandleEnum.DELETE:
                        check = RemoveCache(cacheEntity);
                        break;

                    case CacheHandleEnum.UPDATE:
                        check = RemoveCache(cacheEntity);
                        if (check)
                            check = AddCache(cacheEntity);

                        break;

                    default:
                        check = false;
                        break;
                }
            }
            else
            {
                check = false;
                /*do nothing*/
            }
            return check;
        }

        #endregion

        #region cache core

        /// <summary>
        /// add cache
        /// </summary>
        /// <param name="cacheEntity"></param>
        /// <returns></returns>
        private bool AddCache(CacheEntity cacheEntity)
        {
            bool check;
            Cache.Set(cacheEntity.key, cacheEntity.Value, new TimeSpan(0, 0, cacheEntity.ExpiryTime));
            check = Cache.Get(cacheEntity.key) != null;
            return check;
        }

        /// <summary>
        /// remove cache.
        /// </summary>
        /// <param name="cacheEntity"></param>
        /// <returns></returns>
        private bool RemoveCache(CacheEntity cacheEntity)
        {
            bool check;
            Cache.Remove(cacheEntity.key);
            check = Cache.Get(cacheEntity.key) == null;
            return check;
        }

        /// <summary>
        /// update cache.
        /// </summary>
        /// <param name="cacheEntity"></param>
        /// <returns></returns>
        private bool UpdateCache(CacheEntity cacheEntity)
        {
            bool check = RemoveCache(cacheEntity);
            if (check)
                check = AddCache(cacheEntity);
            return check;
        }

        /// <summary>
        /// get cache by key.
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <returns></returns>
        private TEntity GetCache<TEntity>()
            where TEntity : class
        {
            string key = GetModelKey(default(TEntity));
            Cache.TryGetValue(key, out object value);
            return value as TEntity;
        }

        #endregion

        #region other

        /// <summary>
        /// get the key of a entity.
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        private string GetModelKey(object model)
        {
            string key = "";
            var type = model.GetType().GetProperties();
            foreach (var item in type)
            {
                if (item.GetCustomAttributes(typeof(KeyAttribute), true).Length > 0)
                {
                    key = item.Name;
                    break;
                }
            }
            return key;
        }

        #endregion
        #endregion
    }

    /// <summary>
    /// a entity to handle cache
    /// </summary>
    public sealed class CacheEntity
    {
        /// <summary>
        /// cache key
        /// </summary>
        [Key]
        public string key { get; set; }

        /// <summary>
        /// cache value
        /// </summary>
        public object Value { get; set; }

        /// <summary>
        /// The lifecycle of caching,seconds.
        /// </summary>
        public int ExpiryTime { get; set; }

        /// <summary>
        /// the method will be executed.
        /// </summary>
        public CacheHandleEnum Handle { get; set; }

    }

    /// <summary>
    /// define the handle of cache
    /// </summary>
    public enum CacheHandleEnum
    {
        /// <summary>
        /// add cache
        /// </summary>
        ADD = 1,

        /// <summary>
        /// delete cache
        /// </summary>
        DELETE = 2,

        /// <summary>
        /// update cache
        /// </summary>
        UPDATE = 3
    }
}

猜你喜欢

转载自www.cnblogs.com/fuxuyang/p/10778354.html
今日推荐