public void AddByIdentity <T>(T o, object[] key = null, int?expirySeconds = null) where T : class, new() { // We can only cache tracked objects! var iw = o?.AsInfraWrapped(); if (iw == null) { return; } StringBuilder sb = new StringBuilder(128); sb.Append(o.GetBaseType().Name); if (key == null) { key = (from a in CEF.CurrentKeyService()?.GetKeyValues(o) select a.value).ToArray(); } if (!key.Any()) { throw new CEFInvalidOperationException("No primary key for this object makes it impossible to cache."); } foreach (var k in key) { sb.Append(k); } var c = _index.GetFirstByName(nameof(MFSEntry.ByIdentityComposite), sb.ToString()); if (!expirySeconds.HasValue) { expirySeconds = CEF.CurrentServiceScope.ResolvedCacheDurationForType(typeof(T)); } var newExpDate = DateTime.Now.AddSeconds(expirySeconds.GetValueOrDefault(DefaultCacheIntervalSeconds)); if (c == null) { c = new MFSEntry(); c.ObjectTypeName = o.GetBaseType().Name; c.ByIdentityComposite = sb.ToString(); c.FileName = BuildNewFileName(o.GetBaseType()); _index.Add(c); } long current; lock (c.ObjSync) { current = ++c.Sequence; c.ExpiryDate = newExpDate; c.Properties = iw.GetAllValues(true, true); c.Persisted = false; c.Active = true; } }
private bool IsValid(MFSEntry c) { if (c == null) { return(false); } return(c.Active && c.ExpiryDate > DateTime.Now); }
/// <summary> /// Returns one or more reconstituted "rows" based on a cache entry. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="c"></param> /// <returns></returns> private IEnumerable <T> GetRows <T>(MFSEntry c) where T : class, new() { IEnumerable <IDictionary <string, object> > rows = null; IEnumerable <object> list = null; string cfn = null; lock (c.ObjSync) { rows = c.Rows; list = c.SourceList; cfn = c.FileName; c.ReadCount++; c.LastRead = DateTime.Now; } if (rows == null) { // If the reason it's null is we're still waiting for it to be parsed, just do so now if (list != null) { rows = GetParsedRows(list); lock (c.ObjSync) { c.Rows = rows; c.SourceList = null; } } if (rows == null && !string.IsNullOrEmpty(cfn)) { var fn = Path.Combine(RootDirectory, cfn); if (File.Exists(fn)) { rows = GetRowsFromFile(_formatter, fn); lock (c.ObjSync) { c.Rows = rows; } } } } if (rows != null) { foreach (var rowdata in rows) { yield return(CEF.CurrentServiceScope.InternalCreateAddBase(new T(), false, ObjectState.Unchanged, rowdata, null, null, false, false) as T); } } }
/// <summary> /// Returns a reconstituted object based on a cache entry. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="c"></param> /// <returns></returns> private T GetSingle <T>(MFSEntry c) where T : class, new() { IDictionary <string, object> props = null; string cfn = null; lock (c.ObjSync) { props = c.Properties; cfn = c.FileName; c.ReadCount++; c.LastRead = DateTime.Now; } if (props == null) { if (!string.IsNullOrEmpty(cfn)) { var fn = Path.Combine(RootDirectory, cfn); if (File.Exists(fn)) { props = GetPropertiesFromFile(_formatter, fn); lock (c.ObjSync) { c.Properties = props; } } } } if (props != null) { return(CEF.CurrentServiceScope.InternalCreateAddBase(new T(), false, ObjectState.Unchanged, c.Properties, null, null, false, false) as T); } return(null); }
public void AddByQuery <T>(IEnumerable <T> list, string text, object[] parms = null, int?expirySeconds = null, CacheBehavior?mode = null) where T : class, new() { var ss = CEF.CurrentServiceScope; if (mode == null) { mode = ss.ResolvedCacheBehaviorForType(typeof(T)); } if ((mode & CacheBehavior.QueryBased) == 0 && (mode & CacheBehavior.ConvertQueryToIdentity) != 0 && ((mode & CacheBehavior.ForAllDoesntConvertToIdentity) == 0 || string.Compare(text, "All", true) != 0)) { // All we can do is for all list items, add to identity cache void act2() { try { Interlocked.Increment(ref _working); Parallel.ForEach(list, (i) => { using (CEF.UseServiceScope(ss)) { AddByIdentity <T>(i, expirySeconds: expirySeconds); } }); } catch (Exception ex) { CEFDebug.WriteInfo($"Exception in cache serializer: {ex.Message}"); } finally { Interlocked.Decrement(ref _working); } } if (Globals.AsyncCacheUpdates) { Task.Factory.StartNew(act2); } else { act2(); } return; } if ((mode & CacheBehavior.OnlyForAllQuery) != 0 && string.Compare(text, "All", true) != 0) { return; } StringBuilder sb = new StringBuilder(128); sb.Append(typeof(T).Name); sb.Append(text.ToUpperInvariant()); if (parms != null) { foreach (var k in parms) { sb.Append(k); } } string hash; using (SHA256Managed hasher = new SHA256Managed()) { hash = Convert.ToBase64String(hasher.ComputeHash(Encoding.ASCII.GetBytes(sb.ToString()))); } var c = _index.GetFirstByName(nameof(MFSEntry.ByQuerySHA), hash); if (!expirySeconds.HasValue) { expirySeconds = CEF.CurrentServiceScope.ResolvedCacheDurationForType(typeof(T)); } var newExpDate = DateTime.Now.AddSeconds(expirySeconds.GetValueOrDefault(DefaultCacheIntervalSeconds)); if (c == null) { c = new MFSEntry(); if (list.Any()) { c.ObjectTypeName = list.First().GetBaseType().Name; } else { c.ObjectTypeName = typeof(T).Name; } c.ByQuerySHA = hash; c.FileName = BuildNewFileName(typeof(T)); c.QueryForAll = string.Compare(text, "All", true) == 0; _index.Add(c); } long current; lock (c.ObjSync) { current = ++c.Sequence; c.ExpiryDate = newExpDate; c.SourceList = list; c.Active = true; } void act() { try { Interlocked.Increment(ref _working); using (CEF.UseServiceScope(ss)) { // Process all items in parallel, building a list we'll turn into json but also potentially caching "by identity" per row ConcurrentBag <IDictionary <string, object> > rows = new ConcurrentBag <IDictionary <string, object> >(); var aiw = list.AllAsInfraWrapped().ToArray(); Parallel.ForEach(aiw, (iw) => { using (CEF.UseServiceScope(ss)) { rows.Add(iw.GetAllValues(true, true)); if ((mode & CacheBehavior.ConvertQueryToIdentity) != 0 && ((mode & CacheBehavior.ForAllDoesntConvertToIdentity) == 0 || string.Compare(text, "All", true) != 0)) { var uw = iw.AsUnwrapped() as T; if (uw != null) { AddByIdentity <T>(uw, expirySeconds: expirySeconds); } } } }); lock (c.ObjSync) { if (c.Sequence == current) { c.Rows = rows; c.SourceList = null; } } } } catch (Exception ex) { CEFDebug.WriteInfo($"Exception in cache serializer: {ex.Message}"); // Invalidate the entry is probably the safest c.Active = false; c.Properties = null; c.Rows = null; } finally { Interlocked.Decrement(ref _working); } } if (Globals.AsyncCacheUpdates) { Task.Factory.StartNew(act); } else { act(); } }
/// <summary> /// Reads any existing cache index file, building the in-memory representation, without bringing in actual item data until actually requested. /// </summary> private void RestoreIndex() { if (DefaultStorageStrategy == CacheStorageStrategy.OnlyMemory) { return; } try { var fn = Path.Combine(RootDirectory, "index.json"); if (File.Exists(fn)) { using (var jr = new JsonTextReader(new StreamReader(File.OpenRead(fn)))) { // Should be start array jr.Read(); while (jr.Read() && jr.TokenType != JsonToken.EndArray) { // Should be start object jr.Read(); var i = new MFSEntry(); i.FileName = jr.ReadAsString(); jr.Read(); i.ObjectTypeName = jr.ReadAsString(); jr.Read(); i.ByQuerySHA = jr.ReadAsString(); jr.Read(); i.QueryForAll = jr.ReadAsBoolean().GetValueOrDefault(); jr.Read(); i.ByIdentityComposite = jr.ReadAsString(); jr.Read(); i.ExpiryDate = jr.ReadAsDateTime().GetValueOrDefault(); jr.Read(); if (i.ExpiryDate > DateTime.Now) { i.Active = true; i.Persisted = true; _index.Add(i); } } } } else { FlushAll(RootDirectory); _index.Clear(); } } catch (Exception ex) { CEFDebug.WriteInfo($"Exception in cache restore index: {ex.Message}"); // Any problems, go with an empty cache FlushAll(RootDirectory); _index.Clear(); } }