/// <summary>
        /// Check whether the user has access to the entities by following relationship types where the
        /// <see cref="Relationship"/> type has the <see cref="Relationship.SecuresTo"/> or
        /// <see cref="Relationship.SecuresFrom"/> flag set.
        /// </summary>
        /// <param name="permission">
        /// The type of permission to follow relationships for.
        /// </param>
        /// <param name="user">
        /// The user to do the check access for. This cannot be null.
        /// </param>
        /// <param name="entitiesToCheck">
        /// The entities to check. This cannot be null or contain null.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// No argument can be null.
        /// </exception>
        internal ISet <EntityRef> CheckAccessControlByRelationshipType(EntityRef permission, EntityRef user, ISet <EntityRef> entitiesToCheck)
        {
            if (permission == null)
            {
                throw new ArgumentNullException(nameof(permission));
            }
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }
            if (entitiesToCheck == null)
            {
                throw new ArgumentNullException(nameof(entitiesToCheck));
            }
            ;

            IDictionary <long, ISet <EntityRef> > entitiesOfType;
            HashSet <EntityRef> result;

            result         = new HashSet <EntityRef>(EntityRefComparer.Instance);
            entitiesOfType = TypeRepository.GetEntityTypes(entitiesToCheck);
            foreach (KeyValuePair <long, ISet <EntityRef> > entitiesType in entitiesOfType)
            {
                EntityType entityType;

                entityType = Entity.Get <EntityType>(entitiesType.Key);
                if (entityType != null)
                {
                    if (CheckTypeAccess(entityType, permission, user))
                    {
                        result.UnionWith(entitiesType.Value);
                    }
                }
                else
                {
                    EventLog.Application.WriteWarning("Type ID {0} for entities '{1}' is not a type",
                                                      entitiesType.Key, string.Join(", ", entitiesType.Value));
                }
            }

            return(result);
        }
        /// <summary>
        /// Check whether the user has access to the entities by following relationships where the
        /// <see cref="Relationship"/> type has the <see cref="Relationship.SecuresTo"/> or
        /// <see cref="Relationship.SecuresFrom"/> flag set.
        /// </summary>
        /// <param name="permissions">
        /// The permissions to check for. This cannot be null or contain null.
        /// </param>
        /// <param name="user">
        /// The user to do the check access for. This cannot be null.
        /// </param>
        /// <param name="entitiesToCheck">
        /// The entities to check. This cannot be null or contain null.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// No argument can be null.
        /// </exception>
        internal ISet <EntityRef> CheckAccessControlByRelationship(IList <EntityRef> permissions, EntityRef user,
                                                                   ISet <EntityRef> entitiesToCheck)
        {
            if (permissions == null)
            {
                throw new ArgumentNullException("permissions");
            }
            if (permissions.Contains(null))
            {
                throw new ArgumentNullException("permissions", "Cannot contain null");
            }
            if (user == null)
            {
                throw new ArgumentNullException("user");
            }
            if (entitiesToCheck == null)
            {
                throw new ArgumentNullException("entitiesToCheck");
            }

            EntityMemberRequest entityMemberRequest;
            IDictionary <long, ISet <EntityRef> > entityTypes;
            IEnumerable <EntityData> entitiesData;
            IDictionary <long, bool> accessToRelatedEntities;
            HashSet <EntityRef>      result;
            IList <long>             relatedAccessibleEntities;
            EntityType entityType;

            using (Profiler.MeasureAndSuppress("SecuresFlagEntityAccessControlChecker.CheckAccessControlByRelationship"))
            {
                result = new HashSet <EntityRef>(EntityRefComparer.Instance);

                entityTypes = TypeRepository.GetEntityTypes(entitiesToCheck);
                using (MessageContext outerMessageContext = new MessageContext(EntityAccessControlService.MessageName))
                    foreach (KeyValuePair <long, ISet <EntityRef> > entitiesType in entityTypes)
                    {
                        outerMessageContext.Append(() =>
                                                   string.Format(
                                                       "Checking relationships for entity(ies) \"{0}\" of type \"{1}\":",
                                                       string.Join(", ", entitiesType.Value.Select(er => string.Format("'{0}' ({1})", er.Entity.As <Resource>().Name, er.Id))),
                                                       string.Join(", ", string.Format("'{0}' ({1})", Entity.Get <Resource>(entitiesType.Key).Name, entitiesType.Key))));

                        using (MessageContext innerMessageContext = new MessageContext(EntityAccessControlService.MessageName))
                        {
                            entityType = Entity.Get <EntityType>(entitiesType.Key);
                            if (entityType != null)
                            {
                                entityMemberRequest = EntityMemberRequestFactory.BuildEntityMemberRequest(entityType, permissions);
                                if (entityMemberRequest.Relationships.Count > 0)
                                {
                                    IList <EntityRef> relatedEntitiesToCheck;

                                    innerMessageContext.Append(() => string.Format("Security relationship structure for entity type '{0}':", entityType.Id));
                                    TraceEntityMemberRequest(entityMemberRequest);

                                    // Get the IDs of entities to check security for
                                    EntityRequest request = new EntityRequest
                                    {
                                        Entities          = entitiesType.Value,
                                        Request           = entityMemberRequest,
                                        IgnoreResultCache = true    // security engine does its own result caching
                                    };
                                    entitiesData = BulkRequestRunner.GetEntitiesData(request).ToList( );

                                    // Do a single security check for all entities related to
                                    // the entities passed in, excluding the original entities
                                    // that failed the security check.
                                    relatedEntitiesToCheck = Delegates
                                                             .WalkGraph(
                                        entitiesData,
                                        entityData =>
                                        entityData.Relationships.SelectMany(relType => relType.Entities))
                                                             .Select(ed => ed.Id)
                                                             .Where(er => !entitiesType.Value.Contains(er, EntityRefComparer.Instance))
                                                             .ToList();
                                    if (relatedEntitiesToCheck.Count > 0)
                                    {
                                        // Add the relationship types to watch for cache invalidations
                                        using (CacheContext cacheContext = CacheContext.GetContext())
                                        {
                                            IList <long> relationshipTypes = Delegates
                                                                             .WalkGraph(entityMemberRequest.Relationships,
                                                                                        rr => rr.RequestedMembers.Relationships)
                                                                             .Select(rr => rr.RelationshipTypeId.Id)
                                                                             .ToList();

                                            cacheContext.RelationshipTypes.Add(relationshipTypes);
                                        }

                                        // ReSharper disable AccessToModifiedClosure
                                        // Do a single access check for all entities for efficiency, then pick the
                                        // important ones for each requested entity below.
                                        accessToRelatedEntities = null;
                                        innerMessageContext.Append(
                                            () => string.Format(
                                                "Checking related entities '{0}':",
                                                string.Join(", ", relatedEntitiesToCheck.Select(er => er.Id))));
                                        SecurityBypassContext.RunAsUser(
                                            () =>
                                            accessToRelatedEntities =
                                                Checker.CheckAccess(relatedEntitiesToCheck, permissions, user));
                                        // ReSharper restore AccessToModifiedClosure

                                        foreach (EntityData entityData in entitiesData)
                                        {
                                            // Get the related entities to check
                                            relatedEntitiesToCheck = Delegates.WalkGraph(
                                                entityData,
                                                ed => ed.Relationships.SelectMany(relType => relType.Entities))
                                                                     .Select(ed => ed.Id)
                                                                     .ToList();

                                            // Remove the start entity for the query, since security has
                                            // already been checked on it.
                                            relatedEntitiesToCheck.Remove(entityData.Id);

                                            // Get the related entities the user has access to
                                            relatedAccessibleEntities = accessToRelatedEntities
                                                                        .Where(kvp => kvp.Value && relatedEntitiesToCheck.Contains(kvp.Key, EntityRefComparer.Instance))
                                                                        .Select(kvp => kvp.Key)
                                                                        .ToList();

                                            // Grant access if the user has access to ANY of the related
                                            // entities.
                                            if (relatedEntitiesToCheck.Count > 0 &&
                                                relatedAccessibleEntities.Count > 0)
                                            {
                                                result.Add(entityData.Id);

                                                // ReSharper disable AccessToModifiedClosure
                                                innerMessageContext.Append(
                                                    () => string.Format(
                                                        "Access to '{0}' granted due to corresponding access to '{1}'",
                                                        string.Join(", ", relatedEntitiesToCheck.Select(id => string.Format("'{0}' ({1})", Entity.Get <Resource>(id).Name, id))),
                                                        string.Join(", ", relatedAccessibleEntities.Select(id => string.Format("'{0}' ({1})", Entity.Get <Resource>(id).Name, id)))));
                                                // ReSharper restore AccessToModifiedClosure
                                            }
                                        }
                                    }
                                }
                                else
                                {
                                    innerMessageContext.Append(() => string.Format("No relationships found to check for entity type '{0}'.", entityType.Id));
                                }
                            }
                            else
                            {
                                EventLog.Application.WriteWarning("Type ID {0} for entities '{1}' is not a type",
                                                                  entitiesType.Key, string.Join(", ", entitiesType.Value));
                            }
                        }
                    }
            }

            return(result);
        }