通用对象缓存

4

我正在开发一个项目,计划使用Redis作为持久化数据存储。然而,我目前正在开发一个通用的对象缓存。由于我是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();
    }

希望我没有完全偏离主题?欢迎提出任何建议 :)


你能具体说明一下你的问题是什么吗?你可以在ConcurrentBag上使用LINQ,并通过添加ToList()来获取快照。 - Theodoros Chatzigiannakis
嗨,Theodoros,好的,这个包只是一个混合在一起的对象列表,例如。我已经添加了几百个一个类型的对象和几百个另一个类型的对象,现在我想跳过每种类型迭代然后断言列表的步骤,就像我在第一个例子中展示的那样。 - Joachim
1
你可能想要使用.NET内置的通用对象缓存作为自定义缓存的基础。这样,您可以免费获得一些缓存功能,例如缓存过期。 - Scott Chamberlain
3个回答

3

对您的泛型类型进行哈希并保存特定类型的列表。

类似于:

Dictionary<Type,List<T>>

然后按类型键获取值,并按您的要求查询。

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

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

谢谢你的回答,这正是我所寻找的! - Joachim

1
感谢Discosultanuser1190916的帮助,他们引导我找到了使用Redis作为持久化存储(客户端ServiceStack.Redis)的CRUD缓存对象仓库以及完整的LINQ支持所需的方向,这是我目前所能想出的。
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; }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接