/// <summary>
        /// Gets the data views referenced by this data view's filters.
        /// </summary>
        /// <param name="dataViewId">The data view identifier.</param>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        public List <DataView> GetReferencedDataViews(int dataViewId, RockContext context)
        {
            var dataViewFilterService = new DataViewFilterService(context);

            var relatedDataViews = GetDistinctRelatedDataViews(dataViewId, dataViewFilterService)
                                   .ToDictionary(dvf => dvf.RelatedDataView.Id, dvf => dvf.RelatedDataView);

            var relatedDataViewIds = relatedDataViews.Keys.ToList();

            for (var i = 0; i < relatedDataViewIds.Count; i++)
            {
                var key             = relatedDataViewIds[i];
                var relatedDataView = relatedDataViews[key];

                var relatedChildDataViews = GetDistinctRelatedDataViews(dataViewId, dataViewFilterService)
                                            .Select(dvf => dvf.RelatedDataView)
                                            .ToList();

                foreach (var dv in relatedChildDataViews)
                {
                    if (relatedDataViewIds.Contains(dv.Id))
                    {
                        continue;
                    }

                    relatedDataViewIds.Add(dv.Id);
                    relatedDataViews[dv.Id] = dv;
                }
            }

            return(relatedDataViews.Values.ToList());
        }
 private IEnumerable <DataViewFilter> GetDistinctRelatedDataViews(int dataViewId, DataViewFilterService dataViewFilterService)
 {
     return(dataViewFilterService
            .Queryable()
            .Where(dvf => dvf.DataViewId != null && dvf.DataViewId == dataViewId && dvf.RelatedDataViewId != null)
            .Include("RelatedDataView")
            .DistinctBy(dvf => dvf.RelatedDataView.Id));
 }
        /// <summary>
        /// Gets the group placement registrants.
        /// </summary>
        /// <param name="options">The options.</param>
        /// <param name="currentPerson">The current person.</param>
        /// <returns></returns>
        public List <GroupPlacementRegistrant> GetGroupPlacementRegistrants(GetGroupPlacementRegistrantsParameters options, Person currentPerson)
        {
            var rockContext = this.Context as RockContext;

            var registrationRegistrantService = new RegistrationRegistrantService(rockContext);
            var registrationRegistrantQuery   = registrationRegistrantService.Queryable();

            registrationRegistrantQuery = registrationRegistrantQuery
                                          .Where(a => a.Registration.RegistrationInstance.RegistrationTemplateId == options.RegistrationTemplateId);

            if (options.RegistrationInstanceId.HasValue)
            {
                registrationRegistrantQuery = registrationRegistrantQuery.Where(a => a.Registration.RegistrationInstanceId == options.RegistrationInstanceId.Value);
            }
            else if (options.RegistrationTemplateInstanceIds?.Any() == true)
            {
                registrationRegistrantQuery = registrationRegistrantQuery.Where(a => options.RegistrationTemplateInstanceIds.Contains(a.Registration.RegistrationInstanceId));
            }

            if (options.RegistrantPersonDataViewFilterId.HasValue)
            {
                var           dataFilter    = new DataViewFilterService(rockContext).Get(options.RegistrantPersonDataViewFilterId.Value);
                List <string> errorMessages = new List <string>();

                var personService   = new PersonService(rockContext);
                var paramExpression = personService.ParameterExpression;

                var personWhereExpression = dataFilter?.GetExpression(typeof(Person), personService, paramExpression, errorMessages);
                if (personWhereExpression != null)
                {
                    var personIdQry = personService.Queryable().Where(paramExpression, personWhereExpression, null).Select(x => x.Id);
                    registrationRegistrantQuery = registrationRegistrantQuery.Where(a => personIdQry.Contains(a.PersonAlias.PersonId));
                }
            }

            if (options.RegistrantId.HasValue)
            {
                registrationRegistrantQuery = registrationRegistrantQuery.Where(a => a.Id == options.RegistrantId.Value);
            }

            Block registrationInstanceGroupPlacementBlock = new BlockService(rockContext).Get(options.BlockId);

            if (registrationInstanceGroupPlacementBlock != null && currentPerson != null)
            {
                const string RegistrantAttributeFilter_RegistrationInstanceId = "RegistrantAttributeFilter_RegistrationInstanceId_{0}";
                const string RegistrantAttributeFilter_RegistrationTemplateId = "RegistrantAttributeFilter_RegistrationTemplateId_{0}";
                string       userPreferenceKey;
                if (options.RegistrationInstanceId.HasValue)
                {
                    userPreferenceKey = PersonService.GetBlockUserPreferenceKeyPrefix(options.BlockId) + string.Format(RegistrantAttributeFilter_RegistrationInstanceId, options.RegistrationInstanceId);
                }
                else
                {
                    userPreferenceKey = PersonService.GetBlockUserPreferenceKeyPrefix(options.BlockId) + string.Format(RegistrantAttributeFilter_RegistrationTemplateId, options.RegistrationTemplateId);
                }

                var        attributeFilters          = PersonService.GetUserPreference(currentPerson, userPreferenceKey).FromJsonOrNull <Dictionary <int, string> >() ?? new Dictionary <int, string>();
                var        parameterExpression       = registrationRegistrantService.ParameterExpression;
                Expression registrantWhereExpression = null;
                foreach (var attributeFilter in attributeFilters)
                {
                    var attribute             = AttributeCache.Get(attributeFilter.Key);
                    var attributeFilterValues = attributeFilter.Value.FromJsonOrNull <List <string> >();
                    var entityField           = EntityHelper.GetEntityFieldForAttribute(attribute);
                    if (entityField != null && attributeFilterValues != null)
                    {
                        var attributeWhereExpression = ExpressionHelper.GetAttributeExpression(registrationRegistrantService, parameterExpression, entityField, attributeFilterValues);
                        if (registrantWhereExpression == null)
                        {
                            registrantWhereExpression = attributeWhereExpression;
                        }
                        else
                        {
                            registrantWhereExpression = Expression.AndAlso(registrantWhereExpression, attributeWhereExpression);
                        }
                    }
                }

                if (registrantWhereExpression != null)
                {
                    registrationRegistrantQuery = registrationRegistrantQuery.Where(parameterExpression, registrantWhereExpression);
                }
            }

            var registrationTemplatePlacement = new RegistrationTemplatePlacementService(rockContext).Get(options.RegistrationTemplatePlacementId);

            if (options.FilterFeeId.HasValue)
            {
                registrationRegistrantQuery = registrationRegistrantQuery.Where(a => a.Fees.Any(f => f.RegistrationTemplateFeeId == options.FilterFeeId.Value));
            }

            if (options.FilterFeeOptionIds?.Any() == true)
            {
                registrationRegistrantQuery = registrationRegistrantQuery.Where(a => a.Fees.Any(f => f.RegistrationTemplateFeeItemId.HasValue && options.FilterFeeOptionIds.Contains(f.RegistrationTemplateFeeItemId.Value)));
            }

            // don't include registrants that are on the waiting list
            registrationRegistrantQuery = registrationRegistrantQuery.Where(a => a.OnWaitList == false);

            registrationRegistrantQuery = registrationRegistrantQuery.OrderBy(a => a.PersonAlias.Person.LastName).ThenBy(a => a.PersonAlias.Person.NickName);

            var registrationTemplatePlacementService = new RegistrationTemplatePlacementService(rockContext);
            var registrationInstanceService          = new RegistrationInstanceService(rockContext);

            // get a queryable of PersonIds for the registration template shared groups so we can determine if the registrant has been placed
            var registrationTemplatePlacementGroupsPersonIdQuery = registrationTemplatePlacementService.GetRegistrationTemplatePlacementPlacementGroups(registrationTemplatePlacement).SelectMany(a => a.Members).Select(a => a.PersonId);

            // and also get a queryable of PersonIds for the registration instance placement groups so we can determine if the registrant has been placed
            IQueryable <InstancePlacementGroupPersonId> allInstancesPlacementGroupInfoQuery = null;

            if (!options.RegistrationInstanceId.HasValue && (options.RegistrationTemplateInstanceIds == null || !options.RegistrationTemplateInstanceIds.Any()))
            {
                // if neither RegistrationInstanceId or RegistrationTemplateInstanceIds was specified, use all of the RegistrationTemplates instances
                options.RegistrationTemplateInstanceIds = new RegistrationTemplateService(rockContext).GetSelect(options.RegistrationTemplateId, s => s.Instances.Select(i => i.Id)).ToArray();
            }

            if (options.RegistrationInstanceId.HasValue)
            {
                allInstancesPlacementGroupInfoQuery =
                    registrationInstanceService.GetRegistrationInstancePlacementGroups(registrationInstanceService.Get(options.RegistrationInstanceId.Value))
                    .Where(a => a.GroupTypeId == registrationTemplatePlacement.GroupTypeId)
                    .SelectMany(a => a.Members).Select(a => a.PersonId)
                    .Select(s => new InstancePlacementGroupPersonId
                {
                    PersonId = s,
                    RegistrationInstanceId = options.RegistrationInstanceId.Value
                });
            }
            else if (options.RegistrationTemplateInstanceIds?.Any() == true)
            {
                foreach (var registrationInstanceId in options.RegistrationTemplateInstanceIds)
                {
                    var instancePlacementGroupInfoQuery = registrationInstanceService.GetRegistrationInstancePlacementGroups(registrationInstanceService.Get(registrationInstanceId))
                                                          .Where(a => a.GroupTypeId == registrationTemplatePlacement.GroupTypeId)
                                                          .SelectMany(a => a.Members).Select(a => a.PersonId)
                                                          .Select(s => new InstancePlacementGroupPersonId
                    {
                        PersonId = s,
                        RegistrationInstanceId = registrationInstanceId
                    });

                    if (allInstancesPlacementGroupInfoQuery == null)
                    {
                        allInstancesPlacementGroupInfoQuery = instancePlacementGroupInfoQuery;
                    }
                    else
                    {
                        allInstancesPlacementGroupInfoQuery = allInstancesPlacementGroupInfoQuery.Union(instancePlacementGroupInfoQuery);
                    }
                }
            }

            if (allInstancesPlacementGroupInfoQuery == null)
            {
                throw new ArgumentNullException("Registration Instance(s) must be specified");
            }

            // select in a way to avoid lazy loading
            var registrationRegistrantPlacementQuery = registrationRegistrantQuery.Select(r => new
            {
                Registrant = r,
                r.PersonAlias.Person,
                r.Registration.RegistrationInstance,

                // marked as AlreadyPlacedInGroup if the Registrant is a member of any of the registrant template placement group or the registration instance placement groups
                AlreadyPlacedInGroup =
                    registrationTemplatePlacementGroupsPersonIdQuery.Contains(r.PersonAlias.PersonId) ||
                    allInstancesPlacementGroupInfoQuery.Any(x => x.RegistrationInstanceId == r.Registration.RegistrationInstanceId && x.PersonId == r.PersonAlias.PersonId)
            });

            var registrationRegistrantPlacementList = registrationRegistrantPlacementQuery.AsNoTracking().ToList();

            var groupPlacementRegistrantList = registrationRegistrantPlacementList
                                               .Select(x => new GroupPlacementRegistrant(x.Registrant, x.Person, x.AlreadyPlacedInGroup, x.RegistrationInstance, options))
                                               .ToList();

            return(groupPlacementRegistrantList.ToList());
        }