public void Check_StructuredQuery_Invalidation_On_ReportInterface( ) { using (CacheManager.EnforceCacheHitRules(false)) { Report report; ResourceReportNode rootNode; ResourceArgument exprType; // Create report using (var ctx = DatabaseContext.GetContext(true, preventPostSaveActionsPropagating: true)) { report = new EntityModel.Report(); rootNode = new ResourceReportNode(); var reportType = EntityModel.Entity.Get <EntityType>("test:person");; rootNode.ResourceReportNodeType = reportType; report.RootNode = rootNode.As <ReportNode>(); var column = new EntityModel.ReportColumn( ); exprType = new ResourceArgument(); exprType.ConformsToType = reportType; var expr = new EntityModel.ResourceExpression(); expr.ReportExpressionResultType = exprType.As <ActivityArgument>(); expr.SourceNode = rootNode.As <ReportNode>(); column.ColumnExpression = expr.As <ReportExpression>(); report.ReportColumns.Add(column); report.Save(); ctx.CommitTransaction(); } ReportingInterface ri = new ReportingInterface( ); var settings = new ReportSettings { InitialRow = 0, PageSize = 1, RequireFullMetadata = true, RequireBasicMetadata = true }; // Run and expect 1 column var result = ri.RunReport(report.Id, settings); Assert.AreEqual(1, result.ReportQueryColumns.Count); // Add column using (var ctx = DatabaseContext.GetContext(true, preventPostSaveActionsPropagating: true)) { var column2 = new EntityModel.ReportColumn(); var expr2 = new EntityModel.ResourceExpression(); expr2.SourceNode = rootNode.As <ReportNode>(); expr2.ReportExpressionResultType = exprType.As <ActivityArgument>(); column2.ColumnExpression = expr2.As <ReportExpression>(); report.ReportColumns.Add(column2); report.Save(); ctx.CommitTransaction(); } // Run and expect 2 columns result = ri.RunReport(report.Id, settings); Assert.AreEqual(2, result.ReportQueryColumns.Count); } }
/// <summary> /// Get the structured query, possibly from cache. /// </summary> /// <param name="report">The report to convert.</param> /// <param name="settings">The report run settings.</param> /// <param name="suppressPreload">True if we should suppress preloading.</param> /// <returns>The structured query.</returns> private StructuredQuery GetStructuredQuery(Model.Report report, ReportSettings settings, bool suppressPreload) { using (MessageContext msg = new MessageContext("Reports")) { StructuredQuery immutableStructuredQuery; StructuredQuery structuredQuery; bool useStructuredQueryCache = settings.UseStructuredQueryCache; ReportToQueryConverterSettings converterSettings = new ReportToQueryConverterSettings { SuppressPreload = suppressPreload, RefreshCachedStructuredQuery = settings.RefreshCachedStructuredQuery, SchemaOnly = settings.RequireSchemaMetadata }; if (settings != null && settings.UseStructuredQueryCache) { // don't allow mutations of cached copy immutableStructuredQuery = CachedReportToQueryConverter.Convert(report, converterSettings); } else { // don't allow mutations, just so we can log it correctly immutableStructuredQuery = NonCachedReportToQueryConverter.Convert(report, converterSettings); } structuredQuery = immutableStructuredQuery.DeepCopy( ); // so we can mutate it (in case we need to) // Logging msg.Append(() => new String('-', 50)); msg.Append(() => "GetStructuredQuery"); msg.Append(() => "suppressPreload = " + suppressPreload); msg.Append(() => "useStructuredQueryCache = " + useStructuredQueryCache); msg.Append(() => "Structured Query:\n" + StructuredQueryHelper.ToXml(immutableStructuredQuery)); msg.Append(() => new String('-', 50)); return(structuredQuery); } }
private PreparedQuery PrepareReportRollupRun(Model.Report report, StructuredQuery structuredQuery, ReportSettings reportSettings, QuerySettings nonRollupQuerySettings) { StructuredQuery rollupQuery = null; ClientAggregate clientAggregate = null; StructuredQuery optimisedQuery; QuerySettings rollupSettings; bool adhocRollup; bool reportRollup; adhocRollup = reportSettings.ReportParameters != null && reportSettings.ReportParameters.GroupAggregateRules != null; reportRollup = !adhocRollup && report.ReportColumns.Any(rc => rc.ColumnRollup.Count > 0 || rc.ColumnGrouping.Count > 0); if (adhocRollup) { clientAggregate = ApplyAdhocAggregates(reportSettings.ReportParameters.GroupAggregateRules, structuredQuery); } else if (reportRollup) { clientAggregate = new ClientAggregate(report, structuredQuery); clientAggregate.IncludeRollup = true; } else if (report.RollupGrandTotals != null || report.RollupSubTotals != null || report.RollupOptionLabels != null) { return(new PreparedQuery { ClientAggregate = new ClientAggregate( ) }); } else { return(new PreparedQuery( )); } // Clone the query, so that runs and rollups won't intefere with each others caches if they mutate the structure // In particular, calculated columns get evaluated during execution and mutate the query .. but only if the result doesn't come from cache, but this interferes with the rollup cache key. // Ideally, both calculations and optimisations would be provided in layers, and both applied, and cached, before either normal or rollup executions are run. rollupQuery = structuredQuery.DeepCopy( ); // A poor proxy for determining that this is not a pivot chart. bool isGroupedReport = !(reportSettings.ReportParameters != null && reportSettings.ReportParameters.GroupAggregateRules != null && reportSettings.ReportParameters.GroupAggregateRules.IgnoreRows); if (isGroupedReport) { ReportRollupHelper.EnsureShowTotalsHasCount(rollupQuery, clientAggregate); } // Remove unused columns bool supportQuickSearch = !string.IsNullOrWhiteSpace(reportSettings.QuickSearch); optimisedQuery = ReportRollupHelper.RemoveUnusedColumns(rollupQuery, clientAggregate, supportQuickSearch); rollupSettings = new QuerySettings { SecureQuery = nonRollupQuerySettings.SecureQuery, SupportClientAggregate = true, SupportPaging = false, QuickSearchTerm = reportSettings.QuickSearch, SupportQuickSearch = supportQuickSearch, // rollups query support quick search. ClientAggregate = clientAggregate, AdditionalOrderColumns = BuildAdditionOrderColumnDictionary(optimisedQuery, clientAggregate), FullAggregateClustering = true, Hint = "RptRollup-" + report.Id, TargetResource = nonRollupQuerySettings.TargetResource, IncludeResources = nonRollupQuerySettings.IncludeResources, ExcludeResources = nonRollupQuerySettings.ExcludeResources }; // Note : do not apply quick search filter to rollups (for scalability reasons) PreparedQuery preparedQuery = new PreparedQuery { ClientAggregate = clientAggregate, StructuredQuery = optimisedQuery, QuerySettings = rollupSettings }; return(preparedQuery); }
/// <summary> /// Runs the report. /// </summary> /// <param name="report">The report.</param> /// <param name="reportSettings">The settings.</param> /// <param name="suppressPreload">Pass true if the report has already been preloaded.</param> /// <returns>ReportResult.</returns> /// <exception cref="System.ArgumentException">@The report identifier resource is not a report.;reportId</exception> public ReportCompletionData PrepareReport(Model.Report report, ReportSettings reportSettings, bool suppressPreload = false) { if (report == null) { throw new ArgumentNullException("report"); } if (reportSettings == null) { reportSettings = new ReportSettings( ); } StructuredQuery structuredQuery; PreparedQuery preparedReport; PreparedQuery preparedRollup; using (EDC.ReadiNow.Diagnostics.Profiler.Measure("Prepare report run")) using (MessageContext messageContext = new MessageContext("Reports")) using (new SecurityBypassContext( )) { // Get the structured query structuredQuery = GetStructuredQuery(report, reportSettings, suppressPreload); // Handle metadata-only request if (reportSettings.RequireSchemaMetadata) { ReportResult reportResult = new ReportResult(report, structuredQuery, null, null, null, reportSettings); return(new ReportCompletionData(reportResult)); } // Prepare query settings preparedReport = PrepareReportRun(structuredQuery, reportSettings); preparedReport.QuerySettings.Hint = "Rpt-" + report.Id.ToString( ); // Handle rollups preparedRollup = PrepareReportRollupRun(report, preparedReport.StructuredQuery, reportSettings, preparedReport.QuerySettings); } Func <ReportResult> resultCallback = () => { ReportResult reportResult = null; QueryResult queryResult = null; QueryResult rollupResult = null; using (new SecurityBypassContext( )) { // Execute the query queryResult = QueryRunner.ExecuteQuery(preparedReport.StructuredQuery, preparedReport.QuerySettings); // Execute the rollup query if (preparedRollup.StructuredQuery != null) { rollupResult = QueryRunner.ExecuteQuery(preparedRollup.StructuredQuery, preparedRollup.QuerySettings); } // Package up the result. reportResult = new ReportResult(report, preparedReport.StructuredQuery, queryResult, preparedRollup.ClientAggregate, rollupResult, reportSettings); } return(reportResult); }; // Create cache key (null indicates report is not cacheable) IQueryRunnerCacheKey reportCacheKey = null; IQueryRunnerCacheKey rollupCacheKey = null; ReportResultCacheKey reportResultCacheKey = null; reportCacheKey = QueryRunnerCacheKeyProvider.CreateCacheKey(preparedReport.StructuredQuery, preparedReport.QuerySettings); if (reportCacheKey != null) { if (preparedRollup.StructuredQuery != null) { rollupCacheKey = QueryRunnerCacheKeyProvider.CreateCacheKey(preparedRollup.StructuredQuery, preparedRollup.QuerySettings); } reportResultCacheKey = new ReportResultCacheKey(reportSettings, reportCacheKey, rollupCacheKey); } // Create completion result ReportCompletionData completionData = new ReportCompletionData( ); completionData.ResultCallback = resultCallback; completionData.ResultCacheKey = reportResultCacheKey; completionData.CacheContextDuringPreparation = CacheContext.GetContext( ); return(completionData); }
/// <summary> /// Runs the report. /// </summary> /// <param name="report">The report.</param> /// <param name="settings">The settings.</param> /// <param name="suppressPreload">Pass true if the report has already been preloaded.</param> /// <returns>ReportResult.</returns> /// <exception cref="System.ArgumentException">@The report identifier resource is not a report.;reportId</exception> public ReportResult RunReport(Model.Report report, ReportSettings settings, bool suppressPreload = false) { ReportCompletionData completionData = PrepareReport(report, settings, suppressPreload); return(completionData.PerformRun( )); }