/// <summary> /// Gets the query. /// </summary> /// <param name="dataViewGetQueryArgs">The data view get query arguments.</param> /// <returns></returns> /// <exception cref="Rock.Reporting.RockReportingException"> /// Unable to determine DbContext for {this} /// or /// Unable to determine ServiceInstance for {this} /// or /// Unable to determine IService.Get for {this} /// </exception> public IQueryable <IEntity> GetQuery(DataViewGetQueryArgs dataViewGetQueryArgs) { var dbContext = dataViewGetQueryArgs.DbContext; if (dbContext == null) { dbContext = this.GetDbContext(); if (dbContext == null) { // this could happen if the EntityTypeId id refers to an assembly/type that doesn't exist anymore // we'll just default to new RockContext(), but it'll likely fail when we try to get a ServiceInstance below if the entityType doesn't exist in an assembly dbContext = new RockContext(); } } IService serviceInstance = this.GetServiceInstance(dbContext); if (serviceInstance == null) { var entityTypeCache = EntityTypeCache.Get(this.EntityTypeId ?? 0); throw new RockDataViewFilterExpressionException(this.DataViewFilter, $"Unable to determine ServiceInstance from DataView EntityType {entityTypeCache} for {this}"); } var databaseTimeoutSeconds = dataViewGetQueryArgs.DatabaseTimeoutSeconds; if (databaseTimeoutSeconds.HasValue) { dbContext.Database.CommandTimeout = databaseTimeoutSeconds.Value; } var dataViewFilterOverrides = dataViewGetQueryArgs.DataViewFilterOverrides; ParameterExpression paramExpression = serviceInstance.ParameterExpression; Expression whereExpression = GetExpression(serviceInstance, paramExpression, dataViewFilterOverrides); MethodInfo getMethod = serviceInstance.GetType().GetMethod("Get", new Type[] { typeof(ParameterExpression), typeof(Expression), typeof(SortProperty) }); if (getMethod == null) { throw new RockDataViewFilterExpressionException(this.DataViewFilter, $"Unable to determine IService.Get for Report: {this}"); } var sortProperty = dataViewGetQueryArgs.SortProperty; 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); }
/// <summary> /// Gets the ids. /// </summary> /// <param name="dataViewId">The data view identifier.</param> /// <returns></returns> public List <int> GetIds(int dataViewId) { var dataView = Queryable().AsNoTracking().FirstOrDefault(d => d.Id == dataViewId); var dataViewGetQueryArgs = new DataViewGetQueryArgs { DatabaseTimeoutSeconds = 180, }; return(dataView.GetQuery(dataViewGetQueryArgs).Select(a => a.Id).ToList()); }
/// <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); } }
/// <summary> /// Called after the save operation has been executed /// </summary> /// <remarks> /// This method is only called if <see cref="M:Rock.Data.EntitySaveHook`1.PreSave" /> returns /// without error. /// </remarks> protected override void PostSave() { var rockContext = ( RockContext )this.RockContext; if (Entity.ConnectionStatus == null) { Entity.ConnectionStatus = new ConnectionStatusService(rockContext).Get(Entity.ConnectionStatusId); } if (Entity.ConnectionStatus != null && Entity.ConnectionStatus.AutoInactivateState && Entity.ConnectionState != ConnectionState.Inactive) { Entity.ConnectionState = ConnectionState.Inactive; rockContext.SaveChanges(); } if (Entity.ConnectionStatus.ConnectionStatusAutomations.Any()) { foreach (var connectionStatusAutomation in Entity.ConnectionStatus.ConnectionStatusAutomations) { bool isAutomationValid = true; if (connectionStatusAutomation.DataViewId.HasValue) { // Get the dataview configured for the connection request var dataViewService = new DataViewService(rockContext); var dataview = dataViewService.Get(connectionStatusAutomation.DataViewId.Value); if (dataview != null) { var dataViewGetQueryArgs = new DataViewGetQueryArgs { DbContext = rockContext }; isAutomationValid = dataview.GetQuery(dataViewGetQueryArgs).Any(a => a.Id == Entity.Id); } } if (isAutomationValid && connectionStatusAutomation.GroupRequirementsFilter != GroupRequirementsFilter.Ignore) { // Group Requirement can't be meet when either placement group or placement group role id is missing if (!Entity.AssignedGroupId.HasValue || !Entity.AssignedGroupMemberRoleId.HasValue) { isAutomationValid = false; } else { var isRequirementMeet = true; var group = new GroupService(rockContext).Get(Entity.AssignedGroupId.Value); var hasGroupRequirement = new GroupRequirementService(rockContext).Queryable().Where(a => (a.GroupId.HasValue && a.GroupId == group.Id) || (a.GroupTypeId.HasValue && a.GroupTypeId == group.GroupTypeId)).Any(); if (hasGroupRequirement) { var requirementsResults = group.PersonMeetsGroupRequirements( rockContext, Entity.PersonAlias.PersonId, Entity.AssignedGroupMemberRoleId.Value); if (requirementsResults != null && requirementsResults .Where(a => a.MeetsGroupRequirement != MeetsGroupRequirement.NotApplicable) .Any(r => r.MeetsGroupRequirement != MeetsGroupRequirement.Meets && r.MeetsGroupRequirement != MeetsGroupRequirement.MeetsWithWarning) ) { isRequirementMeet = false; } } // connection request based on if group requirement is meet or not is added to list for status update isAutomationValid = (connectionStatusAutomation.GroupRequirementsFilter == GroupRequirementsFilter.DoesNotMeet && !isRequirementMeet) || (connectionStatusAutomation.GroupRequirementsFilter == GroupRequirementsFilter.MustMeet && isRequirementMeet); } } if (isAutomationValid) { Entity.ConnectionStatusId = connectionStatusAutomation.DestinationStatusId; // disabled pre post processing in order to prevent circular loop that may arise due to status change. rockContext.SaveChanges(true); } } } if (HistoryChangeList?.Any() == true) { HistoryService.SaveChanges(rockContext, typeof(ConnectionRequest), Rock.SystemGuid.Category.HISTORY_CONNECTION_REQUEST.AsGuid(), Entity.Id, HistoryChangeList, true, Entity.ModifiedByPersonAliasId); } if (PersonHistoryChangeList?.Any() == true) { var personAlias = Entity.PersonAlias ?? new PersonAliasService(rockContext).Get(Entity.PersonAliasId); HistoryService.SaveChanges( rockContext, typeof(Person), Rock.SystemGuid.Category.HISTORY_PERSON_CONNECTION_REQUEST.AsGuid(), personAlias.PersonId, PersonHistoryChangeList, "Request", typeof(ConnectionRequest), Entity.Id, true, Entity.ModifiedByPersonAliasId, rockContext.SourceOfChange); } base.PostSave(); }