using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading; using ServiceStack.Common; namespace ServiceStack.ServiceInterface.Auth { /// /// Thread-safe In memory UserAuth data store so it can be used without a dependency on Redis. /// public class InMemoryAuthRepository : RedisAuthRepository { public static readonly InMemoryAuthRepository Instance = new InMemoryAuthRepository(); protected Dictionary> Sets { get; set; } protected Dictionary> Hashes { get; set; } internal List TrackedTypes = new List(); class TypedData : IClearable { internal static TypedData Instance = new TypedData(); private TypedData() { lock (InMemoryAuthRepository.Instance.TrackedTypes) InMemoryAuthRepository.Instance.TrackedTypes.Add(this); } internal readonly List Items = new List(); internal int Sequence = 0; public void Clear() { lock (Items) Items.Clear(); Interlocked.CompareExchange(ref Sequence, 0, Sequence); } } public InMemoryAuthRepository() : base(new InMemoryManagerFacade(Instance)) { this.Sets = new Dictionary>(); this.Hashes = new Dictionary>(); } class InMemoryManagerFacade : IRedisClientManagerFacade { private readonly InMemoryAuthRepository root; public InMemoryManagerFacade(InMemoryAuthRepository root) { this.root = root; } public IRedisClientFacade GetClient() { return new InMemoryClientFacade(root); } public void Clear() { lock (Instance.Sets) Instance.Sets.Clear(); lock (Instance.Hashes) Instance.Hashes.Clear(); lock (Instance.TrackedTypes) Instance.TrackedTypes.ForEach(x => x.Clear()); } } class InMemoryClientFacade : IRedisClientFacade { private readonly InMemoryAuthRepository root; public InMemoryClientFacade(InMemoryAuthRepository root) { this.root = root; } class InMemoryTypedClientFacade : ITypedRedisClientFacade { private readonly InMemoryAuthRepository root; public InMemoryTypedClientFacade(InMemoryAuthRepository root) { this.root = root; } public int GetNextSequence() { return Interlocked.Increment(ref TypedData.Instance.Sequence); } public T GetById(object id) { if (id == null) return default(T); lock (TypedData.Instance.Items) { return TypedData.Instance.Items.FirstOrDefault(x => id.ToString() == x.ToId().ToString()); } } public List GetByIds(IEnumerable ids) { var idsSet = new HashSet(); foreach (var id in ids) idsSet.Add(id.ToString()); lock (TypedData.Instance.Items) { return TypedData.Instance.Items.Where(x => idsSet.Contains(x.ToId().ToString())).ToList(); } } } public HashSet GetAllItemsFromSet(string setId) { lock (root.Sets) { HashSet set; return root.Sets.TryGetValue(setId, out set) ? set : new HashSet(); } } public void Store(T item) where T : class , new() { if (item == null) return; lock (TypedData.Instance.Items) { for (var i = 0; i < TypedData.Instance.Items.Count; i++) { var o = TypedData.Instance.Items[i]; if (o.ToId().ToString() != item.ToId().ToString()) continue; TypedData.Instance.Items[i] = item; return; } TypedData.Instance.Items.Add(item); } } public string GetValueFromHash(string hashId, string key) { hashId.ThrowIfNull("hashId"); key.ThrowIfNull("key"); lock (root.Hashes) { Dictionary hash; if (!root.Hashes.TryGetValue(hashId, out hash)) return null; string value; hash.TryGetValue(key, out value); return value; } } public void SetEntryInHash(string hashId, string key, string value) { hashId.ThrowIfNull("hashId"); key.ThrowIfNull("key"); lock (root.Hashes) { Dictionary hash; if (!root.Hashes.TryGetValue(hashId, out hash)) root.Hashes[hashId] = hash = new Dictionary(); hash[key] = value; } } public void RemoveEntryFromHash(string hashId, string key) { hashId.ThrowIfNull("hashId"); key.ThrowIfNull("key"); lock (root.Hashes) { Dictionary hash; if (!root.Hashes.TryGetValue(hashId, out hash)) root.Hashes[hashId] = hash = new Dictionary(); hash.Remove(key); } } public void AddItemToSet(string setId, string item) { lock (root.Sets) { HashSet set; if (!root.Sets.TryGetValue(setId, out set)) root.Sets[setId] = set = new HashSet(); set.Add(item); } } public ITypedRedisClientFacade As() { return new InMemoryTypedClientFacade(root); } public void Dispose() { } } } }