/// <summary> /// Gets the expression. /// </summary> /// <param name="dataViewEntityTypeType">Type of the data view entity type.</param> /// <param name="serviceInstance">The service instance.</param> /// <param name="parameter">The parameter.</param> /// <param name="dataViewFilterOverrides">The data view filter overrides.</param> /// <returns></returns> /// <exception cref="Rock.Reporting.RockReportingException"> /// EntityTypeId not defined for {this} /// or /// Unable to determine EntityType not defined for EntityTypeId {EntityTypeId} /// or /// Unable to determine Component for EntityType {entityType.Name} /// or /// unable to determine expression for {filter} /// or /// Unexpected FilterExpressionType {ExpressionType} /// </exception> public Expression GetExpression(Type dataViewEntityTypeType, IService serviceInstance, ParameterExpression parameter, DataViewFilterOverrides dataViewFilterOverrides) { switch (ExpressionType) { case FilterExpressionType.Filter: if (!this.EntityTypeId.HasValue) { // if this happens, we want to throw an exception to prevent incorrect results throw new RockDataViewFilterExpressionException(this, $"EntityTypeId not defined for {this}"); } var entityType = EntityTypeCache.Get(this.EntityTypeId.Value); if (entityType == null) { // if this happens, we want to throw an exception to prevent incorrect results throw new RockDataViewFilterExpressionException(this, $"Unable to determine EntityType not defined for EntityTypeId {EntityTypeId}"); } var component = Rock.Reporting.DataFilterContainer.GetComponent(entityType.Name); if (component == null) { // if this happens, we want to throw an exception to prevent incorrect results throw new RockDataViewFilterExpressionException(this, $"Unable to determine Component for EntityType {entityType.Name}"); } string selection; // A formatted string representing the filter settings: FieldName, <see cref="ComparisonType">Comparison Type</see>, (optional) Comparison Value(s) var dataViewFilterOverride = dataViewFilterOverrides?.GetOverride(this.Guid); if (dataViewFilterOverride != null) { if (dataViewFilterOverride.IncludeFilter == false) { /* * 1/15/2021 - Shaun * This should not assume that returning Expression.Constant( true ) is equivalent to not filtering as this predicate * may be joined to other predicates and the AND/OR logic may result in an inappropriate filter. Instead, we simply * return null and allow the caller to handle this in a manner appropriate to the given filter. */ // If the dataview filter should not be included, don't have this filter filter anything. return(null); } else { selection = dataViewFilterOverride.Selection; } } else { selection = this.Selection; } Expression expression; try { if (component is IDataFilterWithOverrides) { expression = (component as IDataFilterWithOverrides).GetExpressionWithOverrides(dataViewEntityTypeType, serviceInstance, parameter, dataViewFilterOverrides, selection); } else { expression = component.GetExpression(dataViewEntityTypeType, serviceInstance, parameter, selection); } } catch (RockDataViewFilterExpressionException dex) { // components don't know which DataView/DataFilter they are working with, so if there was a RockDataViewFilterExpressionException, let's tell it what DataViewFilter/DataView it was using dex.SetDataFilterIfNotSet(this); throw; } if (expression == null) { // If a DataFilter component returned a null expression, that probably means that it decided not to filter anything. So, we'll interpret that as "Don't Filter" expression = Expression.Constant(true); } return(expression); case FilterExpressionType.GroupAll: case FilterExpressionType.GroupAnyFalse: Expression andExp = null; foreach (var filter in this.ChildFilters) { Expression exp = filter.GetExpression(dataViewEntityTypeType, serviceInstance, parameter, dataViewFilterOverrides); if (exp == null) { // If a DataFilter component returned a null expression, that probably means that it decided not to filter anything. So, we'll interpret that as "Don't Filter" exp = Expression.Constant(true); } if (andExp == null) { andExp = exp; } else { andExp = Expression.AndAlso(andExp, exp); } } if (ExpressionType == FilterExpressionType.GroupAnyFalse && andExp != null) { // If only one of the conditions must be false, invert the expression so that it becomes the logical equivalent of "NOT ALL". andExp = Expression.Not(andExp); } if (andExp == null) { // If there aren't any child filters for a GroupAll/GroupAnyFalse. That is OK, so just don't filter anything. return(Expression.Constant(true)); } return(andExp); case FilterExpressionType.GroupAny: case FilterExpressionType.GroupAllFalse: Expression orExp = null; foreach (DataViewFilter filter in this.ChildFilters) { Expression exp = filter.GetExpression(dataViewEntityTypeType, serviceInstance, parameter, dataViewFilterOverrides); if (exp == null) { /* * 1/15/2021 - Shaun * Filter expressions of these types (GroupAny/GroupAllFalse) are joined with an OR clause, * so they must either be defaulted to false or excluded from the where expression altogether * (otherwise they will return every Person record in the database, because a "True OrElse * <anything>" predicate will always be true). * * Therefore, if this child filter is null, we can simply ignore it and move on to the next one. * * Reason: Correcting behavior of dynamic reports where a group is deselected at run time. */ continue; } if (orExp == null) { orExp = exp; } else { orExp = Expression.OrElse(orExp, exp); } } if (ExpressionType == FilterExpressionType.GroupAllFalse && orExp != null) { // If all of the conditions must be false, invert the expression so that it becomes the logical equivalent of "NOT ANY". orExp = Expression.Not(orExp); } if (orExp == null) { // If there aren't any child filters for a GroupAny/GroupAllFalse. That is OK, so just don't filter anything. return(Expression.Constant(true)); } return(orExp); default: throw new RockDataViewFilterExpressionException(this, $"Unexpected FilterExpressionType {ExpressionType} "); } }
/// <summary> /// Gets the Linq expression for the DataViewFilter. /// </summary> /// <param name="filteredEntityType">Type of the filtered entity.</param> /// <param name="serviceInstance">The service instance.</param> /// <param name="parameter">The parameter.</param> /// <param name="dataViewFilterOverrides">The data view filter overrides.</param> /// <param name="errorMessages">The error messages.</param> /// <returns></returns> public virtual Expression GetExpression(Type filteredEntityType, IService serviceInstance, ParameterExpression parameter, DataViewFilterOverrides dataViewFilterOverrides, List <string> errorMessages) { switch (ExpressionType) { case FilterExpressionType.Filter: if (this.EntityTypeId.HasValue) { var entityType = EntityTypeCache.Get(this.EntityTypeId.Value); if (entityType != null) { var component = Rock.Reporting.DataFilterContainer.GetComponent(entityType.Name); if (component != null) { try { string selection; // A formatted string representing the filter settings: FieldName, <see cref="ComparisonType">Comparison Type</see>, (optional) Comparison Value(s) var dataViewFilterOverride = dataViewFilterOverrides?.GetOverride(this.Guid); if (dataViewFilterOverride != null) { if (dataViewFilterOverride.IncludeFilter == false) { return(null); } else { selection = dataViewFilterOverride.Selection; } } else { selection = this.Selection; } if (component is IDataFilterWithOverrides) { return((component as IDataFilterWithOverrides).GetExpressionWithOverrides(filteredEntityType, serviceInstance, parameter, dataViewFilterOverrides, selection)); } else { return(component.GetExpression(filteredEntityType, serviceInstance, parameter, selection)); } } catch (SystemException ex) { ExceptionLogService.LogException(ex, System.Web.HttpContext.Current); errorMessages.Add(string.Format("{0}: {1}", component.FormatSelection(filteredEntityType, this.Selection), ex.Message)); } } } } return(null); case FilterExpressionType.GroupAll: case FilterExpressionType.GroupAnyFalse: Expression andExp = null; foreach (var filter in this.ChildFilters) { Expression exp = filter.GetExpression(filteredEntityType, serviceInstance, parameter, dataViewFilterOverrides, errorMessages); if (exp != null) { if (andExp == null) { andExp = exp; } else { andExp = Expression.AndAlso(andExp, exp); } } } if (ExpressionType == FilterExpressionType.GroupAnyFalse && andExp != null) { // If only one of the conditions must be false, invert the expression so that it becomes the logical equivalent of "NOT ALL". andExp = Expression.Not(andExp); } return(andExp); case FilterExpressionType.GroupAny: case FilterExpressionType.GroupAllFalse: Expression orExp = null; foreach (var filter in this.ChildFilters) { Expression exp = filter.GetExpression(filteredEntityType, serviceInstance, parameter, dataViewFilterOverrides, errorMessages); if (exp != null) { if (orExp == null) { orExp = exp; } else { orExp = Expression.OrElse(orExp, exp); } } } if (ExpressionType == FilterExpressionType.GroupAllFalse && orExp != null) { // If all of the conditions must be false, invert the expression so that it becomes the logical equivalent of "NOT ANY". orExp = Expression.Not(orExp); } return(orExp); } return(null); }