通用对象缓存

时间:2015-06-13 12:40:33

标签: c# linq caching

我正在开发一个项目,我计划将Redis用作持久数据存储,但是手头的任务,我正在研究一个通用的Object缓存。作为LINQ的忠实粉丝,我开始设计一个支持此功能的缓存。

    public ConcurrentBag<Object> Cache = new ConcurrentBag<object>();

    public List<T> FindBy<T>(Func<T, bool> predicate) where T : class
    {
        var tmp = new List<T>();

        foreach (var i in Cache)
        {
            try
            {
                T obj = i as T;

                if (obj != null)
                    tmp.Add(obj);
            }
            catch { }
        }

        return tmp.Where(predicate).ToList();
    }

我担心当对象缓存变大时会变得效率低下。 (我估计500k-1m物体)

我希望可以使用这样的东西

    public ConcurrentBag<Object> Cache = new ConcurrentBag<object>();

    public List<T> FindBy<T>(Func<T, bool> predicate) where T : class
    {
        return Cache.Where<T>(predicate).ToList();
    }

希望我不是全部偏离轨道?欢迎任何建议:)

3 个答案:

答案 0 :(得分:3)

哈希你的genric类型并保存特定类型的列表.. 类似的东西:

Dictionary<Type,List<T>>

然后按类型键和查询获取值

答案 1 :(得分:1)

由于您估计缓存中的大量项目并且缓存上的操作将是特定于类型的,因此您可以使用多个包装到字典中。这将加速找到感兴趣类型的缓存子集,如果缓存包含许多不同类型的最小子集,那将是理想的。

readonly IDictionary<Type, ConcurrentBag<object>> _cache = new ConcurrentDictionary<Type, ConcurrentBag<object>>();

public List<T> FindBy<T>(Func<T, bool> predicate) where T : class
{
    // Check if items of type {T} exist in the cache.
    ConcurrentBag<object> bag;
    if (_cache.TryGetValue(typeof (T), out bag))
    {
        // Cast, apply predicate and return.
        return bag.Cast<T>().Where(predicate).ToList();
    }
    // Return an empty list.
    return new List<T>();
}

当然,现在您还需要处理将项目正确添加到缓存中,以确保将不同的类型放入相应的包中。

答案 2 :(得分:1)

非常感谢Discosultanuser1190916我们都指出了正确的方向,我需要使用Redis进行持久存储来获得具有完全LINQ支持的CRUD缓存对象存储库(Client { {3}}),这是我迄今为止所设想的。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Collections.Concurrent;
using ServiceStack.Redis;

namespace RedisTestRepo
{
    class Program
    {
        //public static DataRepository Db;

        static void Main(string[] args)
        {
            Repo r = new Repo();

            // We do not touch sequence, by running example we can see that sequence will give Users new unique Id.

            // Empty data store.
            Console.WriteLine("Our User Data store should be empty.");
            Console.WriteLine("Users In \"Database\" : {0}\n", r.All<User>().Count);

            // Add imaginary users.
            Console.WriteLine("Adding 100 imaginairy users.");
            for (int i = 0; i < 99; i++)
                r.Create<User>(new User { Id = r.Next<User>(), Name = "Joachim Nordvik" });

            // We should have 100 users in data store.
            Console.WriteLine("Users In \"Database\" : {0}\n", r.All<User>().Count);

            // Lets print 10 users from data store.
            Console.WriteLine("Order by Id, Take (10) and print users.");
            foreach (var u in r.All<User>().OrderBy(z => z.Id).Take(10))
            {
                Console.WriteLine("ID:{0}, Name: {1}", u.Id, u.Name);

                // Lets update an entity.
                u.Name = "My new Name";
                r.Update<User>(x=>x.Id == u.Id, u);
            }


            // Lets print 20 users from data store, we already edited 10 users.
            Console.WriteLine("\nOrder by Id, Take (20) and print users, we previously edited the users that we printed lets see if it worked.");
            foreach (var u in r.All<User>().OrderBy(z => z.Id).Take(20))
            {
                Console.WriteLine("ID:{0}, Name: {1}", u.Id, u.Name);
            }

            // Clean up data store.
            Console.WriteLine("\nCleaning up Data Store.\n");
            foreach (var u in r.All<User>())
                r.Delete<User>(u);

            // Confirm that we no longer have any users.
            Console.WriteLine("Confirm that we no longer have User entities in Data Store.");
            Console.WriteLine("Users In \"Database\" : {0}\n\n", r.All<User>().Count);

            Console.WriteLine("Hit return to exit!");
            Console.Read();
        }
    }

    public class Repo
    {
        private static readonly PooledRedisClientManager m = new PooledRedisClientManager();

        public Repo()
        {
            // Spool Redis Database into our object cache.
            LoadIntoCache<User>();
        }

        readonly IDictionary<Type, List<object>> _cache = new ConcurrentDictionary<Type, List<object>>();

        /// <summary>
        /// Load {T} into object cache from Data Store.
        /// </summary>
        /// <typeparam name="T">class</typeparam>
        private void LoadIntoCache<T>() where T : class
        {
            _cache[typeof(T)] = GetAll<T>().Cast<object>().ToList();
        }

        /// <summary>
        /// Add single {T} into cache and Data Store.
        /// </summary>
        /// <typeparam name="T">class</typeparam>
        /// <param name="entity">class object</param>
        public void Create<T>(T entity) where T : class
        {
            List<object> list;
            if (!_cache.TryGetValue(typeof(T), out list))
            {
                list = new List<object>();
            }
            list.Add(entity);
            _cache[typeof(T)] = list;
            Store<T>(entity);
        }

        /// <summary>
        /// Delete single {T} from cache and Data Store.
        /// </summary>
        /// <typeparam name="T">class</typeparam>
        /// <param name="entity">class object</param>
        public void Delete<T>(T entity) where T : class
        {
            List<object> list;
            if (_cache.TryGetValue(typeof(T), out list))
            {
                list.Remove(entity);
                _cache[typeof(T)] = list;

                RedisDelete<T>(entity);
            }
        }

        /// <summary>
        /// Tries to update or Add entity to object cache and Data Store.
        /// </summary>
        /// <typeparam name="T">class</typeparam>
        /// <param name="predicate">linq expression</param>
        /// <param name="entity">entity</param>
        public void Update<T>(Func<T, bool> predicate, T entity) where T : class
        {
            List<object> list;
            if (_cache.TryGetValue(typeof(T), out list))
            {
                // Look for old entity.
                var e = list.Cast<T>().Where(predicate).FirstOrDefault();

                if(e != null)
                {
                    list.Remove(e);
                }

                // Regardless if object existed or not we add it to our Cache / Data Store.
                list.Add(entity);
                _cache[typeof(T)] = list;
                Store<T>(entity);
            }
        }

        /// <summary>
        /// Find List<T>(predicate) in cache.
        /// </summary>
        /// <typeparam name="T">class</typeparam>
        /// <param name="predicate">linq statement</param>
        /// <returns></returns>
        public List<T> FindBy<T>(Func<T, bool> predicate) where T : class
        {
            List<object> list;
            if (_cache.TryGetValue(typeof(T), out list))
            {
                return list.Cast<T>().Where(predicate).ToList();
            }
            return new List<T>();
        }

        /// <summary>
        /// Find All {T}
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns>List<T></returns>
        public List<T> All<T>() where T : class
        {
            return GetAll<T>().ToList();
        }

        /// <summary>
        /// Find Single {T} in object cache.
        /// </summary>
        /// <typeparam name="T">class</typeparam>
        /// <param name="predicate">linq statement</param>
        /// <returns></returns>
        public T Read<T>(Func<T, bool> predicate) where T : class
        {
            List<object> list;
            if (_cache.TryGetValue(typeof(T), out list))
            {
                return list.Cast<T>().Where(predicate).FirstOrDefault();
            }
            return null;
        }

        public long Next<T>() where T : class
        {
            long id = 1;

            using (var ctx = m.GetClient())
            {
                try
                {
                    id =  ctx.As<T>().GetNextSequence();
                }
                catch(Exception ex)
                {
                    // Add exception handler.
                }
            }


            return id;
        }

        private void RedisDelete<T>(T entity) where T : class
        {
            using (var ctx = m.GetClient())
                ctx.As<T>().Delete(entity);
        }

        private T Find<T>(long id) where T : class
        {
            using (var ctx = m.GetClient())
                return ctx.As<T>().GetById(id);
        }

        private IList<T> GetAll<T>() where T : class
        {
            using(var ctx = m.GetClient())
            {
                try
                {
                    return ctx.As<T>().GetAll();
                }
                catch
                {
                    return new List<T>();
                }
            }
        }

        private void Store<T>(T entity) where T : class
        {
            using (var ctx = m.GetClient())
                ctx.Store<T>(entity);
        }
    }

    public class User
    {
        public long Id { get; set; }
        public string Name { get; set; }
    }
}