Пример #1
0
        /// <summary>
        /// Persists the DataView to the database by updating the DataViewPersistedValues for this DataView. Returns true if successful
        /// </summary>
        /// <param name="databaseTimeoutSeconds">The database timeout seconds.</param>
        public void PersistResult(int?databaseTimeoutSeconds = null)
        {
            List <string> errorMessages;
            var           dbContext = this.GetDbContext();

            DataViewFilterOverrides dataViewFilterOverrides = new DataViewFilterOverrides();

            // set an override so that the Persisted Values aren't used when rebuilding the values from the DataView Query
            dataViewFilterOverrides.IgnoreDataViewPersistedValues.Add(this.Id);

            var qry = this.GetQuery(null, dbContext, dataViewFilterOverrides, databaseTimeoutSeconds, out errorMessages);

            if (!errorMessages.Any())
            {
                RockContext rockContext = dbContext as RockContext;
                if (rockContext == null)
                {
                    rockContext = new RockContext();
                }

                rockContext.Database.CommandTimeout = databaseTimeoutSeconds;
                var savedDataViewPersistedValues = rockContext.DataViewPersistedValues.Where(a => a.DataViewId == this.Id);

                var updatedEntityIdsQry = qry.Select(a => a.Id);

                if (!(rockContext is RockContext))
                {
                    // if this DataView doesn't use a RockContext get the EntityIds into memory as as a List<int> then back into IQueryable<int> so that we aren't use multiple dbContexts
                    updatedEntityIdsQry = updatedEntityIdsQry.ToList().AsQueryable();
                }

                var persistedValuesToRemove    = savedDataViewPersistedValues.Where(a => !updatedEntityIdsQry.Any(x => x == a.EntityId));
                var persistedEntityIdsToInsert = updatedEntityIdsQry.Where(x => !savedDataViewPersistedValues.Any(a => a.EntityId == x)).ToList();

                int removeCount = persistedValuesToRemove.Count();
                if (removeCount > 0)
                {
                    // increase the batch size if there are a bunch of rows (and this is a narrow table with no references to it)
                    int?deleteBatchSize = removeCount > 50000 ? 25000 : ( int? )null;

                    int rowRemoved = rockContext.BulkDelete(persistedValuesToRemove, deleteBatchSize);
                }

                if (persistedEntityIdsToInsert.Any())
                {
                    List <DataViewPersistedValue> persistedValuesToInsert = persistedEntityIdsToInsert.OrderBy(a => a)
                                                                            .Select(a =>
                                                                                    new DataViewPersistedValue
                    {
                        DataViewId = this.Id,
                        EntityId   = a
                    }).ToList();

                    rockContext.BulkInsert(persistedValuesToInsert);
                }
            }
        }
Пример #2
0
        public IQueryable GetQueryable(Type entityType, Dictionary <int, EntityField> entityFields, Dictionary <int, AttributeCache> attributes, Dictionary <int, ReportField> selectComponents, Rock.Web.UI.Controls.SortProperty sortProperty,
                                       DataViewFilterOverrides dataViewFilterOverrides, int?databaseTimeoutSeconds, bool isCommunication, out List <string> errorMessages, out System.Data.Entity.DbContext reportDbContext)
        {
            reportDbContext = Reflection.GetDbContextForEntityType(entityType);

            ReportGetQueryableArgs reportGetQueryableArgs = new ReportGetQueryableArgs
            {
                ReportDbContext         = reportDbContext as Rock.Data.DbContext,
                EntityFields            = entityFields,
                Attributes              = attributes,
                SelectComponents        = selectComponents,
                SortProperty            = sortProperty,
                DataViewFilterOverrides = dataViewFilterOverrides,
                DatabaseTimeoutSeconds  = databaseTimeoutSeconds,
                IsCommunication         = isCommunication
            };

            errorMessages = null;


            return(GetQueryable(reportGetQueryableArgs));
        }
Пример #3
0
        /// <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} ");
            }
        }
Пример #4
0
 public virtual Expression GetExpression(Type filteredEntityType, IService serviceInstance, ParameterExpression parameter, DataViewFilterOverrides dataViewFilterOverrides, List <string> errorMessages)
 {
     return(GetExpression(filteredEntityType, serviceInstance, parameter, dataViewFilterOverrides));
 }
Пример #5
0
        /// <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);
        }
Пример #6
0
        /// <summary>
        /// Gets the queryable.
        /// </summary>
        /// <param name="entityType">Type of the entity.</param>
        /// <param name="entityFields">The entity fields.</param>
        /// <param name="attributes">The attributes.</param>
        /// <param name="selectComponents">The select components.</param>
        /// <param name="sortProperty">The sort property.</param>
        /// <param name="dataViewFilterOverrides">The data view filter overrides.</param>
        /// <param name="databaseTimeoutSeconds">The database timeout seconds.</param>
        /// <param name="isCommunication">if set to <c>true</c> [is communication].</param>
        /// <param name="errorMessages">The error messages.</param>
        /// <param name="reportDbContext">The report database context.</param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public IQueryable GetQueryable(Type entityType, Dictionary <int, EntityField> entityFields, Dictionary <int, AttributeCache> attributes, Dictionary <int, ReportField> selectComponents, Rock.Web.UI.Controls.SortProperty sortProperty, DataViewFilterOverrides dataViewFilterOverrides, int?databaseTimeoutSeconds, bool isCommunication, out List <string> errorMessages, out System.Data.Entity.DbContext reportDbContext)
        {
            errorMessages   = new List <string>();
            reportDbContext = null;

            if (entityType != null)
            {
                reportDbContext = Reflection.GetDbContextForEntityType(entityType);
                IService serviceInstance = Reflection.GetServiceForEntityType(entityType, reportDbContext);

                if (databaseTimeoutSeconds.HasValue)
                {
                    reportDbContext.Database.CommandTimeout = databaseTimeoutSeconds.Value;
                }

                if (serviceInstance != null)
                {
                    ParameterExpression paramExpression = serviceInstance.ParameterExpression;
                    MemberExpression    idExpression    = Expression.Property(paramExpression, "Id");

                    // Get AttributeValue queryable and parameter
                    var attributeValues = reportDbContext.Set <AttributeValue>();
                    ParameterExpression attributeValueParameter = Expression.Parameter(typeof(AttributeValue), "v");

                    // Create the dynamic type
                    var dynamicFields = new Dictionary <string, Type>();
                    dynamicFields.Add("Id", typeof(int));
                    foreach (var f in entityFields)
                    {
                        dynamicFields.Add(string.Format("Entity_{0}_{1}", f.Value.Name, f.Key), f.Value.PropertyType);
                    }

                    foreach (var a in attributes)
                    {
                        dynamicFields.Add(string.Format("Attribute_{0}_{1}", a.Value.Id, a.Key), a.Value.FieldType.Field.AttributeValueFieldType);
                    }

                    foreach (var reportField in selectComponents)
                    {
                        DataSelectComponent selectComponent = DataSelectContainer.GetComponent(reportField.Value.DataSelectComponentEntityType.Name);
                        if (selectComponent != null)
                        {
                            dynamicFields.Add(string.Format("Data_{0}_{1}", selectComponent.ColumnPropertyName, reportField.Key), selectComponent.ColumnFieldType);
                            var customSortProperties = selectComponent.SortProperties(reportField.Value.Selection);
                            if (customSortProperties != null)
                            {
                                foreach (var customSortProperty in customSortProperties.Split(','))
                                {
                                    if (!string.IsNullOrWhiteSpace(customSortProperty))
                                    {
                                        var customSortPropertyType = entityType.GetPropertyType(customSortProperty);
                                        dynamicFields.Add(string.Format("Sort_{0}_{1}", customSortProperty, reportField.Key), customSortPropertyType ?? typeof(string));
                                    }
                                }
                            }

                            if (isCommunication && selectComponent is IRecipientDataSelect)
                            {
                                dynamicFields.Add($"Recipient_{selectComponent.ColumnPropertyName}_{reportField.Key}", ((IRecipientDataSelect)selectComponent).RecipientColumnFieldType);
                            }
                        }
                    }

                    if (dynamicFields.Count == 0)
                    {
                        errorMessages.Add("At least one field must be defined");
                        return(null);
                    }

                    Type            dynamicType      = LinqRuntimeTypeBuilder.GetDynamicType(dynamicFields);
                    ConstructorInfo methodFromHandle = dynamicType.GetConstructor(Type.EmptyTypes);

                    // Bind the dynamic fields to their expressions
                    var bindings = new List <MemberAssignment>();
                    bindings.Add(Expression.Bind(dynamicType.GetField("id"), idExpression));

                    foreach (var f in entityFields)
                    {
                        bindings.Add(Expression.Bind(dynamicType.GetField(string.Format("entity_{0}_{1}", f.Value.Name, f.Key)), Expression.Property(paramExpression, f.Value.Name)));
                    }

                    foreach (var a in attributes)
                    {
                        bindings.Add(Expression.Bind(dynamicType.GetField(string.Format("attribute_{0}_{1}", a.Value.Id, a.Key)), GetAttributeValueExpression(attributeValues, attributeValueParameter, idExpression, a.Value.Id)));
                    }

                    foreach (var reportField in selectComponents)
                    {
                        DataSelectComponent selectComponent = DataSelectContainer.GetComponent(reportField.Value.DataSelectComponentEntityType.Name);
                        if (selectComponent != null)
                        {
                            try
                            {
                                var componentExpression = selectComponent.GetExpression(reportDbContext, idExpression, reportField.Value.Selection ?? string.Empty);
                                if (componentExpression == null)
                                {
                                    componentExpression = Expression.Constant(null, typeof(string));
                                }

                                bindings.Add(Expression.Bind(dynamicType.GetField(string.Format("data_{0}_{1}", selectComponent.ColumnPropertyName, reportField.Key)), componentExpression));

                                if (isCommunication && selectComponent is IRecipientDataSelect)
                                {
                                    var recipientPersonIdExpression = ((IRecipientDataSelect)selectComponent).GetRecipientPersonIdExpression(reportDbContext, idExpression, reportField.Value.Selection ?? string.Empty);
                                    if (recipientPersonIdExpression != null)
                                    {
                                        bindings.Add(Expression.Bind(dynamicType.GetField(string.Format("recipient_{0}_{1}", selectComponent.ColumnPropertyName, reportField.Key)), recipientPersonIdExpression));
                                    }
                                }

                                var customSortProperties = selectComponent.SortProperties(reportField.Value.Selection);
                                if (!string.IsNullOrEmpty(customSortProperties))
                                {
                                    foreach (var customSortProperty in customSortProperties.Split(','))
                                    {
                                        var        customSortPropertyParts = customSortProperty.Split('.');
                                        MemberInfo memberInfo       = dynamicType.GetField(string.Format("sort_{0}_{1}", customSortProperty, reportField.Key));
                                        Expression memberExpression = null;
                                        foreach (var customSortPropertyPart in customSortPropertyParts)
                                        {
                                            memberExpression = Expression.Property(memberExpression ?? paramExpression, customSortPropertyPart);
                                        }

                                        bindings.Add(Expression.Bind(memberInfo, memberExpression));
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                throw new Exception(string.Format("Exception in {0}", selectComponent), ex);
                            }
                        }
                    }

                    ConstructorInfo      constructorInfo      = dynamicType.GetConstructor(Type.EmptyTypes);
                    NewExpression        newExpression        = Expression.New(constructorInfo);
                    MemberInitExpression memberInitExpression = Expression.MemberInit(newExpression, bindings);
                    LambdaExpression     selector             = Expression.Lambda(memberInitExpression, paramExpression);

                    // NOTE: having a NULL Dataview is OK.
                    Expression whereExpression = null;
                    if (this.DataView != null)
                    {
                        whereExpression = this.DataView.GetExpression(serviceInstance, paramExpression, dataViewFilterOverrides, out errorMessages);
                    }

                    MethodInfo getMethod = serviceInstance.GetType().GetMethod("Get", new Type[] { typeof(ParameterExpression), typeof(Expression), typeof(Rock.Web.UI.Controls.SortProperty), typeof(int?) });
                    if (getMethod != null)
                    {
                        var getResult     = getMethod.Invoke(serviceInstance, new object[] { paramExpression, whereExpression, null, null });
                        var qry           = getResult as IQueryable <IEntity>;
                        var qryExpression = qry.Expression;

                        // apply the OrderBy clauses to the Expression from whatever columns are specified in sortProperty.Property
                        string orderByMethod = "OrderBy";
                        if (sortProperty == null)
                        {
                            // if no sorting was specified, sort by Id
                            sortProperty = new Web.UI.Controls.SortProperty {
                                Direction = SortDirection.Ascending, Property = "Id"
                            };
                        }

                        /*
                         * NOTE:  The sort property sorting rules can be a little confusing. Here is how it works:
                         * - SortProperty.Direction of Ascending means sort exactly as what the Columns specification says
                         * - SortProperty.Direction of Descending means sort the _opposite_ of what the Columns specification says
                         * Examples:
                         *  1) SortProperty.Property "LastName desc, FirstName, BirthDate desc" and SortProperty.Direction = Ascending
                         *     OrderBy should be: "order by LastName desc, FirstName, BirthDate desc"
                         *  2) SortProperty.Property "LastName desc, FirstName, BirthDate desc" and SortProperty.Direction = Descending
                         *     OrderBy should be: "order by LastName, FirstName desc, BirthDate"
                         */

                        foreach (var column in sortProperty.Property.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                        {
                            string propertyName;

                            var direction = sortProperty.Direction;
                            if (column.EndsWith(" desc", StringComparison.OrdinalIgnoreCase))
                            {
                                propertyName = column.Left(column.Length - 5);

                                // if the column ends with " desc", toggle the direction if sortProperty is Descending
                                direction = sortProperty.Direction == SortDirection.Ascending ? SortDirection.Descending : SortDirection.Ascending;
                            }
                            else
                            {
                                propertyName = column;
                            }

                            string methodName = direction == SortDirection.Descending ? orderByMethod + "Descending" : orderByMethod;

                            // Call OrderBy on whatever the Expression is for that Column
                            var sortMember = bindings.FirstOrDefault(a => a.Member.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
                            LambdaExpression sortSelector = Expression.Lambda(sortMember.Expression, paramExpression);
                            qryExpression = Expression.Call(typeof(Queryable), methodName, new Type[] { qry.ElementType, sortSelector.ReturnType }, qryExpression, sortSelector);
                            orderByMethod = "ThenBy";
                        }

                        var selectExpression = Expression.Call(typeof(Queryable), "Select", new Type[] { qry.ElementType, dynamicType }, qryExpression, selector);

                        var query = qry.Provider.CreateQuery(selectExpression).AsNoTracking();

                        // cast to a dynamic so that we can do a Queryable.Take (the compiler figures out the T in IQueryable at runtime)
                        dynamic dquery = query;

                        if (FetchTop.HasValue)
                        {
                            dquery = Queryable.Take(dquery, FetchTop.Value);
                        }

                        return(dquery as IQueryable);
                    }
                }
            }

            return(null);
        }
Пример #7
0
        /// <summary>
        /// Persists the DataView to the database by updating the DataViewPersistedValues for this DataView. Returns true if successful
        /// </summary>
        /// <param name="databaseTimeoutSeconds">The database timeout seconds.</param>
        public void PersistResult(int?databaseTimeoutSeconds = null)
        {
            using (var dbContext = this.GetDbContext())
            {
                var persistStopwatch        = Stopwatch.StartNew();
                var dataViewFilterOverrides = new DataViewFilterOverrides();

                dataViewFilterOverrides.ShouldUpdateStatics = false;

                // set an override so that the Persisted Values aren't used when rebuilding the values from the DataView Query
                dataViewFilterOverrides.IgnoreDataViewPersistedValues.Add(this.Id);
                var dataViewGetQueryArgs = new DataViewGetQueryArgs
                {
                    DbContext = dbContext,
                    DataViewFilterOverrides = dataViewFilterOverrides,
                    DatabaseTimeoutSeconds  = databaseTimeoutSeconds,
                };

                var qry = this.GetQuery(dataViewGetQueryArgs);

                RockContext rockContext = dbContext as RockContext;
                if (rockContext == null)
                {
                    rockContext = new RockContext();
                }

                rockContext.Database.CommandTimeout = databaseTimeoutSeconds;
                var savedDataViewPersistedValues = rockContext.DataViewPersistedValues.Where(a => a.DataViewId == this.Id);

                var updatedEntityIdsQry = qry.Select(a => a.Id);

                if (!(rockContext is RockContext))
                {
                    // if this DataView doesn't use a RockContext get the EntityIds into memory as a List<int> then back into IQueryable<int> so that we aren't use multiple dbContexts
                    updatedEntityIdsQry = updatedEntityIdsQry.ToList().AsQueryable();
                }

                var persistedValuesToRemove    = savedDataViewPersistedValues.Where(a => !updatedEntityIdsQry.Any(x => x == a.EntityId));
                var persistedEntityIdsToInsert = updatedEntityIdsQry.Where(x => !savedDataViewPersistedValues.Any(a => a.EntityId == x)).ToList();

                var removeCount = persistedValuesToRemove.Count();
                if (removeCount > 0)
                {
                    // increase the batch size if there are a bunch of rows (and this is a narrow table with no references to it)
                    int?deleteBatchSize = removeCount > 50000 ? 25000 : ( int? )null;

                    int rowRemoved = rockContext.BulkDelete(persistedValuesToRemove, deleteBatchSize);
                }

                if (persistedEntityIdsToInsert.Any())
                {
                    List <DataViewPersistedValue> persistedValuesToInsert = persistedEntityIdsToInsert.OrderBy(a => a)
                                                                            .Select(a =>
                                                                                    new DataViewPersistedValue
                    {
                        DataViewId = this.Id,
                        EntityId   = a
                    }).ToList();

                    rockContext.BulkInsert(persistedValuesToInsert);
                }

                persistStopwatch.Stop();

                // Update the Persisted Refresh information.
                PersistedLastRefreshDateTime         = RockDateTime.Now;
                PersistedLastRunDurationMilliseconds = Convert.ToInt32(persistStopwatch.Elapsed.TotalMilliseconds);
            }
        }
Пример #8
0
        /// <summary>
        /// Gets the expression.
        /// </summary>
        /// <param name="serviceInstance">The service instance.</param>
        /// <param name="paramExpression">The parameter expression.</param>
        /// <param name="dataViewFilterOverrides">The data view filter overrides.</param>
        /// <returns></returns>
        /// <exception cref="Rock.Reporting.RockReportingException">
        /// Unable to determine Assembly for EntityTypeId { EntityTypeId }
        /// or
        /// Unable to determine DataView EntityType for { dataViewEntityTypeCache }.
        /// or
        /// Unable to determine transform expression for TransformEntityTypeId: {TransformEntityTypeId}
        /// </exception>
        public Expression GetExpression(IService serviceInstance, ParameterExpression paramExpression, DataViewFilterOverrides dataViewFilterOverrides)
        {
            var dataViewEntityTypeCache = EntityTypeCache.Get(EntityTypeId.Value);

            if (dataViewEntityTypeCache?.AssemblyName == null)
            {
                throw new RockDataViewFilterExpressionException(this.DataViewFilter, $"Unable to determine DataView Assembly for EntityTypeId { EntityTypeId }");
            }

            Type dataViewEntityTypeType = dataViewEntityTypeCache.GetEntityType();

            if (dataViewEntityTypeType == null)
            {
                throw new RockDataViewFilterExpressionException(this.DataViewFilter, $"Unable to determine DataView EntityType for { dataViewEntityTypeCache }.");
            }

            // DataViews must have a DataViewFilter (something has gone wrong it doesn't have one)
            // Note that DataViewFilterId might be null even though DataViewFilter is not null
            // This is because the DataViewFilter might be just in memory and not saved to the database (for example, a Preview or a DynamicReport)
            if (this.DataViewFilter == null)
            {
                throw new RockDataViewFilterExpressionException(this.DataViewFilter, $"DataViewFilter is null for DataView { this.Name } ({this.Id}).");
            }

            bool usePersistedValues = this.PersistedScheduleIntervalMinutes.HasValue && this.PersistedLastRefreshDateTime.HasValue;

            if (dataViewFilterOverrides != null)
            {
                // don't use persisted values if this DataView in the list of DataViews that should not be persisted due to override
                usePersistedValues = usePersistedValues && !dataViewFilterOverrides.IgnoreDataViewPersistedValues.Contains(this.Id);
            }

            // If dataViewFilterOverrides is null assume true in order to preserve current functionality.
            RockLogger.Log.Debug(RockLogDomains.Reporting, "{methodName} dataViewFilterOverrides: {@dataViewFilterOverrides} DataviewId: {DataviewId}", nameof(GetExpression), dataViewFilterOverrides, DataViewFilter.DataViewId);
            if (dataViewFilterOverrides == null || dataViewFilterOverrides.ShouldUpdateStatics)
            {
                DataViewService.AddRunDataViewTransaction(Id);
            }

            // We need to call GetExpression regardless of whether or not usePresistedValues is true so the child queries get their stats updated.
            var filterExpression = DataViewFilter != null?DataViewFilter.GetExpression(dataViewEntityTypeType, serviceInstance, paramExpression, dataViewFilterOverrides) : null;

            if (usePersistedValues)
            {
                // If this is a persisted DataView, get the ids for the expression by querying DataViewPersistedValue instead of evaluating all the filters
                var rockContext = serviceInstance.Context as RockContext;
                if (rockContext == null)
                {
                    rockContext = new RockContext();
                }

                var persistedValuesQuery = rockContext.DataViewPersistedValues.Where(a => a.DataViewId == this.Id);
                var ids = persistedValuesQuery.Select(v => v.EntityId);
                MemberExpression propertyExpression = Expression.Property(paramExpression, "Id");
                if (!(serviceInstance.Context is RockContext))
                {
                    // if this DataView doesn't use a RockContext get the EntityIds into memory as a List<int> then back into IQueryable<int> so that we aren't use multiple dbContexts
                    ids = ids.ToList().AsQueryable();
                }

                var idsExpression = Expression.Constant(ids.AsQueryable(), typeof(IQueryable <int>));

                Expression expression = Expression.Call(typeof(Queryable), "Contains", new Type[] { typeof(int) }, idsExpression, propertyExpression);

                return(expression);
            }
            else
            {
                if (dataViewEntityTypeCache.Id == EntityTypeCache.Get(typeof(Rock.Model.Person)).Id)
                {
                    var        qry = new PersonService(( RockContext )serviceInstance.Context).Queryable(this.IncludeDeceased);
                    Expression extractedFilterExpression = FilterExpressionExtractor.Extract <Rock.Model.Person>(qry, paramExpression, "p");
                    if (filterExpression == null)
                    {
                        filterExpression = extractedFilterExpression;
                    }
                    else
                    {
                        filterExpression = Expression.AndAlso(filterExpression, extractedFilterExpression);
                    }
                }

                if (this.TransformEntityTypeId.HasValue)
                {
                    Expression transformedExpression = GetTransformExpression(this.TransformEntityTypeId.Value, serviceInstance, paramExpression, filterExpression);
                    if (transformedExpression == null)
                    {
                        // if TransformEntityTypeId is defined, but we got null back, we'll get unexpected results, so throw an exception
                        throw new RockDataViewFilterExpressionException(this.DataViewFilter, $"Unable to determine transform expression for TransformEntityTypeId: {TransformEntityTypeId}");
                    }

                    return(transformedExpression);
                }

                return(filterExpression);
            }
        }
Пример #9
0
 public Expression GetExpression(IService serviceInstance, ParameterExpression paramExpression, DataViewFilterOverrides dataViewFilterOverrides, out List <string> errorMessages)
 {
     errorMessages = new List <string>();
     return(GetExpression(serviceInstance, paramExpression, dataViewFilterOverrides));
 }
Пример #10
0
        /// <summary>
        /// Gets the expression.
        /// </summary>
        /// <param name="serviceInstance">The service instance.</param>
        /// <param name="paramExpression">The parameter expression.</param>
        /// <param name="dataViewFilterOverrides">The data view filter overrides.</param>
        /// <param name="errorMessages">The error messages.</param>
        /// <returns></returns>
        public Expression GetExpression(IService serviceInstance, ParameterExpression paramExpression, DataViewFilterOverrides dataViewFilterOverrides, out List <string> errorMessages)
        {
            errorMessages = new List <string>();

            var cachedEntityType = EntityTypeCache.Read(EntityTypeId.Value);

            if (cachedEntityType != null && cachedEntityType.AssemblyName != null)
            {
                Type filteredEntityType = cachedEntityType.GetEntityType();

                if (filteredEntityType != null)
                {
                    Expression filterExpression = DataViewFilter != null?DataViewFilter.GetExpression(filteredEntityType, serviceInstance, paramExpression, dataViewFilterOverrides, errorMessages) : null;

                    Expression transformedExpression = GetTransformExpression(serviceInstance, paramExpression, filterExpression, errorMessages);
                    if (transformedExpression != null)
                    {
                        return(transformedExpression);
                    }

                    return(filterExpression);
                }
            }

            return(null);
        }
Пример #11
0
        /// <summary>
        /// Gets the query.
        /// </summary>
        /// <param name="sortProperty">The sort property.</param>
        /// <param name="dbContext">The database context.</param>
        /// <param name="dataViewFilterOverrides">The data view filter overrides.</param>
        /// <param name="databaseTimeoutSeconds">The database timeout seconds.</param>
        /// <param name="errorMessages">The error messages.</param>
        /// <returns></returns>
        public IQueryable <IEntity> GetQuery(SortProperty sortProperty, System.Data.Entity.DbContext dbContext, DataViewFilterOverrides dataViewFilterOverrides, int?databaseTimeoutSeconds, out List <string> errorMessages)
        {
            errorMessages = new List <string>();

            if (EntityTypeId.HasValue)
            {
                var cachedEntityType = EntityTypeCache.Read(EntityTypeId.Value);
                if (cachedEntityType != null && cachedEntityType.AssemblyName != null)
                {
                    Type entityType = cachedEntityType.GetEntityType();

                    if (entityType != null)
                    {
                        if (dbContext == null)
                        {
                            dbContext = Reflection.GetDbContextForEntityType(entityType);
                        }

                        if (databaseTimeoutSeconds.HasValue)
                        {
                            dbContext.Database.CommandTimeout = databaseTimeoutSeconds.Value;
                        }

                        IService serviceInstance = Reflection.GetServiceForEntityType(entityType, dbContext);

                        if (serviceInstance != null)
                        {
                            ParameterExpression paramExpression = serviceInstance.ParameterExpression;
                            Expression          whereExpression = GetExpression(serviceInstance, paramExpression, dataViewFilterOverrides, out errorMessages);

                            MethodInfo getMethod = serviceInstance.GetType().GetMethod("Get", new Type[] { typeof(ParameterExpression), typeof(Expression), typeof(SortProperty) });
                            if (getMethod != null)
                            {
                                if (sortProperty == null)
                                {
                                    // if no sorting is specified, just sort by Id
                                    sortProperty = new SortProperty {
                                        Direction = SortDirection.Ascending, Property = "Id"
                                    };
                                }

                                var getResult = getMethod.Invoke(serviceInstance, new object[] { paramExpression, whereExpression, sortProperty });
                                var qry       = getResult as IQueryable <IEntity>;

                                return(qry);
                            }
                        }
                    }
                }
            }

            return(null);
        }
Пример #12
0
        /// <summary>
        /// Gets the expression.
        /// </summary>
        /// <param name="serviceInstance">The service instance.</param>
        /// <param name="paramExpression">The parameter expression.</param>
        /// <param name="dataViewFilterOverrides">The data view filter overrides.</param>
        /// <param name="errorMessages">The error messages.</param>
        /// <returns></returns>
        public Expression GetExpression(IService serviceInstance, ParameterExpression paramExpression, DataViewFilterOverrides dataViewFilterOverrides, out List <string> errorMessages)
        {
            errorMessages = new List <string>();

            var cachedEntityType = EntityTypeCache.Get(EntityTypeId.Value);

            if (cachedEntityType?.AssemblyName == null)
            {
                return(null);
            }

            Type filteredEntityType = cachedEntityType.GetEntityType();

            if (filteredEntityType == null)
            {
                return(null);
            }

            bool usePersistedValues = this.PersistedScheduleIntervalMinutes.HasValue && this.PersistedLastRefreshDateTime.HasValue;

            if (dataViewFilterOverrides != null)
            {
                // don't use persisted values if this dataview is in the list of dataviews that should not be persisted due to override
                usePersistedValues = usePersistedValues && !dataViewFilterOverrides.IgnoreDataViewPersistedValues.Contains(this.Id);
            }

            DataViewService.AddRunDataViewTransaction(Id);

            if (usePersistedValues)
            {
                // If this is a persisted dataview, get the ids for the expression by querying DataViewPersistedValue instead of evaluating all the filters
                var rockContext = serviceInstance.Context as RockContext;
                if (rockContext == null)
                {
                    rockContext = new RockContext();
                }

                var persistedValuesQuery = rockContext.DataViewPersistedValues.Where(a => a.DataViewId == this.Id);
                var ids = persistedValuesQuery.Select(v => v.EntityId);
                MemberExpression propertyExpression = Expression.Property(paramExpression, "Id");
                if (!(serviceInstance.Context is RockContext))
                {
                    // if this DataView doesn't use a RockContext get the EntityIds into memory as as a List<int> then back into IQueryable<int> so that we aren't use multiple dbContexts
                    ids = ids.ToList().AsQueryable();
                }

                var idsExpression = Expression.Constant(ids.AsQueryable(), typeof(IQueryable <int>));

                Expression expression = Expression.Call(typeof(Queryable), "Contains", new Type[] { typeof(int) }, idsExpression, propertyExpression);

                return(expression);
            }
            else
            {
                Expression filterExpression = DataViewFilter != null?DataViewFilter.GetExpression(filteredEntityType, serviceInstance, paramExpression, dataViewFilterOverrides, errorMessages) : null;

                if (cachedEntityType.Id == EntityTypeCache.Get(typeof(Rock.Model.Person)).Id)
                {
                    var        qry = new PersonService(( RockContext )serviceInstance.Context).Queryable(this.IncludeDeceased);
                    Expression extractedFilterExpression = FilterExpressionExtractor.Extract <Rock.Model.Person>(qry, paramExpression, "p");
                    if (filterExpression == null)
                    {
                        filterExpression = extractedFilterExpression;
                    }
                    else
                    {
                        filterExpression = Expression.AndAlso(filterExpression, extractedFilterExpression);
                    }
                }

                Expression transformedExpression = GetTransformExpression(serviceInstance, paramExpression, filterExpression, errorMessages);
                if (transformedExpression != null)
                {
                    return(transformedExpression);
                }

                return(filterExpression);
            }
        }
Пример #13
0
        /// <summary>
        /// Gets the expression.
        /// </summary>
        /// <param name="serviceInstance">The service instance.</param>
        /// <param name="paramExpression">The parameter expression.</param>
        /// <param name="dataViewFilterOverrides">The data view filter overrides.</param>
        /// <param name="errorMessages">The error messages.</param>
        /// <returns></returns>
        public Expression GetExpression(IService serviceInstance, ParameterExpression paramExpression, DataViewFilterOverrides dataViewFilterOverrides, out List <string> errorMessages)
        {
            errorMessages = new List <string>();

            var cachedEntityType = EntityTypeCache.Get(EntityTypeId.Value);

            if (cachedEntityType != null && cachedEntityType.AssemblyName != null)
            {
                Type filteredEntityType = cachedEntityType.GetEntityType();

                if (filteredEntityType != null)
                {
                    bool usePersistedValues = this.PersistedScheduleIntervalMinutes.HasValue && this.PersistedLastRefreshDateTime.HasValue;
                    if (dataViewFilterOverrides != null)
                    {
                        usePersistedValues = usePersistedValues && !dataViewFilterOverrides.IgnoreDataViewPersistedValues.Contains(this.Id);
                    }

                    // don't use persisted values if there are dataViewFilterOverrides
                    if (usePersistedValues && dataViewFilterOverrides?.Any() == true)
                    {
                        usePersistedValues = false;
                    }

                    if (usePersistedValues)
                    {
                        // If this is a persisted dataview, get the ids for the expression by querying DataViewPersistedValue instead of evaluating all the filters
                        var rockContext = serviceInstance.Context as RockContext;
                        if (rockContext == null)
                        {
                            rockContext = new RockContext();
                        }

                        var persistedValuesQuery = rockContext.DataViewPersistedValues.Where(a => a.DataViewId == this.Id);
                        var ids = persistedValuesQuery.Select(v => v.EntityId);
                        MemberExpression propertyExpression = Expression.Property(paramExpression, "Id");
                        if (!(serviceInstance.Context is RockContext))
                        {
                            // if this DataView doesn't use a RockContext get the EntityIds into memory as as a List<int> then back into IQueryable<int> so that we aren't use multiple dbContexts
                            ids = ids.ToList().AsQueryable();
                        }

                        var idsExpression = Expression.Constant(ids.AsQueryable(), typeof(IQueryable <int>));

                        Expression expression = Expression.Call(typeof(Queryable), "Contains", new Type[] { typeof(int) }, idsExpression, propertyExpression);

                        return(expression);
                    }
                    else
                    {
                        Expression filterExpression = DataViewFilter != null?DataViewFilter.GetExpression(filteredEntityType, serviceInstance, paramExpression, dataViewFilterOverrides, errorMessages) : null;

                        Expression transformedExpression = GetTransformExpression(serviceInstance, paramExpression, filterExpression, errorMessages);
                        if (transformedExpression != null)
                        {
                            return(transformedExpression);
                        }

                        return(filterExpression);
                    }
                }
            }

            return(null);
        }
Пример #14
0
 public IQueryable <IEntity> GetQuery(SortProperty sortProperty, System.Data.Entity.DbContext dbContext, DataViewFilterOverrides dataViewFilterOverrides, int?databaseTimeoutSeconds, out List <string> errorMessages)
 {
     errorMessages = new List <string>();
     return(GetQuery(new DataViewGetQueryArgs
     {
         DbContext = dbContext,
         DataViewFilterOverrides = dataViewFilterOverrides,
         DatabaseTimeoutSeconds = databaseTimeoutSeconds,
         SortProperty = sortProperty
     }));
 }