/// <summary> /// Loads structured data for the specified entity. /// </summary> /// <param name="entityId">The entity to load.</param> /// <param name="request">The description of fields and related entities to load.</param> /// <returns>The requested data. Or null if the entity does not exist.</returns> /// <exception cref="PlatformSecurityException"> /// The user lacks read access to the given entity. /// </exception> public EntityData GetEntityData(EntityRef entityId, EntityMemberRequest request) { try { // Validate input if (entityId == null) { throw new ArgumentNullException("entityId"); } try { long id = entityId.Id; // ensure we can resolve any alias } catch (ArgumentException) { return(null); // alias does not exist .. return null } request = ValidateRequest(request); // Place request var resultList = GetEntitiesData(entityId.ToEnumerable(), request, SecurityOption.DemandAll); var result = resultList.FirstOrDefault(); return(result); } catch (Exception ex) { EventLog.Application.WriteError("Failed GetEntityData. Entity: #{0}\nRequest details:\n{1}\n\nException:\n{2}", entityId, request == null ? "null" : request.Debug(), ex); throw; } }
/// <summary> /// Finds the named relationships. /// </summary> public static IEnumerable <RelationshipRequest> FindRelationshipRequests(this EntityMemberRequest request, EntityRef findRelationshipType) { var relRequests = Delegates.WalkGraph(request.Relationships, rel => rel.RequestedMembers.Relationships); string sFind = findRelationshipType.ToString(); return(relRequests.Where(r => r.RelationshipTypeId.ToString() == sFind)); }
/// <summary> /// Finds the members requested for the named relationship. /// </summary> public static EntityMemberRequest FindRelMembers(this EntityMemberRequest request, EntityRef findRelationshipType) { var rel = FindRel(request, findRelationshipType); if (rel == null) { return(null); } return(rel.RequestedMembers); }
/// <summary> /// Convert the list of Entities into EntityData populated with the provided query /// </summary> /// <param name="entities">Teh entities to convert</param> /// <param name="query">Additional information to collect</param> /// <returns></returns> public static IEnumerable <EntityData> ToEntityData(IEnumerable <IEntity> entities, string query) { EntityInfoService service = new EntityInfoService(); EntityMemberRequest rq = Factory.RequestParser.ParseRequestQuery(query); IEnumerable <EntityRef> entityIds = entities.Select(e => new EntityRef(e.Id)); IEnumerable <EntityData> entityDataList = service.GetEntitiesData(entityIds, rq); return(entityDataList); }
/// <summary> /// Validates the member request. Note: null requests are allowed. /// </summary> /// <param name="request">The request.</param> /// <param name="validatedEntityMemberRequests"> /// Requests that have already been validated. Used for cycle detection. /// </param> private static EntityMemberRequest ValidateRequest(EntityMemberRequest request, ISet <EntityMemberRequest> validatedEntityMemberRequests = null) { if (request == null) { return(new EntityMemberRequest()); } if (validatedEntityMemberRequests == null) { validatedEntityMemberRequests = new HashSet <EntityMemberRequest>(); } validatedEntityMemberRequests.Add(request); foreach (var fieldRequest in request.Fields) { if (fieldRequest == null) { throw new ArgumentException("One or more requested field entries was null.", "request"); } if (fieldRequest.Id == 0) { throw new ArgumentException("One or more requested field entries could not be resolved.", "request"); } } foreach (var relRequest in request.Relationships) { if (relRequest == null) { throw new ArgumentException("One or more requested relationship entries was null.", "request"); } if (relRequest.RelationshipTypeId == null) { throw new ArgumentException("One or more requested relationship entries had a null RelationshipTypeId.", "request"); } if (relRequest.RelationshipTypeId.Id == 0) { throw new ArgumentException("One or more requested relationship entries could not be resolved.", "request"); } if (!validatedEntityMemberRequests.Contains(relRequest.RequestedMembers)) { relRequest.RequestedMembers = ValidateRequest(relRequest.RequestedMembers, validatedEntityMemberRequests); } } return(request); }
/// <summary> /// Visits each entity and adds it to the set. /// </summary> /// <param name="request">The request.</param> /// <param name="entities">The entities.</param> public IEnumerable <EntityData> PackageEntities(EntityMemberRequest request, IEnumerable <TEntity> entities) { if (request == null) { throw new ArgumentNullException("request"); } if (entities == null) { throw new ArgumentNullException("entities"); } Context context = new Context(); var result = PackageEntities(context, request, entities); return(result); }
/// <summary> /// Populates the field data for an EntityData. /// </summary> /// <param name="context">Overall request context.</param> /// <param name="request">Current request node to be loaded.</param> /// <param name="entity">Source entity to get the field data from.</param> /// <param name="entityData">Target entityData to store field data into.</param> /// <param name="isNew">Flag indicating that EntityData is fresh, so we don't need to worry about checking if data is already loaded.</param> private void PackageEntityFields(Context context, EntityMemberRequest request, TEntity entity, EntityData entityData, bool isNew) { // Overall process: // - determine list of fields to load for this request // - determine if a particular field has already been loaded // - get the fields from entity // - store the field in entityData // Determine fields to load IEnumerable <IEntityRef> fields; if (request.AllFields) { fields = GetAllFields(entity); } else { fields = request.Fields; } // Prepare container if (entityData.Fields == null) { entityData.Fields = new List <FieldData>(); } // Translate fields foreach (EntityRef fieldId in fields) { // Check if already loaded if (!isNew && entityData.Fields.Any(f => f.FieldId.Id == fieldId.Id)) { continue; } if (!IsFieldApplicable(fieldId, entity)) { continue; } // And store it in the result var fieldData = new FieldData(); fieldData.FieldId = fieldId; fieldData.Value = GetFieldValue(entity, fieldId); entityData.Fields.Add(fieldData); } }
/// <summary> /// Finds the relationship request by type name. /// </summary> public static RelationshipRequest FindRel(this EntityMemberRequest request, EntityRef findRelationshipType) { if (findRelationshipType == null) { return(null); } if (request == null) { return(null); } if (request.Relationships == null) { return(null); } string sFind = findRelationshipType.ToString(); return(request.Relationships.FirstOrDefault(r => r.RelationshipTypeId.ToString() == sFind)); }
public static void GetDebug(EntityMemberRequest request, StringBuilder sb, string indent, HashSet <EntityMemberRequest> visited) { if (visited.Contains(request)) { sb.AppendFormat("{0}Cycle detected", indent); return; } visited.Add(request); if (request.Fields != null) { foreach (var field in request.Fields) { if (field == null) { sb.AppendFormat("{0}Field: null\n", indent); } else { sb.AppendFormat("{0}Field: {1}\n", indent, field); } } } if (request.Relationships != null) { foreach (var rel in request.Relationships) { if (rel == null) { sb.AppendFormat("{0}Rel: null\n", indent); } else { sb.AppendFormat("{0}Rel: {1}{2}{3}\n", indent, rel.RelationshipTypeId, rel.IsReverse ? " -rev" : "", rel.IsRecursive ? " *rec" : ""); if (rel.RequestedMembers != null) { EntityRequestHelper.GetDebug(rel.RequestedMembers, sb, indent + " ", visited); } } } } }
/// <summary> /// Validates the member request. Note: null requests are allowed. /// </summary> /// <param name="request">The request.</param> private static EntityMemberRequest ValidateRequest(EntityMemberRequest request) { if (request == null) { return(new EntityMemberRequest()); } foreach (var fieldRequest in request.Fields) { if (fieldRequest == null) { throw new ArgumentException("One or more requested field entries was null.", "request"); } if (fieldRequest.Id == 0) { throw new ArgumentException("One or more requested field entries could not be resolved.", "request"); } } foreach (var relRequest in request.Relationships) { if (relRequest == null) { throw new ArgumentException("One or more requested relationship entries was null.", "request"); } if (relRequest.RelationshipTypeId == null) { throw new ArgumentException("One or more requested relationship entries had a null RelationshipTypeId.", "request"); } if (relRequest.RelationshipTypeId.Id == 0) { throw new ArgumentException("One or more requested relationship entries could not be resolved.", "request"); } relRequest.RequestedMembers = ValidateRequest(relRequest.RequestedMembers); } return(request); }
/// <summary> /// Gets a cache key for the request. /// </summary> public static string GetCacheKey(this EntityMemberRequest request) { if (request == null) { throw new ArgumentNullException("request"); } if (request.CacheKey == null) { if (!string.IsNullOrEmpty(request.RequestString)) { request.CacheKey = request.RequestString; } else { // This will at least allow caching if object reference matches request.CacheKey = Guid.NewGuid().ToString(); } } return(request.CacheKey); }
/// <summary> /// Determines the set of fields and relationships required for a particular request node. /// </summary> /// <param name="request">The request.</param> /// <param name="context">The context.</param> /// <returns></returns> private static IEntityRef[] RequestedMembers(EntityMemberRequest request, Context context) { // TODO: it'd be nice if we could somehow detect that request.AllFields is set at this point, then pass something back in the array. // Problem is that the total set of fields varies from instance to instance as instances may be of multiple types. IEntityRef[] result; var cache = context.RequestMembers; // Check if cached if (!cache.TryGetValue(request, out result)) { // Get list of fields and relationships result = request.Fields // .Concat(request.Relationships.Select(r => r.RelationshipTypeId)) // TODO: preloading relationships doesn't seem to be working at the moment .ToArray(); // Cache cache.Add(request, result); } return(result); }
/// <summary> /// Runs a query and returns all entities that match the query. /// </summary> /// <param name="query">The query to execute. Do not specify columns or ordering. Only specify conditions and a root entity (and any joins required to apply the condition).</param> /// <param name="request">The description of fields and related entities to load.</param> /// <returns>The requested data for all entities matched by the query.</returns> public IEnumerable <EntityData> QueryEntityData(StructuredQuery query, EntityMemberRequest request) { try { // Validate input if (query == null) { throw new ArgumentNullException("query"); } if (query.SelectColumns.Count > 0) { throw new ArgumentException("query.SelectColumns must be empty", "query"); } request = ValidateRequest(request); // Create context Context context = new Context(); // Query for top-level entities IEntityRef[] members = RequestedMembers(request, context); IEnumerable <IEntity> entities = Entity.GetMatches(query, members); // Fill data IEnumerable <EntityData> result = PackageEntities(context, request, entities, true); var list = result.Where(e => e != null).ToList(); return(list); } catch (Exception ex) { string xml = ""; try { xml = EDC.ReadiNow.Metadata.Query.Structured.StructuredQueryHelper.ToXml(query); } catch { } EventLog.Application.WriteError("Failed QueryEntityData:\nRequest:\n{0}\n\nQuery:\n{1}\n\nException:\n{2}", request == null ? "null" : request.Debug(), xml, ex); throw; } }
/// <summary> /// Produce a debug string for the request. /// </summary> /// <param name="request">The request</param> /// <returns>Diagnostic text.</returns> public static string Debug(this EntityMemberRequest request) { if (request == null) { throw new ArgumentNullException("request"); } try { var sb = new StringBuilder(); var visited = new HashSet <EntityMemberRequest>(); GetDebug(request, sb, "", visited); string result = sb.ToString(); if (result.Length > 10000) { result = result.Substring(0, 100000) + " ...truncated at 10k"; } return(result); } catch (Exception ex) { return(ex.ToString()); } }
/// <summary> /// Populates the relationships for an EntityData. Recursively load entities. /// </summary> /// <param name="context">Overall request context.</param> /// <param name="request">Current request node to be loaded.</param> /// <param name="entity">Source entity to get the field data from.</param> /// <param name="entityData">Target entityData to store field data into.</param> /// <param name="isNew">Flag indicating that EntityData is fresh, so we don't need to worry about checking if data is already loaded.</param> private void PackageEntityRelationships(Context context, EntityMemberRequest request, IEntity entity, EntityData entityData, bool isNew) { // Overall process: // - determine list of fields to load for this request // - determine if a particular field has already been loaded // - get the fields from entity // - store the field in entityData // Prepare container if (isNew) { entityData.Relationships = new List <RelationshipData>(); } // Translate relationships foreach (RelationshipRequest relReq in request.Relationships) { // Establish relationship identity and direction IEntity relationshipDefn = relReq.RelationshipTypeId.Entity; Direction direction = GetDirection(relReq.RelationshipTypeId, relReq.IsReverse); // Create object to represent results for this relationship type // Note: immediately register the relationship, as this method is re-entrant. var relData = new RelationshipData(); relData.RelationshipTypeId = relReq.RelationshipTypeId; relData.IsReverse = relReq.IsReverse; relData.IsReverseActual = direction == Direction.Reverse; var relEntity = relationshipDefn.As <Relationship>(); if (relEntity != null && relEntity.Cardinality != null && relEntity.Cardinality.Alias != null) { relData.IsLookup = relEntity.IsLookup(direction); } // Hmm.. we may be processing the same relationship twice, via different member requests. // We need to do this so that any additional members get loaded on the related entities, but we don't want to // include the relationship list twice, so just remove the existing. (This is a bit hacky) entityData.Relationships.RemoveAll(r => SameRelationship(r, relData)); entityData.Relationships.Add(relData); if (relReq.MetadataOnly) { relData.Instances = new List <RelationshipInstanceData>(); } else { // Get the list of related entities from the entity model var relationshipsRaw = Entity.GetRelationships(entity, relationshipDefn, direction); // Throttle list size var relationships = relationshipsRaw.Take(FanoutHelper.MaxRelatedEntities + 1).ToList(); FanoutHelper.CheckFanoutLimit(relReq, entityData.Id.Id, relationships.Count); var entityIds = relationships.Where(p => p != null).Select(p => new EntityRef(p.Key)); var entityList = Entity.Get(entityIds, false, true, relReq.RequestedMembers.Fields.Cast <IEntityRef>().ToArray()) .ToList(); // Package related entities and relationship instances var entities = PackageEntities(context, relReq.RequestedMembers, entityList, false); // Convert results into RelationshipInstanceData list relData.Instances = entities.Select(relatedEntity => new RelationshipInstanceData() { Entity = relatedEntity }).ToList(); // Rerun again with recursive request (note: this will fill into the existing entities) if (relReq.IsRecursive) { var tmpEnum = PackageEntities(context, request, entityList, false); ForceVisitAll(tmpEnum); } } } }
/// <summary> /// Populates the field data for an EntityData. /// </summary> /// <param name="context">Overall request context.</param> /// <param name="request">Current request node to be loaded.</param> /// <param name="entity">Source entity to get the field data from.</param> /// <param name="entityData">Target entityData to store field data into.</param> /// <param name="isNew">Flag indicating that EntityData is fresh, so we don't need to worry about checking if data is already loaded.</param> private static void PackageEntityFields(Context context, EntityMemberRequest request, IEntity entity, EntityData entityData, bool isNew) { // Overall process: // - determine list of fields to load for this request // - determine if a particular field has already been loaded // - get the fields from entity // - store the field in entityData // Determine fields to load IEnumerable <IEntityRef> fields = null; if (request.AllFields) { fields = entity.GetAllFields().Select <Field, IEntityRef>(f => new EntityRef(f, f.Alias)); } else { fields = request.Fields; } // Prepare container if (entityData.Fields == null) { entityData.Fields = new List <FieldData>(); } // Translate fields foreach (EntityRef fieldId in fields) { // Check if already loaded if (!isNew && entityData.Fields.Any(f => f.FieldId.Id == fieldId.Id)) { continue; } // Get value in field // Note: hopefully all the fields got pre-loaded object value = entity.GetField(fieldId); // Get/convert the type info & writeability for the field DatabaseType typeInfo; if (!context.FieldDatabaseTypeCache.TryGetValue(fieldId.Id, out typeInfo)) { Field field = Entity.Get <Field>(fieldId); typeInfo = field.ConvertToDatabaseType(); context.FieldDatabaseTypeCache.Add(fieldId.Id, typeInfo); if (field.IsFieldWriteOnly ?? false) { context.WriteOnlyFields.Add(fieldId.Id); } if (BulkRequests.BulkRequestHelper.IsVirtualAccessControlField(fieldId)) { context.VirtualFields.Add(fieldId.Id); } } // The field is writeonly so we set its value to null if (context.WriteOnlyFields.Contains(fieldId.Id)) { value = new WriteOnlyFieldReadValueGenerator().GenerateValue(fieldId.Id, typeInfo).Value; } else if (context.VirtualFields.Contains(fieldId.Id)) { value = BulkRequests.BulkRequestResultConverter.TryGetAccessControlField(entity.Id, fieldId).Value; } // And store it in the result var fieldData = new FieldData(); fieldData.FieldId = fieldId; fieldData.Value = new TypedValue() { Type = typeInfo, Value = value }; entityData.Fields.Add(fieldData); } }
/// <summary> /// Visits each entity and adds it to the set. /// </summary> /// <param name="context">The context of the overall query, including the resulting EntitySet. This cannot be null.</param> /// <param name="request">The request. This cannot be null.</param> /// <param name="entities">The entities. This cannot be null but may contain null.</param> /// <param name="membersPreloaded">If false, then PackageEntities will do a preload of entity fields.</param> /// <exception cref="ArgumentNullException">No argument can be null.</exception> private IEnumerable <EntityData> PackageEntities(Context context, EntityMemberRequest request, IEnumerable <IEntity> entities, bool membersPreloaded) { if (context == null) { throw new ArgumentException("context"); } if (request == null) { throw new ArgumentException("request"); } if (entities == null) { throw new ArgumentException("entities"); } // Note: it is an intentional behavior that the result set must match the input set item-for-item, including nulls, // because PackageEntityRelationships needs to call this twice, once for entities, and once for relationship instances, and then // stitch the two back together. Therefore, no 'continue' without yielding a value, even if its null. // Bulk pre-load field data //if (!membersPreloaded && entities.Any( e => e != null )) //{ // IEntityRef[] members = RequestedMembers(request, context); // Entity.PreloadData(entities, members); //} // No need for a security check here - since you are already passing in IEntity objects, // they must have already been checked. //IEntityAccessControlService entityAccessControlService = new EntityAccessControlFactory().Service; //var checkedEntityIds = entityAccessControlService // .Check( // entities.Where(e => e != null).Select(e => new EntityRef(e)), // new[] { Permissions.Read }); //var filteredEntities = entities.Select(e => (e == null || !checkedEntityIds[e.Id]) ? null : e); // Translate each entity foreach (IEntity entity in entities) { // Skip nulls (but preserve symmetry in result) if (entity == null) { yield return(null); continue; } // Create or get EntityData // Note: a node may have already been processed for a different request object (i.e. visitied twice asking for different fields) // So we still need to look up existing references EntityData entityData; bool isNew = false; if (!context.LoadedEntities.TryGetValue(entity.Id, out entityData)) { // Create a new EntityData entityData = new EntityData(); entityData.Id = entity.Id; entityData.TypeIds = entity.TypeIds.Select(typeId => new EntityRef(typeId)).ToList(); // And register it into entity set context.LoadedEntities.Add(entity.Id, entityData); isNew = true; } // Determine if this entity has already been processed for this member request var hashKey = new KeyValuePair <long, EntityMemberRequest>(entity.Id, request); if (isNew || !context.Processed.Contains(hashKey)) { context.Processed.Add(hashKey); // Add field data PackageEntityFields(context, request, entity, entityData, isNew); // Add relationship data PackageEntityRelationships(context, request, entity, entityData, isNew); } yield return(entityData); } }
/// <summary> /// Loads structured data for the specified entities. /// This is an internal helper method and is not exposed as a service method. /// </summary> /// <param name="entities">The entities.</param> /// <param name="request">The description of fields and related entities to load.</param> /// <returns> /// The requested data. /// </returns> /// <exception cref="System.ArgumentNullException"> /// entities /// or /// entities;One of the entities was null. /// </exception> internal IEnumerable <EntityData> ToEntitiesData(IEnumerable <IEntity> entities, EntityMemberRequest request) { try { if (entities == null) { throw new ArgumentNullException("entities"); } if (entities.Any(e => e == null)) { throw new ArgumentNullException("entities", "One of the entities was null."); } request = ValidateRequest(request); // Create context Context context = new Context(); // Load top-level entities IEntityRef[] members = RequestedMembers(request, context); // Fill data IEnumerable <EntityData> result = PackageEntities(context, request, entities, true); var list = result.Where(e => e != null).ToList(); return(list); } catch (Exception ex) { List <EntityRef> entityRefs = new List <EntityRef>(); if (entities != null) { entityRefs = entities.Select(e => new EntityRef(e as Entity)).ToList(); } string.Join(", ", entityRefs); EventLog.Application.WriteError("Failed GetEntitiesData:\n{0}\nRequest:\n{1}\n\nException:\n{2}", entityRefs, request == null ? "null" : request.Debug(), ex); throw; } }
/// <summary> /// Loads structured data for the specified entity. /// </summary> /// <param name="entityIds">The IDs of the entities to load.</param> /// <param name="request">The description of fields and related entities to load.</param> /// <param name="securityOption">How to handle access denied situations.</param> /// <returns>The requested data.</returns> /// <exception cref="PlatformSecurityException"> /// Thrown if the user lacks read access to the given entities and <paramref name="securityOption"/> is <see cref="SecurityOption.DemandAll"/>. /// </exception> public IEnumerable <EntityData> GetEntitiesData(IEnumerable <EntityRef> entityIds, EntityMemberRequest request, SecurityOption securityOption = SecurityOption.SkipDenied) { try { if (entityIds == null) { throw new ArgumentNullException("entityIds"); } if (entityIds.Any(e => e == null)) { throw new ArgumentNullException("entityIds", "One of the entityIds was null."); } request = ValidateRequest(request); // Create context Context context = new Context(); // Load top-level entities IEntityRef[] members = RequestedMembers(request, context); IEnumerable <IEntity> entities = Entity.Get(entityIds, false, securityOption, true, members); // Fill data IEnumerable <EntityData> result = PackageEntities(context, request, entities, true); var list = result.Where(e => e != null).ToList(); return(list); } catch (Exception ex) { if (entityIds != null) { string.Join(", ", entityIds); } EventLog.Application.WriteError("Failed GetEntitiesData:\n{0}\nRequest:\n{1}\n\nException:\n{2}", entityIds, request == null ? "null" : request.Debug(), ex); throw; } }
/// <summary> /// Loads structured data for all entities of the specified type. /// </summary> /// <param name="entityType">The type of the entities to be loaded.</param> /// <param name="includeDerivedTypes">If true, then instances of types that directly or indirectly derive from 'entityType' are also returned.</param> /// <param name="request">The description of fields and related entities to load.</param> /// <returns>All instances of type 'entityType', and optionally all instances of types that derive from 'entityType'.</returns> public IEnumerable <EntityData> GetEntitiesByType(EntityRef entityType, bool includeDerivedTypes, EntityMemberRequest request) { // Note: this is only called by the legacy entity info service. try { if (entityType == null) { throw new ArgumentNullException("entityType"); } request = ValidateRequest(request); // Create context Context context = new Context(); // Load top-level entities IEntityRef[] members = RequestedMembers(request, context); IEnumerable <IEntity> entities = Entity.GetInstancesOfType(entityType, includeDerivedTypes); // Fill data IEnumerable <EntityData> result = PackageEntities(context, request, entities, true); var list = result.Where(e => e != null).ToList(); return(list); } catch (Exception ex) { EventLog.Application.WriteError("Failed GetEntitiesByType:\n{0}\nRequest:\n{1}\n\nException:\n{2}", entityType, request == null ? "null" : request.Debug(), ex); throw; } }
/// <summary> /// Default constructor. /// </summary> public RelationshipRequest() { RequestedMembers = new EntityMemberRequest(); }
/// <summary> /// Finds all nodes in the request. /// </summary> public static IEnumerable <EntityMemberRequest> WalkNodes(this EntityMemberRequest request) { var nodes = Delegates.WalkGraph(request, rq => rq.Relationships.Select(rel => rel.RequestedMembers)); return(nodes); }
/// <summary> /// Visits each entity and adds it to the set. /// </summary> /// <param name="context">The context of the overall query, including the resulting EntitySet.</param> /// <param name="request">The request.</param> /// <param name="entities">The entities.</param> private IEnumerable <EntityData> PackageEntities(Context context, EntityMemberRequest request, IEnumerable <TEntity> entities) { // Note: it is an intentional behavior that the result set must match the input set item-for-item, including nulls, // because PackageEntityRelationships needs to call this twice, once for entities, and once for relationship instances, and then // stitch the two back together. Therefore, no 'continue' without yielding a value, even if its null. // Translate each entity foreach (TEntity entity in entities) { // Skip nulls (but preserve symmetry in result) if (Equals(entity, default(TEntity))) { yield return(null); continue; } // Call security callback if (!CanAccess(entity)) { yield return(null); continue; } // Return nulls if request is null, which can happen if we encounter a non-null relationship instance with a null relationship instance request if (request == null) { yield return(null); continue; } // Create or get EntityData // Note: a node may have already been processed for a different request object (i.e. visitied twice asking for different fields) // So we still need to look up existing references bool noShare = request.DisallowInstanceReuse; Context useContext = noShare ? context.InternalContext : context; EntityData entityData = null; bool isNew = false; long id = GetId(entity); if (!useContext.LoadedEntities.TryGetValue(id, out entityData)) { // Create a new EntityData entityData = new EntityData(); entityData.Id = id; // And register it into entity set useContext.LoadedEntities.AddOrUpdate(id, entityData, (k, v) => v); isNew = true; } // Determine if this entity has already been processed for this member request var hashKey = new KeyValuePair <long, EntityMemberRequest>(id, request); if (isNew || !useContext.Processed.Contains(hashKey)) { useContext.Processed.Add(hashKey); // Add field data PackageEntityFields(context, request, entity, entityData, isNew); // Add relationship data PackageEntityRelationships(context, request, entity, entityData, isNew); } yield return(entityData); } }