/// <summary> /// Gets the relationship values for a single relationship on a single entity. /// </summary> private static IEnumerable <long> GetRelationshipsImpl(Context context, IEntityRef relTypeId, Direction direction, long entityId) { long relTypeIdWithDir = relTypeId.Id; if (direction == Direction.Reverse) { relTypeIdWithDir = -relTypeIdWithDir; } RelationshipKey key = new RelationshipKey(entityId, relTypeIdWithDir); List <long> relList; if (!context.RawData.Relationships.TryGetValue(key, out relList)) { return(Enumerable.Empty <long>()); } return(relList); }
/// <summary> /// Creates an entity ref for the ID to be checked. /// </summary> /// <remarks> /// The BulkRequestResult graph typically already has type information for the entities being loaded. /// The security engine requires type information to determine applicable rules. /// So instead of having it attempt to activate the entities (additional DB trips) to determine the types, just /// pass the type information along instead. /// This is done by creating a EntityTypeOnly implementation of IEntity, and packing it into the EntityRef that we pass. /// </remarks> /// <param name="data"></param> /// <param name="entityId"></param> /// <returns></returns> private static EntityRef CreateEntityRefForSecurityCheck(BulkRequestResult data, long entityId) { EntityRef result; long isOfTypeRelId = WellKnownAliases.CurrentTenant.IsOfType; // Look up type information RelationshipKey key = new RelationshipKey(entityId, isOfTypeRelId); List <long> typeIds; if (data.Relationships.TryGetValue(key, out typeIds)) { IEntity entity = new EntityTypeOnly(entityId, typeIds); result = new EntityRef(entity); } else { result = new EntityRef(entityId); } return(result); }
/// <summary> /// Remove duplicate entries from relationship lists. /// (that could result if two different nodes point to the same node along the same relationship) /// </summary> /// <param name="result"></param> private static void RemoveDuplicateRelationshipEntries(BulkRequestResult result) { // Note: this is a bit messy // Ideally we could remove the duplicates during the main ReadRelationships loop. // However, I don't want to return the result sorted, as it will unnecessarily slow down the SQL. // And I don't want to store individual HashSets in the object if I can help it. foreach (var pair in result.Relationships) { RelationshipKey key = pair.Key; List <long> targetEntityIds = pair.Value; // Only consolidate if there are multiple entries // (And do so, without recreating the list, otherwise we'll break our enumeration) if (targetEntityIds.Count > 1) { HashSet <long> distinct = new HashSet <long>(targetEntityIds); targetEntityIds.Clear( ); targetEntityIds.AddRange(distinct); } } }
/// <summary> /// Read relationships, and top level entities, from the database result. /// </summary> private static void ReadRelationships(IDataReader reader, BulkSqlQuery query, BulkRequestResult result) { // select distinct EntityId, RelSrcId, RelTypeId from #process while (reader.Read()) { // Caution: We're in a database-read context, so don't touch the entity model or things will crash. long toId = reader.GetInt64(0); int nodeTag = reader.GetInt32(1); // tag of the request node that returned this entity long fromId = reader.GetInt64(2); // zero for root-level entities long typeIdWithNeg = reader.GetInt64(3); // relationship type-id, with reverse being indicated with negative values // Root result entity if (fromId == 0) { result.RootEntities.Add(toId); } else { // Add to dictionary var key = new RelationshipKey(fromId, typeIdWithNeg); var value = toId; List <long> list; if (!result.Relationships.TryGetValue(key, out list)) { list = new List <long>(); result.Relationships[key] = list; } list.Add(value); } // Implicit relationship security bool implicitlySecured = false; if (fromId != 0) { var relInfo = query.Relationships[typeIdWithNeg]; implicitlySecured = relInfo.ImpliesSecurity; } // Store entity EntityValue ev; if (!result.AllEntities.TryGetValue(toId, out ev)) { ev = new EntityValue { ImplicitlySecured = implicitlySecured }; result.AllEntities[toId] = ev; } else { ev.ImplicitlySecured = ev.ImplicitlySecured && implicitlySecured; } // Store the request node that specified members to load for this entity RequestNodeInfo requestNode = query.RequestNodes [nodeTag]; ev.Nodes.Add(requestNode); } #if DEBUG if (result.RootEntitiesList.Count != 0) { throw new InvalidOperationException("Assert false .. expected RootEntityList to be empty."); } #endif result.RootEntitiesList.AddRange(result.RootEntities); RemoveDuplicateRelationshipEntries(result); }