/// <summary> /// Merges the specified values into the specified key. /// </summary> /// <param name="key">The key.</param> /// <param name="value">The value.</param> /// <exception cref="System.ArgumentNullException">key</exception> public void Merge(EntityRelationshipCacheKey key, IDictionary <long, ISet <long> > value) { if (key == null) { throw new ArgumentNullException("key"); } ///// // Ensure the dictionary is a concurrent dictionary. ///// var concurrentDictionary = value as ConcurrentDictionary <long, ISet <long> > ?? new ConcurrentDictionary <long, ISet <long> >(value); if (key.Direction == Direction.Forward) { if (_forwardCache.SetValue(key.EntityId, concurrentDictionary)) { _reverseCache.Validate(key.EntityId, concurrentDictionary); } } else { if (_reverseCache.SetValue(key.EntityId, concurrentDictionary)) { _forwardCache.Validate(key.EntityId, concurrentDictionary); } } }
/// <summary> /// Providers the remove. /// </summary> /// <param name="key">The key.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">key</exception> private bool ProviderRemove(EntityRelationshipCacheKey key) { if (key == null) { throw new ArgumentNullException("key"); } bool result; lock ( _syncRoot ) { if (key.Direction == Direction.Forward) { IDictionary <long, ISet <long> > typedCachedValues; result = _forwardCache.Remove(key.EntityId, out typedCachedValues); _reverseCache.Remove(key.EntityId, out typedCachedValues); } else { IDictionary <long, ISet <long> > typedCachedValues; result = _reverseCache.Remove(key.EntityId, out typedCachedValues); _forwardCache.Remove(key.EntityId, out typedCachedValues); } } return(result); }
/// <summary> /// Fill the EntityRelationshipCache with preloaded data. /// </summary> /// <param name="entityData"></param> private static void AddRelationshipsToCache(EntityData entityData) { long entityId = entityData.Id.Id; // Relationships IEnumerable <IGrouping <bool, RelationshipData> > relsByEntityAndDir = entityData.Relationships .GroupBy(rel => rel.IsReverseActual); foreach (var entry in relsByEntityAndDir) { Direction direction = entry.Key ? Direction.Reverse : Direction.Forward; var entityAndDirKey = new EntityRelationshipCacheKey(entityId, direction); // Get relationship cache for entity IDictionary <long, ISet <long> > cachedRelationships = new ConcurrentDictionary <long, ISet <long> >( ); // Group by relationship type foreach (RelationshipData relTypeEntry in entry) { long relTypeId = relTypeEntry.RelationshipTypeId.Id; List <RelationshipInstanceData> values = relTypeEntry.Instances; var relSet = new HashSet <long>(values.Select(value => value.Entity.Id.Id)); cachedRelationships[relTypeId] = relSet; } EntityRelationshipCache.Instance.Merge(entityAndDirKey, cachedRelationships); } }
/// <summary> /// Tries the parse. /// </summary> /// <param name="key">The key.</param> /// <param name="cacheKey">The cache key.</param> /// <returns></returns> public static bool TryParse(string key, out EntityRelationshipCacheKey cacheKey) { cacheKey = null; if (!string.IsNullOrEmpty(key)) { string[] args = key.Split('_'); if (args.Length == 2 && !string.IsNullOrEmpty(args[0]) && !string.IsNullOrEmpty(args[1])) { long id; if (long.TryParse(args[0], out id)) { Direction direction; if (Enum.TryParse(args[1], out direction)) { cacheKey = new EntityRelationshipCacheKey(id, direction); return(true); } } } } return(false); }
/// <summary> /// Determines whether the specified <see cref="System.Object" /> is equal to this instance. /// </summary> /// <param name="obj"> /// The <see cref="System.Object" /> to compare with this instance. /// </param> /// <returns> /// <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>. /// </returns> public bool Equals(EntityRelationshipCacheKey obj) { if (obj == null) { return(false); } return(EntityId == obj.EntityId && Direction == obj.Direction); }
/// <summary> /// Determines whether the specified key contains key. /// </summary> /// <param name="key">The key.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">key</exception> public bool ContainsKey(EntityRelationshipCacheKey key) { if (key == null) { throw new ArgumentNullException("key"); } if (key.Direction == Direction.Forward) { return(_forwardCache.ContainsKey(key.EntityId)); } return(_reverseCache.ContainsKey(key.EntityId)); }
/// <summary> /// Gets or sets the <see cref="IDictionary{TKey,TValue}" /> with the specified key. /// </summary> /// <value> /// The <see cref="IDictionary{TKey,TValue}" />. /// </value> /// <param name="key">The key.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">key</exception> public IReadOnlyDictionary <long, ISet <long> > this[EntityRelationshipCacheKey key] { get { if (key == null) { throw new ArgumentNullException("key"); } IReadOnlyDictionary <long, ISet <long> > cacheValue; if (!TryGetValue(key, out cacheValue)) { throw new KeyNotFoundException( ); } return(cacheValue); } }
/// <summary> /// Tries the get value. /// </summary> /// <param name="key">The key.</param> /// <param name="value">The value.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">key</exception> public bool TryGetValue(EntityRelationshipCacheKey key, out IReadOnlyDictionary <long, ISet <long> > value) { if (key == null) { throw new ArgumentNullException("key"); } value = null; IDictionary <long, ISet <long> > cacheValue; bool result = key.Direction == Direction.Forward ? _forwardCache.TryGetValue(key.EntityId, out cacheValue) : _reverseCache.TryGetValue(key.EntityId, out cacheValue); if (result) { value = new ReadOnlyDictionary <long, ISet <long> >(cacheValue); } return(result); }
/// <summary> /// Removes the specified key. /// </summary> /// <param name="key">The key.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">key</exception> public bool Remove(EntityRelationshipCacheKey key) { using (var entityTypeContext = new EntityTypeContext( )) { IEntity entity = EntityCache.Instance.Get(key.EntityId); if (entity != null) { entityTypeContext.Merge(key.EntityId, entity.TypeIds); } bool result = ProviderRemove(key); HashSet <long> typeIds = entityTypeContext.Get(key.EntityId); var message = new EntityRelationshipCacheMessage( ); message.RemoveKeys.Add(new SerializableEntityRelationshipCacheKey(SerializableEntityId.Create(key.EntityId, typeIds), key.Direction)); MessageChannel.Publish(message, PublishOptions.FireAndForget, false, MergeMessages); return(result); } }
/// <summary> /// Caches the relationship table. /// </summary> /// <param name="getQuery">Function that returns the query to execute.</param> /// <param name="direction">The direction.</param> private static void CacheRelationshipTable(Func <string> getQuery, Direction direction) { using (var context = DatabaseContext.GetContext( )) { using (var command = context.CreateCommand(getQuery( ))) { context.AddParameter(command, "@tenantId", DbType.Int64, RequestContext.TenantId); var values = new Dictionary <EntityRelationshipCacheKey, IDictionary <long, ISet <long> > >( ); using (IDataReader reader = command.ExecuteReader( )) { long currentTypeId = -1; long currentFromId = -1; EntityRelationshipCacheKey key = null; ConcurrentDictionary <long, ISet <long> > dic = null; ISet <long> set = null; while (reader.Read( )) { long typeId = reader.GetInt64(0); long fromId = reader.GetInt64(1); long toId = reader.GetInt64(2); if (fromId != currentFromId) { if (dic != null) { values.Add(key, dic); } key = new EntityRelationshipCacheKey(fromId, direction); dic = new ConcurrentDictionary <long, ISet <long> >( ); currentFromId = fromId; currentTypeId = -1; } if (typeId != currentTypeId) { set = new HashSet <long>( ); if (dic != null) { dic[typeId] = set; } currentTypeId = typeId; } if (set != null) { set.Add(toId); } } if (dic != null) { values.Add(key, dic); } EntityRelationshipCache.Instance.Preload(values); } } } }