Skip to main content
Code Review

Return to Question

replaced http://codereview.stackexchange.com/ with https://codereview.stackexchange.com/
Source Link

I have previously attempted to write a object-cache that I use in co-junction with Redis keeping a persistent data-model. Old post can be read here Cached-object Store c# with Redis client for persistent storage Cached-object Store c# with Redis client for persistent storage

I have previously attempted to write a object-cache that I use in co-junction with Redis keeping a persistent data-model. Old post can be read here Cached-object Store c# with Redis client for persistent storage

I have previously attempted to write a object-cache that I use in co-junction with Redis keeping a persistent data-model. Old post can be read here Cached-object Store c# with Redis client for persistent storage

Source Link
Joachim
  • 205
  • 3
  • 11

Object-cache C# Take two

I have previously attempted to write a object-cache that I use in co-junction with Redis keeping a persistent data-model. Old post can be read here Cached-object Store c# with Redis client for persistent storage

Not focusing on Redis it's the Object-cache that is the important portion, one of the questions i previously got was what if the data-model changes in Redis how will the application know about this? It wont, and at current stage this is not something that is a concern, for now I use Redis just as a dump, read / writes happen from only "this" application.

Where is this used? In the environment where this is used, application itself is a REST / RPC (json) service where multiple clients connect and 99% of the time they read and most of the time its a matter of searching through very long lists of entities, I store entities in HashSet's where i override both Equals and GetHashCode code so that the "key" of each entity is it's unique Id of each entity (in some cases a composite key). With tests of millions of entities in the HashSet's finding single/multiple entities take a few milliseconds compared to several seconds or minutes at times when model/predicate is getting complex when I use ORM's such as EF / Castle ActiveRecord / native NHibernate. I am not trying to write my own ORM, however I duplicate some of the functionality that are typically found in ORM's.

Is this looking right? Previously my code contained a huge amount of mistakes and outright wrong code.

using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Runtime.Caching;
using System.Linq;
using ServiceStack.Redis;
namespace Datamodel
{
 public class Repository
 {
 private static readonly PooledRedisClientManager m = new PooledRedisClientManager();
 readonly static ConcurrentDictionary<Type, HashSet<object>> _cache = new ConcurrentDictionary<Type, HashSet<object>>();
 /// <summary>
 /// Load {T} into Object-cache from Data Store.
 /// </summary>
 /// <typeparam name="T">class</typeparam>
 public static void LoadIntoCache<T>() where T : class
 {
 // Lets make sure we never replace _cache[T] if key is already present. 
 if (!_cache.ContainsKey(typeof(T)))
 {
 _cache[typeof(T)] = new HashSet<object>(RedisGetAll<T>().Cast<object>().ToList());
 }
 }
 /// <summary>
 /// Ensures that given type exist in Object-cache.
 /// </summary>
 /// <param name="type"></param>
 /// <returns></returns>
 private static Type EnsureTypeInCache(Type type)
 {
 // Check if type exist in _cache 
 if (!_cache.ContainsKey(type))
 {
 // I am aware that this portion should contain exception handling. 
 // Key not present adding key and empty cache.
 _cache.TryAdd(type, new HashSet<object>());
 }
 return type;
 }
 
 /// <summary>
 /// Find First {T} in Object-cache.
 /// </summary>
 /// <typeparam name="T">class</typeparam>
 /// <param name="predicate">linq statement</param>
 /// <returns></returns>
 public static T FindFirstBy<T>(Func<T, bool> predicate) where T : class
 {
 // Lets prevent race conditions, locking down cache.
 lock (EnsureTypeInCache(typeof(T)))
 {
 return _cache[typeof(T)].Cast<T>().Where(predicate).FirstOrDefault();
 }
 }
 /// <summary>
 /// Find Single {T} in Object-cache.
 /// </summary>
 /// <typeparam name="T">class</typeparam>
 /// <param name="predicate">linq statement</param>
 /// <returns></returns>
 public static T FindSingleBy<T>(Func<T, bool> predicate) where T : class
 {
 // Lets prevent race conditions, locking down cache.
 lock (EnsureTypeInCache(typeof(T)))
 {
 return _cache[typeof(T)].Cast<T>().Where(predicate).SingleOrDefault();
 }
 }
 /// <summary>
 /// Add or Update entity in Object-cache and Data Store.
 /// </summary>
 /// <typeparam name="T">class</typeparam>
 /// <param name="predicate">linq expression</param>
 /// <param name="entity">entity</param>
 public static bool AddOrUpdate<T>(T entity) where T : class
 {
 // Lets prevent race conditions, locking down cache.
 lock (EnsureTypeInCache(typeof(T)))
 {
 if (_cache[typeof(T)].Contains(entity))
 {
 _cache[typeof(T)].Remove(entity);
 }
 _cache[typeof(T)].Add(entity);
 }
 // Redis does not care if record is new or old as it will Add or Update regardless.
 RedisStore<T>(entity);
 return true;
 }
 /// <summary>
 /// Delete single {T} from Object-cache and Data Store.
 /// </summary>
 /// <typeparam name="T">class</typeparam>
 /// <param name="entity">class object</param>
 public static void Remove<T>(T entity) where T : class
 {
 // Lets prevent race conditions, locking down cache.
 lock (EnsureTypeInCache(typeof(T)))
 {
 if (_cache[typeof(T)].Contains(entity))
 {
 _cache[typeof(T)].Remove(entity);
 }
 RedisDelete<T>(entity);
 }
 }
 /// <summary>
 /// Find List<T>(predicate) in Object-cache.
 /// </summary>
 /// <typeparam name="T">class</typeparam>
 /// <param name="predicate">linq statement</param>
 /// <returns></returns>
 public static List<T> FindBy<T>(Func<T, bool> predicate) where T : class
 {
 // Lets prevent race conditions, locking down cache.
 lock (EnsureTypeInCache(typeof(T)))
 {
 return _cache[typeof(T)].Cast<T>().Where(predicate).ToList();
 }
 }
 /// <summary>
 /// Get all {T} from Object-cache.
 /// </summary>
 /// <typeparam name="T"></typeparam>
 /// <returns></returns>
 public static List<T> All<T>() where T : class
 {
 // Lets prevent race conditions, locking down cache.
 lock (EnsureTypeInCache(typeof(T)))
 {
 return _cache[typeof(T)].Cast<T>().ToList();
 }
 }
 /// <summary>
 /// Get Next Sequence for the given {T} Entity from Data Store. 
 /// </summary>
 /// <typeparam name="T">class</typeparam>
 /// <returns>long</returns>
 public static long Next<T>() where T : class
 {
 return RedisNext<T>();
 }
 #region Redis Commands
 //
 // Following methods are ment as static private methods.
 //
 private static long RedisNext<T>() where T : class
 {
 using (var ctx = m.GetClient())
 return ctx.As<T>().GetNextSequence();
 }
 private static void RedisDelete<T>(T entity) where T : class
 {
 using (var ctx = m.GetClient())
 ctx.As<T>().Delete(entity);
 }
 private static T RedisFind<T>(long id) where T : class
 {
 using (var ctx = m.GetClient())
 return ctx.As<T>().GetById(id);
 }
 private static HashSet<T> RedisGetAll<T>() where T : class
 {
 using (var ctx = m.GetClient())
 return new HashSet<T>(ctx.As<T>().GetAll());
 }
 private static void RedisStore<T>(T entity) where T : class
 {
 using (var ctx = m.GetClient())
 ctx.Store<T>(entity);
 }
 #endregion
 }
}
lang-cs

AltStyle によって変換されたページ (->オリジナル) /