/// <summary>
 /// Adds to map.
 /// </summary>
 /// <param name="definition">The definition.</param>
 /// <param name="forceIgnoreHierarchical">if set to <c>true</c> [force ignore hierarchical].</param>
 public void AddToMap(IDefinition definition, bool forceIgnoreHierarchical = false)
 {
     MapKeys(fileKeys, definition.FileCI);
     MapKeys(typeKeys, definition.Type);
     MapKeys(typeAndIdKeys, ConstructKey(definition.Type, definition.Id));
     MapKeys(allFileKeys, definition.FileCI);
     MapKeys(directoryKeys, definition.ParentDirectory);
     if (!string.IsNullOrWhiteSpace(definition.DiskFile))
     {
         MapKeys(allFileKeys, definition.DiskFile.ToLowerInvariant());
     }
     if (definition.OverwrittenFileNames?.Count > 0)
     {
         foreach (var item in definition.OverwrittenFileNames)
         {
             MapKeys(allFileKeys, item.ToLowerInvariant());
         }
     }
     if (useHierarchalMap && !forceIgnoreHierarchical)
     {
         MapHierarchicalDefinition(definition);
     }
     if (definition.IsFromGame)
     {
         gameDefinitionsCount++;
     }
     definitions.Add(definition);
 }
        /// <summary>
        /// Maps the pretty print hierarchy.
        /// </summary>
        /// <param name="definition">The definition.</param>
        private void MapHierarchicalDefinition(IDefinition definition)
        {
            bool shouldAdd              = false;
            var  parentDirectoryCI      = ResolveHierarchalParentDirectory(definition);
            var  hierarchicalDefinition = mainHierarchalDefinitions.GetFirstByNameNoLock(nameof(IHierarchicalDefinitions.Name), parentDirectoryCI);

            if (hierarchicalDefinition == null)
            {
                hierarchicalDefinition      = DIResolver.Get <IHierarchicalDefinitions>();
                hierarchicalDefinition.Name = parentDirectoryCI;
                childHierarchicalDefinitions.TryAdd(parentDirectoryCI, new ConcurrentIndexedList <IHierarchicalDefinitions>(nameof(IHierarchicalDefinitions.Name)));
                shouldAdd = true;
            }
            bool exists = false;
            IHierarchicalDefinitions child = null;

            if (childHierarchicalDefinitions.TryGetValue(hierarchicalDefinition.Name, out var children))
            {
                child  = children.GetFirstByNameNoLock(nameof(IHierarchicalDefinitions.Name), definition.Id);
                exists = child != null;
            }
            if (!exists)
            {
                child      = DIResolver.Get <IHierarchicalDefinitions>();
                child.Name = definition.Id;
                child.Key  = definition.TypeAndId;
                child.FileNames.Add(definition.FileCI);
                children.Add(child);
                if (shouldAdd)
                {
                    mainHierarchalDefinitions.Add(hierarchicalDefinition);
                }
            }
            else
            {
                if (!child.FileNames.Contains(definition.FileCI))
                {
                    child.FileNames.Add(definition.FileCI);
                }
            }
            if (child.Mods == null)
            {
                child.Mods = new List <string>();
            }
            if (!child.Mods.Contains(definition.ModName))
            {
                child.Mods.Add(definition.ModName);
            }
            if (hierarchicalDefinition.Mods == null)
            {
                hierarchicalDefinition.Mods = new List <string>();
            }
            if (!hierarchicalDefinition.Mods.Contains(definition.ModName))
            {
                hierarchicalDefinition.Mods.Add(definition.ModName);
            }
        }
 /// <summary>
 /// Adds to map.
 /// </summary>
 /// <param name="definition">The definition.</param>
 /// <param name="forceIgnoreHierarchical">if set to <c>true</c> [force ignore hierarchical].</param>
 public void AddToMap(IDefinition definition, bool forceIgnoreHierarchical = false)
 {
     MapKeys(fileKeys, definition.FileCI);
     MapKeys(typeKeys, definition.Type);
     MapKeys(typeAndIdKeys, ConstructKey(definition.Type, definition.Id));
     MapKeys(allFileKeys, definition.FileCI);
     if (definition.OverwrittenFileNames?.Count > 0)
     {
         foreach (var item in definition.OverwrittenFileNames)
         {
             MapKeys(allFileKeys, item.ToLowerInvariant());
         }
     }
     if (useHierarchalMap && !forceIgnoreHierarchical)
     {
         MapHierarchicalDefinition(definition);
     }
     definitions.Add(definition);
 }
        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();
            }
        }